feat: implement authentication system for API Gateway and FileService #34
@@ -13,6 +13,7 @@
|
||||
<PackageReference Include="HotChocolate.Data.EntityFramework" Version="15.1.11" />
|
||||
<PackageReference Include="HotChocolate.Fusion" Version="15.1.11" />
|
||||
<PackageReference Include="HotChocolate.Types.Scalars" Version="15.1.11" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.HeaderPropagation" Version="8.0.22" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.11">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
@@ -12,7 +12,11 @@ public class Program
|
||||
|
||||
#region Fusion Gateway
|
||||
|
||||
builder.Services.AddHttpClient("Fusion");
|
||||
builder.Services.AddHttpClient("Fusion")
|
||||
.AddHeaderPropagation(opt =>
|
||||
{
|
||||
opt.Headers.Add("Authorization");
|
||||
});
|
||||
|
||||
builder.Services
|
||||
.AddFusionGatewayServer()
|
||||
@@ -23,7 +27,6 @@ public class Program
|
||||
|
||||
// Add authentication
|
||||
builder.Services.AddOidcAuthentication(builder.Configuration);
|
||||
builder.Services.AddFictionArchiveAuthorization();
|
||||
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
@@ -41,11 +44,10 @@ public class Program
|
||||
|
||||
app.UseCors("AllowFictionArchiveOrigins");
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapHealthChecks("/healthz");
|
||||
|
||||
app.UseHeaderPropagation();
|
||||
|
||||
app.MapGraphQL();
|
||||
|
||||
app.RunWithGraphQLCommands(args);
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"OIDC": {
|
||||
"Authority": "https://auth.orfl.xyz/application/o/fictionarchive/",
|
||||
"Authority": "https://auth.orfl.xyz/application/o/fiction-archive/",
|
||||
"ClientId": "fictionarchive-api",
|
||||
"Audience": "fictionarchive-api",
|
||||
"ValidIssuer": "https://auth.orfl.xyz/application/o/fiction-archive/",
|
||||
"ValidateIssuer": true,
|
||||
"ValidateAudience": true,
|
||||
"ValidateLifetime": true,
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"BaseUrl": "https://localhost:7247/api"
|
||||
},
|
||||
"RabbitMQ": {
|
||||
"ConnectionString": "amqp://localhost2",
|
||||
"ConnectionString": "amqp://localhost",
|
||||
"ClientIdentifier": "FileService"
|
||||
},
|
||||
"S3": {
|
||||
@@ -19,9 +19,10 @@
|
||||
"SecretKey": "REPLACE_ME"
|
||||
},
|
||||
"OIDC": {
|
||||
"Authority": "https://auth.orfl.xyz/application/o/fictionarchive/",
|
||||
"Authority": "https://auth.orfl.xyz/application/o/fiction-archive/",
|
||||
"ClientId": "fictionarchive-files",
|
||||
"Audience": "fictionarchive-api",
|
||||
"ValidIssuer": "https://auth.orfl.xyz/application/o/fiction-archive/",
|
||||
"ValidateIssuer": true,
|
||||
"ValidateAudience": true,
|
||||
"ValidateLifetime": true,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using FictionArchive.Service.NovelService.Models.Novels;
|
||||
using FictionArchive.Service.NovelService.Services;
|
||||
using HotChocolate.Authorization;
|
||||
using HotChocolate.Data;
|
||||
using HotChocolate.Types;
|
||||
|
||||
@@ -7,6 +8,7 @@ namespace FictionArchive.Service.NovelService.GraphQL;
|
||||
|
||||
public class Query
|
||||
{
|
||||
[Authorize]
|
||||
[UsePaging]
|
||||
[UseProjection]
|
||||
[UseFiltering]
|
||||
|
||||
@@ -43,7 +43,8 @@ public class Program
|
||||
|
||||
#region GraphQL
|
||||
|
||||
builder.Services.AddDefaultGraphQl<Query, Mutation>();
|
||||
builder.Services.AddDefaultGraphQl<Query, Mutation>()
|
||||
.AddAuthorization();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -75,6 +76,10 @@ public class Program
|
||||
|
||||
builder.Services.AddHealthChecks();
|
||||
|
||||
// Authentication & Authorization
|
||||
builder.Services.AddOidcAuthentication(builder.Configuration);
|
||||
builder.Services.AddFictionArchiveAuthorization();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Update database (skip in schema export mode)
|
||||
@@ -89,6 +94,9 @@ public class Program
|
||||
|
||||
app.MapHealthChecks("/healthz");
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapGraphQL();
|
||||
|
||||
app.RunWithGraphQLCommands(args);
|
||||
|
||||
@@ -19,5 +19,15 @@
|
||||
"ConnectionString": "amqp://localhost",
|
||||
"ClientIdentifier": "NovelService"
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
"AllowedHosts": "*",
|
||||
"OIDC": {
|
||||
"Authority": "https://auth.orfl.xyz/application/o/fiction-archive/",
|
||||
"ClientId": "ldi5IpEidq2WW0Ka1lehVskb2SOBjnYRaZCpEyBh",
|
||||
"Audience": "ldi5IpEidq2WW0Ka1lehVskb2SOBjnYRaZCpEyBh",
|
||||
"ValidIssuer": "https://auth.orfl.xyz/application/o/fiction-archive/",
|
||||
"ValidateIssuer": true,
|
||||
"ValidateAudience": true,
|
||||
"ValidateLifetime": true,
|
||||
"ValidateIssuerSigningKey": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"subgraph": "Novels",
|
||||
"http": {
|
||||
"baseAddress": "http://localhost:5101/graphql"
|
||||
"baseAddress": "https://localhost:7208/graphql"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Data;
|
||||
using FictionArchive.Service.SchedulerService.Models;
|
||||
using FictionArchive.Service.SchedulerService.Services;
|
||||
using FictionArchive.Service.Shared.Constants;
|
||||
using HotChocolate.Authorization;
|
||||
using HotChocolate.Types;
|
||||
using Quartz;
|
||||
@@ -11,21 +12,21 @@ public class Mutation
|
||||
{
|
||||
[Error<DuplicateNameException>]
|
||||
[Error<FormatException>]
|
||||
[Authorize(Roles = new[] { "admin" })]
|
||||
[Authorize(Roles = [AuthorizationConstants.Roles.Admin])]
|
||||
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);
|
||||
}
|
||||
|
||||
[Error<JobPersistenceException>]
|
||||
[Authorize(Roles = new[] { "admin" })]
|
||||
[Authorize(Roles = [AuthorizationConstants.Roles.Admin])]
|
||||
public async Task<bool> RunJob(string jobKey, JobManagerService jobManager)
|
||||
{
|
||||
return await jobManager.TriggerJob(jobKey);
|
||||
}
|
||||
|
||||
[Error<KeyNotFoundException>]
|
||||
[Authorize(Roles = new[] { "admin" })]
|
||||
[Authorize(Roles = [AuthorizationConstants.Roles.Admin])]
|
||||
public async Task<bool> DeleteJob(string jobKey, JobManagerService jobManager)
|
||||
{
|
||||
bool deleted = await jobManager.DeleteJob(jobKey);
|
||||
|
||||
@@ -17,10 +17,15 @@ public class Program
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Services
|
||||
builder.Services.AddDefaultGraphQl<Query, Mutation>();
|
||||
builder.Services.AddDefaultGraphQl<Query, Mutation>()
|
||||
.AddAuthorization();
|
||||
builder.Services.AddHealthChecks();
|
||||
builder.Services.AddTransient<JobManagerService>();
|
||||
|
||||
// Authentication & Authorization
|
||||
builder.Services.AddOidcAuthentication(builder.Configuration);
|
||||
builder.Services.AddFictionArchiveAuthorization();
|
||||
|
||||
#region Database
|
||||
|
||||
builder.Services.RegisterDbContext<SchedulerServiceDbContext>(
|
||||
@@ -88,6 +93,9 @@ public class Program
|
||||
|
||||
app.MapHealthChecks("/healthz");
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapGraphQL();
|
||||
|
||||
app.RunWithGraphQLCommands(args);
|
||||
|
||||
@@ -12,5 +12,15 @@
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Host=localhost;Database=FictionArchive_SchedulerService;Username=postgres;password=postgres"
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
"AllowedHosts": "*",
|
||||
"OIDC": {
|
||||
"Authority": "https://auth.orfl.xyz/application/o/fiction-archive/",
|
||||
"ClientId": "fictionarchive-api",
|
||||
"Audience": "fictionarchive-api",
|
||||
"ValidIssuer": "https://auth.orfl.xyz/application/o/fiction-archive/",
|
||||
"ValidateIssuer": true,
|
||||
"ValidateAudience": true,
|
||||
"ValidateLifetime": true,
|
||||
"ValidateIssuerSigningKey": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace FictionArchive.Service.Shared.Constants;
|
||||
|
||||
public static class AuthorizationConstants
|
||||
{
|
||||
public static class Roles
|
||||
{
|
||||
public const string Admin = "admin";
|
||||
}
|
||||
|
||||
public static class Policies
|
||||
{
|
||||
public const string Admin = "Admin";
|
||||
public const string User = "User";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using FictionArchive.Service.Shared.Constants;
|
||||
using FictionArchive.Service.Shared.Models.Authentication;
|
||||
using System.Linq;
|
||||
|
||||
@@ -30,16 +32,59 @@ public static class AuthenticationExtensions
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = oidcConfig.ValidateIssuer,
|
||||
ValidIssuer = oidcConfig.ValidIssuer,
|
||||
ValidateAudience = oidcConfig.ValidateAudience,
|
||||
ValidateLifetime = oidcConfig.ValidateLifetime,
|
||||
ValidateIssuerSigningKey = oidcConfig.ValidateIssuerSigningKey,
|
||||
ClockSkew = TimeSpan.FromMinutes(5)
|
||||
};
|
||||
|
||||
options.Events = CreateLoggingJwtBearerEvents();
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static JwtBearerEvents CreateLoggingJwtBearerEvents(JwtBearerEvents? existingEvents = null)
|
||||
{
|
||||
return new JwtBearerEvents
|
||||
{
|
||||
OnMessageReceived = existingEvents?.OnMessageReceived ?? (_ => Task.CompletedTask),
|
||||
OnAuthenticationFailed = context =>
|
||||
{
|
||||
var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>()
|
||||
.CreateLogger("JwtBearerAuthentication");
|
||||
|
||||
logger.LogWarning(context.Exception, "JWT authentication failed: {Message}", context.Exception.Message);
|
||||
|
||||
return existingEvents?.OnAuthenticationFailed?.Invoke(context) ?? Task.CompletedTask;
|
||||
},
|
||||
OnChallenge = context =>
|
||||
{
|
||||
var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>()
|
||||
.CreateLogger("JwtBearerAuthentication");
|
||||
|
||||
logger.LogDebug(
|
||||
"JWT challenge issued. Error: {Error}, ErrorDescription: {ErrorDescription}",
|
||||
context.Error,
|
||||
context.ErrorDescription);
|
||||
|
||||
return existingEvents?.OnChallenge?.Invoke(context) ?? Task.CompletedTask;
|
||||
},
|
||||
OnTokenValidated = context =>
|
||||
{
|
||||
var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>()
|
||||
.CreateLogger("JwtBearerAuthentication");
|
||||
|
||||
logger.LogDebug(
|
||||
"JWT token validated for subject: {Subject}",
|
||||
context.Principal?.FindFirst("sub")?.Value ?? "unknown");
|
||||
|
||||
return existingEvents?.OnTokenValidated?.Invoke(context) ?? Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static IServiceCollection AddOidcCookieAuthentication(this IServiceCollection services, IConfiguration configuration, string cookieName = "fa_session")
|
||||
{
|
||||
var oidcConfig = configuration.GetSection("OIDC").Get<OidcConfiguration>();
|
||||
@@ -58,7 +103,7 @@ public static class AuthenticationExtensions
|
||||
options.Audience = oidcConfig.Audience;
|
||||
options.RequireHttpsMetadata = !string.IsNullOrEmpty(oidcConfig.Authority) && oidcConfig.Authority.StartsWith("https://");
|
||||
|
||||
options.Events = new JwtBearerEvents
|
||||
var cookieEvents = new JwtBearerEvents
|
||||
{
|
||||
OnMessageReceived = context =>
|
||||
{
|
||||
@@ -71,10 +116,12 @@ public static class AuthenticationExtensions
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
options.Events = CreateLoggingJwtBearerEvents(cookieEvents);
|
||||
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = oidcConfig.ValidateIssuer,
|
||||
ValidIssuer = oidcConfig.ValidIssuer,
|
||||
ValidateAudience = oidcConfig.ValidateAudience,
|
||||
ValidateLifetime = oidcConfig.ValidateLifetime,
|
||||
ValidateIssuerSigningKey = oidcConfig.ValidateIssuerSigningKey,
|
||||
@@ -88,8 +135,8 @@ public static class AuthenticationExtensions
|
||||
public static IServiceCollection AddFictionArchiveAuthorization(this IServiceCollection services)
|
||||
{
|
||||
services.AddAuthorizationBuilder()
|
||||
.AddPolicy("Admin", policy => policy.RequireRole("admin"))
|
||||
.AddPolicy("User", policy => policy.RequireAuthenticatedUser());
|
||||
.AddPolicy(AuthorizationConstants.Policies.Admin, policy => policy.RequireRole(AuthorizationConstants.Roles.Admin))
|
||||
.AddPolicy(AuthorizationConstants.Policies.User, policy => policy.RequireAuthenticatedUser());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="GraphQL.Server.Ui.GraphiQL" Version="8.3.3" />
|
||||
<PackageReference Include="HotChocolate.AspNetCore" Version="15.1.11" />
|
||||
<PackageReference Include="HotChocolate.AspNetCore.Authorization" Version="15.1.11" />
|
||||
<PackageReference Include="HotChocolate.AspNetCore.CommandLine" Version="15.1.11" />
|
||||
<PackageReference Include="HotChocolate.Data.EntityFramework" Version="15.1.11" />
|
||||
<PackageReference Include="HotChocolate.Types.Scalars" Version="15.1.11" />
|
||||
|
||||
@@ -5,6 +5,7 @@ public class OidcConfiguration
|
||||
public string Authority { get; set; } = string.Empty;
|
||||
public string ClientId { get; set; } = string.Empty;
|
||||
public string Audience { get; set; } = string.Empty;
|
||||
public string? ValidIssuer { get; set; }
|
||||
public bool ValidateIssuer { get; set; } = true;
|
||||
public bool ValidateAudience { get; set; } = true;
|
||||
public bool ValidateLifetime { get; set; } = true;
|
||||
|
||||
@@ -5,11 +5,13 @@ using FictionArchive.Service.TranslationService.Models.Enums;
|
||||
using FictionArchive.Service.TranslationService.Services;
|
||||
using FictionArchive.Service.TranslationService.Services.Database;
|
||||
using FictionArchive.Service.TranslationService.Services.TranslationEngines;
|
||||
using HotChocolate.Authorization;
|
||||
|
||||
namespace FictionArchive.Service.TranslationService.GraphQL;
|
||||
|
||||
public class Mutation
|
||||
{
|
||||
[Authorize]
|
||||
public async Task<TranslationResult> TranslateText(string text, Language from, Language to, string translationEngineKey, TranslationEngineService translationEngineService)
|
||||
{
|
||||
var result = await translationEngineService.Translate(from, to, text, translationEngineKey);
|
||||
|
||||
@@ -2,12 +2,14 @@ using FictionArchive.Service.TranslationService.Models;
|
||||
using FictionArchive.Service.TranslationService.Models.Database;
|
||||
using FictionArchive.Service.TranslationService.Services.Database;
|
||||
using FictionArchive.Service.TranslationService.Services.TranslationEngines;
|
||||
using HotChocolate.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace FictionArchive.Service.TranslationService.GraphQL;
|
||||
|
||||
public class Query
|
||||
{
|
||||
[Authorize]
|
||||
[UseFiltering]
|
||||
[UseSorting]
|
||||
public IEnumerable<TranslationEngineDescriptor> GetTranslationEngines(IEnumerable<ITranslationEngine> engines)
|
||||
@@ -15,6 +17,7 @@ public class Query
|
||||
return engines.Select(engine => engine.Descriptor);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[UsePaging]
|
||||
[UseProjection]
|
||||
[UseFiltering]
|
||||
|
||||
@@ -50,7 +50,8 @@ public class Program
|
||||
|
||||
#region GraphQL
|
||||
|
||||
builder.Services.AddDefaultGraphQl<Query, Mutation>();
|
||||
builder.Services.AddDefaultGraphQl<Query, Mutation>()
|
||||
.AddAuthorization();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -66,6 +67,10 @@ public class Program
|
||||
|
||||
#endregion
|
||||
|
||||
// Authentication & Authorization
|
||||
builder.Services.AddOidcAuthentication(builder.Configuration);
|
||||
builder.Services.AddFictionArchiveAuthorization();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Update database (skip in schema export mode)
|
||||
@@ -80,6 +85,9 @@ public class Program
|
||||
|
||||
app.MapHealthChecks("/healthz");
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapGraphQL();
|
||||
|
||||
app.RunWithGraphQLCommands(args);
|
||||
|
||||
@@ -15,5 +15,15 @@
|
||||
"ConnectionString": "amqp://localhost",
|
||||
"ClientIdentifier": "TranslationService"
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
"AllowedHosts": "*",
|
||||
"OIDC": {
|
||||
"Authority": "https://auth.orfl.xyz/application/o/fiction-archive/",
|
||||
"ClientId": "fictionarchive-api",
|
||||
"Audience": "fictionarchive-api",
|
||||
"ValidIssuer": "https://auth.orfl.xyz/application/o/fiction-archive/",
|
||||
"ValidateIssuer": true,
|
||||
"ValidateAudience": true,
|
||||
"ValidateLifetime": true,
|
||||
"ValidateIssuerSigningKey": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using FictionArchive.Service.Shared.Constants;
|
||||
using FictionArchive.Service.UserService.Models.Database;
|
||||
using FictionArchive.Service.UserService.Services;
|
||||
using HotChocolate.Authorization;
|
||||
|
||||
namespace FictionArchive.Service.UserService.GraphQL;
|
||||
|
||||
public class Mutation
|
||||
{
|
||||
[Authorize(Roles = [AuthorizationConstants.Roles.Admin])]
|
||||
public async Task<User> RegisterUser(string username, string email, string oAuthProviderId,
|
||||
string? inviterOAuthProviderId, UserManagementService userManagementService)
|
||||
{
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using FictionArchive.Service.UserService.Models.Database;
|
||||
using FictionArchive.Service.UserService.Services;
|
||||
using HotChocolate.Authorization;
|
||||
|
||||
namespace FictionArchive.Service.UserService.GraphQL;
|
||||
|
||||
public class Query
|
||||
{
|
||||
[Authorize]
|
||||
public async Task<IQueryable<User>> GetUsers(UserManagementService userManagementService)
|
||||
{
|
||||
return userManagementService.GetUsers();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using FictionArchive.Common.Extensions;
|
||||
using FictionArchive.Service.Shared;
|
||||
using FictionArchive.Service.Shared.Extensions;
|
||||
using FictionArchive.Service.Shared.Services.EventBus.Implementations;
|
||||
@@ -15,6 +16,7 @@ public class Program
|
||||
var isSchemaExport = SchemaExportDetector.IsSchemaExportMode(args);
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.AddLocalAppsettings();
|
||||
|
||||
#region Event Bus
|
||||
|
||||
@@ -31,7 +33,8 @@ public class Program
|
||||
|
||||
#region GraphQL
|
||||
|
||||
builder.Services.AddDefaultGraphQl<Query, Mutation>();
|
||||
builder.Services.AddDefaultGraphQl<Query, Mutation>()
|
||||
.AddAuthorization();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -42,6 +45,10 @@ public class Program
|
||||
|
||||
builder.Services.AddHealthChecks();
|
||||
|
||||
// Authentication & Authorization
|
||||
builder.Services.AddOidcAuthentication(builder.Configuration);
|
||||
builder.Services.AddFictionArchiveAuthorization();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Update database (skip in schema export mode)
|
||||
@@ -52,6 +59,9 @@ public class Program
|
||||
dbContext.UpdateDatabase();
|
||||
}
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapGraphQL();
|
||||
|
||||
app.MapHealthChecks("/healthz");
|
||||
|
||||
@@ -12,5 +12,15 @@
|
||||
"ConnectionString": "amqp://localhost",
|
||||
"ClientIdentifier": "UserService"
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
"AllowedHosts": "*",
|
||||
"OIDC": {
|
||||
"Authority": "https://auth.orfl.xyz/application/o/fiction-archive/",
|
||||
"ClientId": "fictionarchive-api",
|
||||
"Audience": "fictionarchive-api",
|
||||
"ValidIssuer": "https://auth.orfl.xyz/application/o/fiction-archive/",
|
||||
"ValidateIssuer": true,
|
||||
"ValidateAudience": true,
|
||||
"ValidateLifetime": true,
|
||||
"ValidateIssuerSigningKey": true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user