From 15d9015e611b12ac64499c572337df66ab335528 Mon Sep 17 00:00:00 2001 From: "Dioum, Serigne Saliou Mbacke" Date: Mon, 19 Jun 2023 22:26:26 +0200 Subject: [PATCH 1/2] Feat: Oauth Code Flow --- .../Configuration/ConfigureSwaggerOptions.cs | 57 +++++++++++++++++-- .../Infrastructure/Options/AzureAdOptions.cs | 4 +- WebApp-Authentication-API/Program.cs | 11 +++- WebApp-Authentication-API/appsettings.json | 13 +++-- 4 files changed, 72 insertions(+), 13 deletions(-) diff --git a/WebApp-Authentication-API/Infrastructure/Configuration/ConfigureSwaggerOptions.cs b/WebApp-Authentication-API/Infrastructure/Configuration/ConfigureSwaggerOptions.cs index fd0e6dd..fea9339 100644 --- a/WebApp-Authentication-API/Infrastructure/Configuration/ConfigureSwaggerOptions.cs +++ b/WebApp-Authentication-API/Infrastructure/Configuration/ConfigureSwaggerOptions.cs @@ -18,6 +18,7 @@ public ConfigureSwaggerOptions(IOptions azureAdOptions) public void Configure(SwaggerGenOptions options) { #region OAuth2 Implicit + options.AddSecurityDefinition("OAuth2 Implicit", new OpenApiSecurityScheme { Type = SecuritySchemeType.OAuth2, @@ -33,7 +34,7 @@ public void Configure(SwaggerGenOptions options) { { azureAdOptions.CustomScopeApi, azureAdOptions.CustomScopeApi }, } - } + }, } }); @@ -53,9 +54,11 @@ public void Configure(SwaggerGenOptions options) new List() } }); - #endregion + + #endregion OAuth2 Implicit #region Bearer + options.AddSecurityDefinition(name: "Bearer", securityScheme: new OpenApiSecurityScheme { Name = "Authorization", @@ -81,9 +84,11 @@ public void Configure(SwaggerGenOptions options) new List() } }); - #endregion + + #endregion Bearer #region OAuth Client Credentials + options.AddSecurityDefinition("OAuth Client Credentials", new OpenApiSecurityScheme { Type = SecuritySchemeType.OAuth2, @@ -116,7 +121,49 @@ public void Configure(SwaggerGenOptions options) new List() } }); - #endregion + + #endregion OAuth Client Credentials + + #region Authorization code + + options.AddSecurityDefinition("OAuth2 autorization code", new OpenApiSecurityScheme + { + Type = SecuritySchemeType.OAuth2, + Scheme = "oauth2_auth_code", + In = ParameterLocation.Header, + Name = "Authorization", + Flows = new OpenApiOAuthFlows + { + AuthorizationCode = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri(azureAdOptions.AuthorizationUrl), + TokenUrl = new Uri(azureAdOptions.TokenUrl), + Scopes = new Dictionary + { + { "openid", "OpenID" }, + { azureAdOptions.CustomScopeApi, "Your API Scope" }, + } + } + } + }); + + options.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + In = ParameterLocation.Header, + Reference = new OpenApiReference + { + Id = "OAuth2 autorization code", + Type = ReferenceType.SecurityScheme + } + }, + new[] { "openid", azureAdOptions.CustomScopeApi } + } + }); + + #endregion Authorization code } } -} +} \ No newline at end of file diff --git a/WebApp-Authentication-API/Infrastructure/Options/AzureAdOptions.cs b/WebApp-Authentication-API/Infrastructure/Options/AzureAdOptions.cs index 4be7683..b148c82 100644 --- a/WebApp-Authentication-API/Infrastructure/Options/AzureAdOptions.cs +++ b/WebApp-Authentication-API/Infrastructure/Options/AzureAdOptions.cs @@ -6,6 +6,8 @@ public class AzureAdOptions public string? ClientId { get; set; } + public string? SwaggerClientId { get; set; } + public string? ClientSecret { get; set; } public string? CallbackPath { get; set; } @@ -24,4 +26,4 @@ public class AzureAdOptions public string CustomScopeApi => $"api://{ClientId}/{CustomScope}"; } -} +} \ No newline at end of file diff --git a/WebApp-Authentication-API/Program.cs b/WebApp-Authentication-API/Program.cs index eb6c6ea..25fdda8 100644 --- a/WebApp-Authentication-API/Program.cs +++ b/WebApp-Authentication-API/Program.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Options; using Microsoft.Identity.Web; using Swashbuckle.AspNetCore.SwaggerGen; @@ -33,7 +34,13 @@ if (app.Environment.IsDevelopment()) { app.UseSwagger(); - app.UseSwaggerUI(); + app.UseSwaggerUI(c => + { + c.OAuthUsePkce(); + c.SwaggerEndpoint("/swagger/v1/swagger.json", "Your API V1"); + c.OAuthClientId(azureAD.Value.SwaggerClientId); + c.OAuthScopes("openid", azureAD.Value.CustomScopeApi); + }); } app.UseHttpsRedirection(); @@ -44,4 +51,4 @@ app.MapControllers(); -app.Run(); +app.Run(); \ No newline at end of file diff --git a/WebApp-Authentication-API/appsettings.json b/WebApp-Authentication-API/appsettings.json index c3fd3e3..1e070c6 100644 --- a/WebApp-Authentication-API/appsettings.json +++ b/WebApp-Authentication-API/appsettings.json @@ -1,11 +1,14 @@ { "AzureAd": { - "Instance": "https://login.microsoftonline.com", - "Domain": "expertime365.onmicrosoft.com", - "TenantId": "6494460e-8600-4edc-850f-528e8faad290", - "ClientId": "fe9ef5ee-4a63-4d56-81e1-16ff4ba2eb3e", + "Instance": "https://login.microsoftonline.com/", + "Domain": "sdioumobertysoutlook.onmicrosoft.com", + "TenantId": "1b47abca-7f45-4ff8-9a9f-6ae33216768b", + "ClientId": "095cdf77-8dad-4ec1-a3f7-9db43f77ec3d", + "SwaggerClientId": "0bd89ded-6926-454d-a969-e04b8540dae6", "CallbackPath": "/signin-oidc", - "CustomScope": "access_as_user" + "Scopes": "user.read", + "ClientCertificates": [], + "CustomScope": "EmployerManagement.Read" }, "Logging": { "LogLevel": { From 60b59f4158b4fc2d84761c0e6921dee5e7a0e44b Mon Sep 17 00:00:00 2001 From: "Dioum, Serigne Saliou Mbacke" Date: Sun, 25 Jun 2023 01:17:35 +0200 Subject: [PATCH 2/2] Add: Add Oauth Code Flow --- .../ApiVersioningConfiguration.cs | 3 +- .../AuthenticationConfiguration.cs | 77 ++++++++++++++++++- 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/WebApp-Authentication-API/Infrastructure/Configuration/ApiVersioningConfiguration.cs b/WebApp-Authentication-API/Infrastructure/Configuration/ApiVersioningConfiguration.cs index 3511501..54b9bb6 100644 --- a/WebApp-Authentication-API/Infrastructure/Configuration/ApiVersioningConfiguration.cs +++ b/WebApp-Authentication-API/Infrastructure/Configuration/ApiVersioningConfiguration.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.DependencyInjection; namespace WebApp_Authentication_API.Infrastructure.Configuration { @@ -23,4 +22,4 @@ public static IServiceCollection AddCustomApiVersioning(this IServiceCollection return services; } } -} +} \ No newline at end of file diff --git a/WebApp-Authentication-API/Infrastructure/Configuration/AuthenticationConfiguration.cs b/WebApp-Authentication-API/Infrastructure/Configuration/AuthenticationConfiguration.cs index ef5d4f9..c5ee24d 100644 --- a/WebApp-Authentication-API/Infrastructure/Configuration/AuthenticationConfiguration.cs +++ b/WebApp-Authentication-API/Infrastructure/Configuration/AuthenticationConfiguration.cs @@ -1,4 +1,5 @@ using Microsoft.Identity.Web; +using Jwt = Microsoft.AspNetCore.Authentication.JwtBearer; using JwtDefaults = Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults; namespace WebApp_Authentication_API.Infrastructure.Configuration @@ -13,12 +14,82 @@ public static class AuthenticationConfiguration /// The so that additional calls can be chained. public static IServiceCollection AddCustomAuthentication(this IServiceCollection services, IConfiguration configuration) { - // Add services to the container. services .AddAuthentication(JwtDefaults.AuthenticationScheme) - .AddMicrosoftIdentityWebApi(configuration, Constants.AzureAd); + .AddMicrosoftIdentityWebApi(configuration, "Azure:AD"); + + //services.Configure(JwtDefaults.AuthenticationScheme, options => + //{ + // options.Events = new Jwt.JwtBearerEvents + // { + // // Handle the token validated event + // OnTokenValidated = OnTokenValidated + // }; + //}); return services; } + + /// + /// Called after the auth token is validated. + /// + //private static async Task OnTokenValidated(Jwt.TokenValidatedContext context) + //{ + // var userService = context.HttpContext.RequestServices.GetRequiredService(); + + // // Get user email from claims + // var uniqueId = context.Principal?.GetUniqueId(); + // var email = context.Principal?.GetEmail(); + // var name = context.Principal?.GetName(); + + // if (uniqueId is null) + // { + // var unknownUniqueIdentifierException = new UnknownUniqueIdentifierAuthenticationException("User does not have object identifier"); + // context.Fail(unknownUniqueIdentifierException); + // } + // else + // { + // if (string.IsNullOrEmpty(email)) + // { + // var unknownMailException = new UnknownEmailAuthenticationException($"User with object identifier '{uniqueId}' does not have an email."); + // context.Fail(unknownMailException); + // } + // else if (string.IsNullOrEmpty(name)) + // { + // var unknownNameException = new UnknownNameAuthenticationException($"User with object identifier '{uniqueId}' does not have a name."); + // context.Fail(unknownNameException); + // } + // else + // { + // // Check if user exists in database + // var user = await userService.GetUser(uniqueId.Value); + + // var identity = GetUserClaimsIdentity(user); + // context.Principal?.AddIdentity(identity); + // } + // } + //} + + //private static ClaimsIdentity GetUserClaimsIdentity(User? user) + //{ + // var claims = new List(); + + // if (user is not null && user.IsActive) + // { + // var nameClaim = new Claim(ClaimTypes.Name, user.Name ?? ""); + // claims.Add(nameClaim); + + // var userIdClaim = new Claim(CustomClaimTypes.UserId, user.UserId.ToString("d")); + // claims.Add(userIdClaim); + + // var isGlobalAdministratorClaim = new Claim(CustomClaimTypes.UserIsGlobalAdministrator, user.IsGlobalAdministrator.ToString()); + // claims.Add(isGlobalAdministratorClaim); + + // var permissionsClaim = new Claim(CustomClaimTypes.UserPermissions, JsonSerializer.Serialize(user.Permissions)); + // claims.Add(permissionsClaim); + // } + + // return new ClaimsIdentity(claims); + //} } -} +} \ No newline at end of file