using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.OpenApi; using Microsoft.Extensions.Options; using Microsoft.OpenApi; using Musify.Api.Authentication; using Musify.Application.Serialization; namespace Musify.Api.OpenApi; public sealed class OpenApiOptionsSetup(IOptions options) : IConfigureNamedOptions { private const string SecuritySchemeId = "OAuth2"; private readonly AuthenticationConfiguration _configuration = options.Value; public void Configure(string? name, OpenApiOptions options) => Configure(options); public void Configure(OpenApiOptions options) { options.AddDocumentTransformer((document, _, _) => { document.Components ??= new OpenApiComponents(); document.Components.SecuritySchemes ??= new Dictionary(); document.Components.SecuritySchemes[SecuritySchemeId] = new OpenApiSecurityScheme { Type = SecuritySchemeType.OAuth2, Flows = new OpenApiOAuthFlows { AuthorizationCode = new OpenApiOAuthFlow { AuthorizationUrl = new Uri(_configuration.AuthorizationEndpoint), TokenUrl = new Uri(_configuration.TokenEndpoint), Scopes = _configuration.Scopes.ToDictionary(scope => scope, scope => scope), }, }, }; return Task.CompletedTask; } ); options.AddSchemaTransformer((schema, context, _) => { if (context.JsonPropertyInfo?.CustomConverter is LongAsStringConverter or NullableLongAsStringConverter) { schema.Type = JsonSchemaType.String; schema.Format = null; } return Task.CompletedTask; } ); options.AddOperationTransformer((operation, context, _) => { var requiresAuthorization = context.Description.ActionDescriptor.EndpointMetadata .OfType() .Any(); var allowsAnonymous = context.Description.ActionDescriptor.EndpointMetadata .OfType() .Any(); if (!requiresAuthorization || allowsAnonymous) { return Task.CompletedTask; } operation.Security ??= new List(); operation.Security.Add(new OpenApiSecurityRequirement { [new OpenApiSecuritySchemeReference(SecuritySchemeId, context.Document)] = _configuration.Scopes.ToList(), } ); return Task.CompletedTask; } ); } }