diff --git a/src/SimpleAuthentication.Swashbuckle/Swagger/AuthenticationOperationFilter.cs b/src/SimpleAuthentication.Swashbuckle/Swagger/AuthenticationOperationFilter.cs index f17f597..4b9a451 100644 --- a/src/SimpleAuthentication.Swashbuckle/Swagger/AuthenticationOperationFilter.cs +++ b/src/SimpleAuthentication.Swashbuckle/Swagger/AuthenticationOperationFilter.cs @@ -22,8 +22,8 @@ public async Task ApplyAsync(OpenApiOperation operation, OperationFilterContext if ((requireAuthenticatedUser || requireAuthorization) && !allowAnonymous) { - operation.Responses.TryAdd(StatusCodes.Status401Unauthorized.ToString(), Helpers.CreateResponse(HttpStatusCode.Unauthorized.ToString())); - operation.Responses.TryAdd(StatusCodes.Status403Forbidden.ToString(), Helpers.CreateResponse(HttpStatusCode.Forbidden.ToString())); + operation.Responses.TryAdd(StatusCodes.Status401Unauthorized.ToString(), OpenApiHelpers.CreateResponse(HttpStatusCode.Unauthorized.ToString())); + operation.Responses.TryAdd(StatusCodes.Status403Forbidden.ToString(), OpenApiHelpers.CreateResponse(HttpStatusCode.Forbidden.ToString())); } } } diff --git a/src/SimpleAuthentication.Swashbuckle/Swagger/Helpers.cs b/src/SimpleAuthentication.Swashbuckle/Swagger/OpenApiHelpers.cs similarity index 97% rename from src/SimpleAuthentication.Swashbuckle/Swagger/Helpers.cs rename to src/SimpleAuthentication.Swashbuckle/Swagger/OpenApiHelpers.cs index ca7b11d..2af5e61 100644 --- a/src/SimpleAuthentication.Swashbuckle/Swagger/Helpers.cs +++ b/src/SimpleAuthentication.Swashbuckle/Swagger/OpenApiHelpers.cs @@ -4,7 +4,7 @@ namespace SimpleAuthentication.Swagger; -internal static class Helpers +internal static class OpenApiHelpers { public static OpenApiSecurityRequirement CreateSecurityRequirement(string name) => new() diff --git a/src/SimpleAuthentication.Swashbuckle/SwaggerExtensions.cs b/src/SimpleAuthentication.Swashbuckle/SwaggerExtensions.cs index 64fd380..2681219 100644 --- a/src/SimpleAuthentication.Swashbuckle/SwaggerExtensions.cs +++ b/src/SimpleAuthentication.Swashbuckle/SwaggerExtensions.cs @@ -49,7 +49,7 @@ public static void AddSimpleAuthentication(this SwaggerGenOptions options, IConf /// public static void AddSimpleAuthentication(this SwaggerGenOptions options, IConfiguration configuration, string sectionName, IEnumerable? additionalSecurityDefinitionNames) { - var securityRequirements = additionalSecurityDefinitionNames?.Select(Helpers.CreateSecurityRequirement).ToArray(); + var securityRequirements = additionalSecurityDefinitionNames?.Select(OpenApiHelpers.CreateSecurityRequirement).ToArray(); options.AddSimpleAuthentication(configuration, sectionName, securityRequirements ?? []); } @@ -150,8 +150,59 @@ static void AddSecurityDefinition(SwaggerGenOptions options, string name, Securi Type = securitySchemeType, Scheme = scheme }); + } + + /// + /// Adds OAuth2 authentication support in Swagger, for example for example to integrate with Microsoft.Identity.Web. + /// + /// The to add configuration to. + /// The name of the OAuth2 authentication scheme to add. + /// The Authorization Url for OAuth2 authorization. + /// The Token Url for OAuth2 authorization. + /// The list of scopes. + /// + public static void AddOAuth2Authentication(this SwaggerGenOptions options, string name, string authorizationUrl, string tokenUrl, IDictionary scopes) + { + ArgumentNullException.ThrowIfNull(options); + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentException.ThrowIfNullOrWhiteSpace(authorizationUrl); + ArgumentException.ThrowIfNullOrWhiteSpace(tokenUrl); + ArgumentNullException.ThrowIfNull(scopes); - static void AddSecurityRequirement(SwaggerGenOptions options, string name) - => options.AddSecurityRequirement(Helpers.CreateSecurityRequirement(name)); + options.AddOAuth2Authentication(name, new() + { + AuthorizationUrl = new(authorizationUrl), + TokenUrl = new(tokenUrl), + Scopes = scopes + }); } + + /// + /// Adds OAuth2 authentication support in Open API, for example for example to integrate with Microsoft.Identity.Web. + /// + /// The to add configuration to. + /// The name of the OAuth2 authentication scheme to add. + /// The object that describes the authorization flow. + /// + /// + public static void AddOAuth2Authentication(this SwaggerGenOptions options, string name, OpenApiOAuthFlow authFlow) + { + ArgumentNullException.ThrowIfNull(options); + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentNullException.ThrowIfNull(authFlow); + + options.AddSecurityDefinition(name, new() + { + Type = SecuritySchemeType.OAuth2, + Flows = new() + { + Implicit = authFlow + } + }); + + AddSecurityRequirement(options, name); + } + + private static void AddSecurityRequirement(SwaggerGenOptions options, string name) + => options.AddSecurityRequirement(OpenApiHelpers.CreateSecurityRequirement(name)); } diff --git a/src/SimpleAuthentication/OpenApi/AuthenticationDocumentTransformer.cs b/src/SimpleAuthentication/OpenApi/AuthenticationDocumentTransformer.cs index 574e586..191c0ed 100644 --- a/src/SimpleAuthentication/OpenApi/AuthenticationDocumentTransformer.cs +++ b/src/SimpleAuthentication/OpenApi/AuthenticationDocumentTransformer.cs @@ -88,16 +88,12 @@ private static void AddSecurityScheme(OpenApiDocument document, string name, Sec Name = parameterName, Description = description, Type = securitySchemeType, - Scheme = scheme, - //Reference = new() - //{ - // Id = name, - // Type = ReferenceType.SecurityScheme - //} + Scheme = scheme }); } - private static void AddSecurityRequirement(OpenApiDocument document, string name) - => AddSecurityRequirement(document, Helpers.CreateSecurityRequirement(name)); + + internal static void AddSecurityRequirement(OpenApiDocument document, string name) + => AddSecurityRequirement(document, OpenApiHelpers.CreateSecurityRequirement(name)); private static void AddSecurityRequirement(OpenApiDocument document, OpenApiSecurityRequirement requirement) { diff --git a/src/SimpleAuthentication/OpenApi/AuthenticationOperationTransformer.cs b/src/SimpleAuthentication/OpenApi/AuthenticationOperationTransformer.cs index 72ac26a..4883f63 100644 --- a/src/SimpleAuthentication/OpenApi/AuthenticationOperationTransformer.cs +++ b/src/SimpleAuthentication/OpenApi/AuthenticationOperationTransformer.cs @@ -24,8 +24,8 @@ public async Task TransformAsync(OpenApiOperation operation, OpenApiOperationTra if ((requireAuthenticatedUser || requireAuthorization) && !allowAnonymous) { - operation.Responses.TryAdd(StatusCodes.Status401Unauthorized.ToString(), Helpers.CreateResponse(HttpStatusCode.Unauthorized.ToString())); - operation.Responses.TryAdd(StatusCodes.Status403Forbidden.ToString(), Helpers.CreateResponse(HttpStatusCode.Forbidden.ToString())); + operation.Responses.TryAdd(StatusCodes.Status401Unauthorized.ToString(), OpenApiHelpers.CreateResponse(HttpStatusCode.Unauthorized.ToString())); + operation.Responses.TryAdd(StatusCodes.Status403Forbidden.ToString(), OpenApiHelpers.CreateResponse(HttpStatusCode.Forbidden.ToString())); } } } diff --git a/src/SimpleAuthentication/OpenApi/OAuth2AuthenticationDocumentTransformer.cs b/src/SimpleAuthentication/OpenApi/OAuth2AuthenticationDocumentTransformer.cs new file mode 100644 index 0000000..8325a7e --- /dev/null +++ b/src/SimpleAuthentication/OpenApi/OAuth2AuthenticationDocumentTransformer.cs @@ -0,0 +1,30 @@ +#if NET9_0_OR_GREATER + +using Microsoft.AspNetCore.OpenApi; +using Microsoft.OpenApi.Models; + +namespace SimpleAuthentication.OpenApi; + +internal class OAuth2AuthenticationDocumentTransformer(string name, OpenApiOAuthFlow authFlow) : IOpenApiDocumentTransformer +{ + public Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken) + { + document.Components ??= new(); + document.Components.SecuritySchemes ??= new Dictionary(); + + document.Components.SecuritySchemes.Add(name, new() + { + Type = SecuritySchemeType.OAuth2, + Flows = new() + { + Implicit = authFlow + } + }); + + AuthenticationDocumentTransformer.AddSecurityRequirement(document, name); + + return Task.CompletedTask; + } +} + +#endif \ No newline at end of file diff --git a/src/SimpleAuthentication/OpenApi/Helpers.cs b/src/SimpleAuthentication/OpenApi/OpenApiHelpers.cs similarity index 97% rename from src/SimpleAuthentication/OpenApi/Helpers.cs rename to src/SimpleAuthentication/OpenApi/OpenApiHelpers.cs index b777c6d..737b882 100644 --- a/src/SimpleAuthentication/OpenApi/Helpers.cs +++ b/src/SimpleAuthentication/OpenApi/OpenApiHelpers.cs @@ -6,7 +6,7 @@ namespace SimpleAuthentication.OpenApi; -internal static class Helpers +internal static class OpenApiHelpers { public static OpenApiSecurityRequirement CreateSecurityRequirement(string name) => new() diff --git a/src/SimpleAuthentication/OpenApiExtensions.cs b/src/SimpleAuthentication/OpenApiExtensions.cs index ea582e5..d217eff 100644 --- a/src/SimpleAuthentication/OpenApiExtensions.cs +++ b/src/SimpleAuthentication/OpenApiExtensions.cs @@ -1,14 +1,8 @@ #if NET9_0_OR_GREATER -using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.OpenApi; using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Net.Http.Headers; using Microsoft.OpenApi.Models; -using SimpleAuthentication.ApiKey; -using SimpleAuthentication.BasicAuthentication; -using SimpleAuthentication.JwtBearer; using SimpleAuthentication.OpenApi; namespace SimpleAuthentication; @@ -51,7 +45,7 @@ public static void AddSimpleAuthentication(this OpenApiOptions options, IConfigu /// public static void AddSimpleAuthentication(this OpenApiOptions options, IConfiguration configuration, string sectionName, params IEnumerable additionalSecurityDefinitionNames) { - var securityRequirements = additionalSecurityDefinitionNames?.Select(Helpers.CreateSecurityRequirement).ToArray(); + var securityRequirements = additionalSecurityDefinitionNames?.Select(OpenApiHelpers.CreateSecurityRequirement).ToArray(); options.AddSimpleAuthentication(configuration, sectionName, securityRequirements ?? []); } @@ -84,6 +78,47 @@ public static void AddSimpleAuthentication(this OpenApiOptions options, IConfigu options.AddDocumentTransformer(new AuthenticationDocumentTransformer(configuration, sectionName, additionalSecurityRequirements)); options.AddOperationTransformer(); } + + /// + /// Adds OAuth2 custom authentication support in Open API, for example to integrate with Microsoft.Identity.Web. + /// + /// The to add configuration to. + /// The name of the OAuth2 authentication scheme to add. + /// The Authorization Url for OAuth2 authorization. + /// The Token Url for OAuth2 authorization. + /// The list of scopes. + /// + public static void AddOAuth2Authentication(this OpenApiOptions options, string name, string authorizationUrl, string tokenUrl, IDictionary scopes) + { + ArgumentNullException.ThrowIfNull(options); + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentException.ThrowIfNullOrWhiteSpace(authorizationUrl); + ArgumentException.ThrowIfNullOrWhiteSpace(tokenUrl); + ArgumentNullException.ThrowIfNull(scopes); + + options.AddOAuth2Authentication(name, new() + { + AuthorizationUrl = new(authorizationUrl), + TokenUrl = new(tokenUrl), + Scopes = scopes + }); + } + + /// + /// Adds OAuth2 custom authentication support in Open API, for example to integrate with Microsoft.Identity.Web. + /// + /// The to add configuration to. + /// The name of the OAuth2 authentication scheme to add. + /// The object that describes the authorization flow. + /// + /// + public static void AddOAuth2Authentication(this OpenApiOptions options, string name, OpenApiOAuthFlow authFlow) + { + ArgumentNullException.ThrowIfNull(options); + ArgumentNullException.ThrowIfNull(authFlow); + + options.AddDocumentTransformer(new OAuth2AuthenticationDocumentTransformer(name, authFlow)); + } } #endif \ No newline at end of file