feat: implement authentication system for API Gateway and FileService #34
@@ -13,7 +13,7 @@ namespace FictionArchive.Service.NovelService.GraphQL;
|
|||||||
|
|
||||||
public class Mutation
|
public class Mutation
|
||||||
{
|
{
|
||||||
[Authorize(Roles = "admin")]
|
[Authorize]
|
||||||
public async Task<NovelUpdateRequestedEvent> ImportNovel(string novelUrl, NovelUpdateService service)
|
public async Task<NovelUpdateRequestedEvent> ImportNovel(string novelUrl, NovelUpdateService service)
|
||||||
{
|
{
|
||||||
return await service.QueueNovelImport(novelUrl);
|
return await service.QueueNovelImport(novelUrl);
|
||||||
|
|||||||
@@ -11,21 +11,21 @@ public class Mutation
|
|||||||
{
|
{
|
||||||
[Error<DuplicateNameException>]
|
[Error<DuplicateNameException>]
|
||||||
[Error<FormatException>]
|
[Error<FormatException>]
|
||||||
[Authorize(Roles = "admin")]
|
[Authorize(Roles = new[] { "admin" })]
|
||||||
public async Task<SchedulerJob> ScheduleEventJob(string key, string description, string eventType, string eventData, string cronSchedule, JobManagerService jobManager)
|
public async Task<SchedulerJob> ScheduleEventJob(string key, string description, string eventType, string eventData, string cronSchedule, JobManagerService jobManager)
|
||||||
{
|
{
|
||||||
return await jobManager.ScheduleEventJob(key, description, eventType, eventData, cronSchedule);
|
return await jobManager.ScheduleEventJob(key, description, eventType, eventData, cronSchedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Error<JobPersistenceException>]
|
[Error<JobPersistenceException>]
|
||||||
[Authorize(Roles = "admin")]
|
[Authorize(Roles = new[] { "admin" })]
|
||||||
public async Task<bool> RunJob(string jobKey, JobManagerService jobManager)
|
public async Task<bool> RunJob(string jobKey, JobManagerService jobManager)
|
||||||
{
|
{
|
||||||
return await jobManager.TriggerJob(jobKey);
|
return await jobManager.TriggerJob(jobKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Error<KeyNotFoundException>]
|
[Error<KeyNotFoundException>]
|
||||||
[Authorize(Roles = "admin")]
|
[Authorize(Roles = new[] { "admin" })]
|
||||||
public async Task<bool> DeleteJob(string jobKey, JobManagerService jobManager)
|
public async Task<bool> DeleteJob(string jobKey, JobManagerService jobManager)
|
||||||
{
|
{
|
||||||
bool deleted = await jobManager.DeleteJob(jobKey);
|
bool deleted = await jobManager.DeleteJob(jobKey);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using FictionArchive.Service.Shared.Models.Authentication;
|
using FictionArchive.Service.Shared.Models.Authentication;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace FictionArchive.Service.Shared.Extensions;
|
namespace FictionArchive.Service.Shared.Extensions;
|
||||||
|
|
||||||
@@ -17,6 +18,8 @@ public static class AuthenticationExtensions
|
|||||||
throw new InvalidOperationException("OIDC configuration is required but not found in app settings");
|
throw new InvalidOperationException("OIDC configuration is required but not found in app settings");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ValidateOidcConfiguration(oidcConfig);
|
||||||
|
|
||||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
.AddJwtBearer(options =>
|
.AddJwtBearer(options =>
|
||||||
{
|
{
|
||||||
@@ -46,6 +49,8 @@ public static class AuthenticationExtensions
|
|||||||
throw new InvalidOperationException("OIDC configuration is required but not found in app settings");
|
throw new InvalidOperationException("OIDC configuration is required but not found in app settings");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ValidateOidcConfiguration(oidcConfig);
|
||||||
|
|
||||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
.AddJwtBearer(options =>
|
.AddJwtBearer(options =>
|
||||||
{
|
{
|
||||||
@@ -88,4 +93,29 @@ public static class AuthenticationExtensions
|
|||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ValidateOidcConfiguration(OidcConfiguration config)
|
||||||
|
{
|
||||||
|
var errors = new List<string>();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(config.Authority))
|
||||||
|
errors.Add("OIDC Authority is required");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(config.ClientId))
|
||||||
|
errors.Add("OIDC ClientId is required");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(config.Audience))
|
||||||
|
errors.Add("OIDC Audience is required");
|
||||||
|
|
||||||
|
if (!Uri.TryCreate(config.Authority, UriKind.Absolute, out var authorityUri))
|
||||||
|
errors.Add($"OIDC Authority '{config.Authority}' is not a valid URI");
|
||||||
|
else if (!authorityUri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!authorityUri.Host.Equals("localhost", StringComparison.OrdinalIgnoreCase))
|
||||||
|
errors.Add("OIDC Authority must use HTTPS unless running on localhost");
|
||||||
|
|
||||||
|
if (errors.Any())
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"OIDC configuration validation failed:{Environment.NewLine}{string.Join(Environment.NewLine, errors)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user