Skip to content

Commit bcf4d9e

Browse files
committed
#164 Updated Swagger UI for JTW Bearer token auth using OAuth Implicit flow (not ideal as not as secure as Authorization code flow + PKCE but its a start.
1 parent 2654357 commit bcf4d9e

File tree

2 files changed

+81
-3
lines changed

2 files changed

+81
-3
lines changed

src/AdminAssistant.Blazor/Server/Startup.cs

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@
44
using FluentValidation.AspNetCore;
55
using MicroElements.Swashbuckle.FluentValidation;
66
using Microsoft.AspNetCore.Authentication.JwtBearer;
7+
using Microsoft.AspNetCore.Authorization;
78
using Microsoft.AspNetCore.Builder;
89
using Microsoft.AspNetCore.Hosting;
910
using Microsoft.AspNetCore.Mvc;
11+
using Microsoft.AspNetCore.Mvc.Authorization;
1012
using Microsoft.AspNetCore.ResponseCompression;
1113
using Microsoft.Extensions.Configuration;
1214
using Microsoft.Extensions.DependencyInjection;
1315
using Microsoft.Extensions.Hosting;
1416
using Microsoft.OpenApi.Models;
1517
using Swashbuckle.AspNetCore.Swagger;
18+
using System.Collections.Generic;
1619
using System.Linq;
1720

1821
namespace AdminAssistant.Blazor.Server
@@ -58,9 +61,14 @@ public void ConfigureServices(IServiceCollection services)
5861
services.AddResponseCompression(opts => opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/octet-stream" }));
5962

6063
services.AddHttpContextAccessor();
61-
services.AddControllers()
62-
.AddNewtonsoftJson()
63-
.AddFluentValidation(c => c.RegisterValidatorsFromAssemblyContaining<Infra.DAL.IDatabasePersistable>());
64+
services.AddControllers(opts =>
65+
{
66+
// Add [Authorize] for all controllers ...
67+
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
68+
opts.Filters.Add(new AuthorizeFilter(policy));
69+
70+
}).AddNewtonsoftJson()
71+
.AddFluentValidation(c => c.RegisterValidatorsFromAssemblyContaining<Infra.DAL.IDatabasePersistable>());
6472

6573
services.AddSwaggerGen(c =>
6674
{
@@ -77,10 +85,31 @@ public void ConfigureServices(IServiceCollection services)
7785
});
7886

7987
c.SwaggerDoc(WebAPIVersion, new OpenApiInfo { Title = WebAPITitle, Version = WebAPIVersion }); // Add OpenAPI/Swagger middleware
88+
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
89+
{
90+
Name = "Authorization",
91+
In = ParameterLocation.Header,
92+
Type = SecuritySchemeType.OAuth2,
93+
Flows = new OpenApiOAuthFlows
94+
{
95+
Implicit = new OpenApiOAuthFlow
96+
{
97+
Scopes = new Dictionary<string, string>
98+
{
99+
{ "openid", "Open Id" }
100+
},
101+
AuthorizationUrl = new System.Uri($"https://{configSettings.Auth0Authority}/" + "authorize?audience=" + configSettings.Auth0ApiIdentifier)
102+
}
103+
}
104+
});
80105
c.AddFluentValidationRules(); // Adds fluent validation rules to swagger schema See: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation
81106

82107
// Include documentation from Annotations (Swashbuckle.AspNetCore.Annotations)...
83108
c.EnableAnnotations(); // https://github.com/domaindrivendev/Swashbuckle.AspNetCore#install-and-enable-annotations
109+
110+
// Let Swagger UI know that we put Authorize on all endpoints using a filter policy
111+
// See services.AddControllers code above ...
112+
c.OperationFilter<SwaggerSecurityRequirementsOperationFilter>();
84113
});
85114
services.AddSwaggerGenNewtonsoftSupport();
86115

@@ -113,6 +142,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
113142
c.SwaggerEndpoint("/swagger/v1/swagger.json", WebAPITitle);
114143
c.RoutePrefix = "api-docs";
115144
c.DocExpansion(Swashbuckle.AspNetCore.SwaggerUI.DocExpansion.List);
145+
c.OAuthClientId(_configuration.GetSection(nameof(ConfigurationSettings)).Get<ConfigurationSettings>().Auth0ClientId);
116146
});
117147

118148
app.UseHttpsRedirection();
@@ -129,4 +159,51 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
129159
});
130160
}
131161
}
162+
163+
public class SwaggerSecurityRequirementsOperationFilter : Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter
164+
{
165+
/// <summary>
166+
/// Applies this filter on swagger documentation generation.
167+
/// </summary>
168+
/// <param name="operation"></param>
169+
/// <param name="context"></param>
170+
public void Apply(OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context)
171+
{
172+
// then check if there is a method-level 'AllowAnonymous', as this overrides any controller-level 'Authorize'
173+
var anonControllerScope = context
174+
.MethodInfo
175+
.DeclaringType
176+
.GetCustomAttributes(true)
177+
.OfType<AllowAnonymousAttribute>();
178+
179+
var anonMethodScope = context
180+
.MethodInfo
181+
.GetCustomAttributes(true)
182+
.OfType<AllowAnonymousAttribute>();
183+
184+
// only add authorization specification information if there is at least one 'Authorize' in the chain and NO method-level 'AllowAnonymous'
185+
if (!anonMethodScope.Any() && !anonControllerScope.Any())
186+
{
187+
// add generic message if the controller methods dont already specify the response type
188+
if (!operation.Responses.ContainsKey("401"))
189+
operation.Responses.Add("401", new OpenApiResponse { Description = "If Authorization header not present, has no value or no valid jwt bearer token" });
190+
191+
if (!operation.Responses.ContainsKey("403"))
192+
operation.Responses.Add("403", new OpenApiResponse { Description = "If user not authorized to perform requested action" });
193+
194+
var jwtAuthScheme = new OpenApiSecurityScheme
195+
{
196+
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
197+
};
198+
199+
operation.Security = new List<OpenApiSecurityRequirement>
200+
{
201+
new OpenApiSecurityRequirement
202+
{
203+
[ jwtAuthScheme ] = new List<string>()
204+
}
205+
};
206+
}
207+
}
208+
}
132209
}

src/AdminAssistant/DomainModel/Shared/ConfigurationSettings.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ public record ConfigurationSettings
66
public string ConnectionString { get; set; } = string.Empty;
77
public string Auth0Authority { get; set; } = string.Empty;
88
public string Auth0ApiIdentifier { get; set; } = string.Empty;
9+
public string Auth0ClientId { get; set; } = string.Empty;
910
}
1011
}

0 commit comments

Comments
 (0)