diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index ef763fe..65c2484 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -16,7 +16,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: '9.0' + dotnet-version: '10.0' include-prerelease: True - name: Restore dependencies run: dotnet restore diff --git a/BlazorAuth0Bff/Client/BlazorAuth0Bff.Client.csproj b/BlazorAuth0Bff/Client/BlazorAuth0Bff.Client.csproj index 4ee5b70..ff108da 100644 --- a/BlazorAuth0Bff/Client/BlazorAuth0Bff.Client.csproj +++ b/BlazorAuth0Bff/Client/BlazorAuth0Bff.Client.csproj @@ -1,17 +1,17 @@  - net9.0 + net10.0 true enable enable - - - - + + + + diff --git a/BlazorAuth0Bff/Server/BlazorAuth0Bff.Server.csproj b/BlazorAuth0Bff/Server/BlazorAuth0Bff.Server.csproj index afda91c..a107faa 100644 --- a/BlazorAuth0Bff/Server/BlazorAuth0Bff.Server.csproj +++ b/BlazorAuth0Bff/Server/BlazorAuth0Bff.Server.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable de0b7f31-65d4-46d6-8382-30c94073cf4a @@ -13,10 +13,10 @@ - - - - + + + + diff --git a/BlazorAuth0Bff/Server/appsettings.json b/BlazorAuth0Bff/Server/appsettings.json index 306d92b..33081d8 100644 --- a/BlazorAuth0Bff/Server/appsettings.json +++ b/BlazorAuth0Bff/Server/appsettings.json @@ -10,7 +10,7 @@ "Microsoft.Hosting.Lifetime": "Information" } }, - "MyApiUrl": "https://localhost:44390", + "MyApiUrl": "https://localhost:5014", "AllowedHosts": "*" //"Auth0": { // "Domain": "your-domain-in-auth0", diff --git a/BlazorAuth0Bff/Shared/BlazorAuth0Bff.Shared.csproj b/BlazorAuth0Bff/Shared/BlazorAuth0Bff.Shared.csproj index 4b5c766..9a4d7b5 100644 --- a/BlazorAuth0Bff/Shared/BlazorAuth0Bff.Shared.csproj +++ b/BlazorAuth0Bff/Shared/BlazorAuth0Bff.Shared.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 enable enable diff --git a/MyApi/Controllers/AzureADUserOneController.cs b/MyApi/Controllers/EntraIdUserOneController.cs similarity index 70% rename from MyApi/Controllers/AzureADUserOneController.cs rename to MyApi/Controllers/EntraIdUserOneController.cs index 9cdd026..92d1d34 100644 --- a/MyApi/Controllers/AzureADUserOneController.cs +++ b/MyApi/Controllers/EntraIdUserOneController.cs @@ -1,33 +1,30 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Identity.Web; -using Swashbuckle.AspNetCore.Annotations; -using System.Collections.Generic; - -namespace MyApi.Controllers; - -/// -/// API protected with Microsoft.Identity.Web and Microsoft Entra ID -/// scope from App registration used to authorize. -/// -[SwaggerTag("API protected with Microsoft.Identity.Web and Microsoft Entra ID")] -[AuthorizeForScopes(Scopes = ["api://b2a09168-54e2-4bc4-af92-a710a64ef1fa/access_as_user"], - AuthenticationScheme = "myADscheme")] -[Authorize(AuthenticationSchemes = "myADscheme")] -[ApiController] -[Route("api/[controller]")] -public class AzureADUserOneController : ControllerBase -{ - /// - /// returns data id the correct Microsoft Entra ID access token is used with the correct scope. - /// - /// protected data - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - public IEnumerable Get() - { - return new List { "Microsoft Entra ID user one data" }; - } +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Identity.Web; +using System.Collections.Generic; + +namespace MyApi.Controllers; + +/// +/// API protected with Microsoft.Identity.Web and Microsoft Entra ID +/// scope from App registration used to authorize. +/// +[AuthorizeForScopes(Scopes = ["api://b2a09168-54e2-4bc4-af92-a710a64ef1fa/access_as_user"], AuthenticationScheme = "myADscheme")] +[Authorize(AuthenticationSchemes = "myADscheme")] +[ApiController] +[Route("api/[controller]")] +public class EntraIdUserOneController : ControllerBase +{ + /// + /// returns data id the correct Microsoft Entra ID access token is used with the correct scope. + /// + /// protected data + [HttpGet("")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + public IEnumerable Get() + { + return ["Microsoft Entra ID user one data"]; + } } \ No newline at end of file diff --git a/MyApi/Controllers/ServiceTwoController.cs b/MyApi/Controllers/ServiceTwoController.cs index 7646894..3edc45a 100644 --- a/MyApi/Controllers/ServiceTwoController.cs +++ b/MyApi/Controllers/ServiceTwoController.cs @@ -1,7 +1,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Swashbuckle.AspNetCore.Annotations; using System.Collections.Generic; namespace MyApi.Controllers; @@ -10,7 +9,6 @@ namespace MyApi.Controllers; /// Service access token protected using Auth0 /// protected using "p-service-api-auth0" policy defined in the Startup /// -[SwaggerTag("Service access token protected using Auth0 ")] [Authorize(Policy = "p-service-api-auth0")] [ApiController] [Route("api/[controller]")] diff --git a/MyApi/Controllers/UserOneController.cs b/MyApi/Controllers/UserOneController.cs index 8ade4f2..33bea2c 100644 --- a/MyApi/Controllers/UserOneController.cs +++ b/MyApi/Controllers/UserOneController.cs @@ -1,7 +1,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -using Swashbuckle.AspNetCore.Annotations; using System.Collections.Generic; namespace MyApi.Controllers; @@ -10,7 +9,6 @@ namespace MyApi.Controllers; /// User access token protected using Auth0 /// protected using "p-user-api-auth0" policy defined in the Startup /// -[SwaggerTag("User access token protected using Auth0")] [Authorize(Policy = "p-user-api-auth0")] [ApiController] [Route("api/[controller]")] diff --git a/MyApi/MyApi.csproj b/MyApi/MyApi.csproj index 528c977..6638493 100644 --- a/MyApi/MyApi.csproj +++ b/MyApi/MyApi.csproj @@ -1,7 +1,7 @@  - net9.0 + net10.0 7b15921c-6a17-41fd-90cc-86a09d128527 true $(NoWarn);1591 @@ -9,18 +9,18 @@ - - - - - - - - - - - - + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + diff --git a/MyApi/Program.cs b/MyApi/Program.cs index 443c454..ca1db61 100644 --- a/MyApi/Program.cs +++ b/MyApi/Program.cs @@ -1,17 +1,20 @@ +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Mvc.Authorization; +using Microsoft.AspNetCore.OpenApi; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Identity.Web; using Microsoft.IdentityModel.JsonWebTokens; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using MyApi; using NetEscapades.AspNetCore.SecurityHeaders.Infrastructure; -using System; -using System.IO; -using System.Reflection; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; var builder = WebApplication.CreateBuilder(args); @@ -54,47 +57,19 @@ options.Audience = "https://auth0-api1"; }); -services.AddSwaggerGen(c => +services.AddOpenApi(options => { - c.EnableAnnotations(); - // add JWT Authentication - var securityScheme = new OpenApiSecurityScheme - { - Name = "JWT Authentication", - Description = "Enter JWT Bearer token **_only_**", - In = ParameterLocation.Header, - Type = SecuritySchemeType.Http, - Scheme = "bearer", // must be lower case - BearerFormat = "JWT", - Reference = new OpenApiReference - { - Id = JwtBearerDefaults.AuthenticationScheme, - Type = ReferenceType.SecurityScheme - } - }; - c.AddSecurityDefinition(securityScheme.Reference.Id, securityScheme); - c.AddSecurityRequirement(new OpenApiSecurityRequirement - { - {securityScheme, Array.Empty()} - }); - - c.SwaggerDoc("v1", new OpenApiInfo - { - Title = "My API", - Version = "v1", - Description = "My API", - Contact = new OpenApiContact - { - Name = "damienbod", - Email = string.Empty, - Url = new Uri("https://damienbod.com/"), - } - }); - - // Set the comments path for the Swagger JSON and UI. - var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; - var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); - c.IncludeXmlComments(xmlPath); + //options.UseTransformer((document, context, cancellationToken) => + //{ + // document.Info = new() + // { + // Title = "My API", + // Version = "v1", + // Description = "API for Damien" + // }; + // return Task.CompletedTask; + //}); + options.AddDocumentTransformer(); }); services.AddSingleton(); @@ -130,18 +105,18 @@ app.UseSecurityHeaders(); -app.UseSwagger(); -app.UseSwaggerUI(c => +//app.MapOpenApi(); // /openapi/v1.json +app.MapOpenApi("/openapi/v1/openapi.json"); +//app.MapOpenApi("/openapi/{documentName}/openapi.json"); + +app.UseSwaggerUI(options => { - c.SwaggerEndpoint("/swagger/v1/swagger.json", "User API"); - c.RoutePrefix = string.Empty; + options.SwaggerEndpoint("/openapi/v1/openapi.json", "v1"); }); // only needed for browser clients // app.UseCors("AllowAllOrigins"); -app.UseHttpsRedirection(); - app.UseRouting(); app.UseAuthentication(); @@ -150,3 +125,32 @@ app.MapControllers(); app.Run(); + +internal sealed class BearerSecuritySchemeTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider) : IOpenApiDocumentTransformer +{ + public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken) + { + var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync(); + if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer")) + { + var requirements = new Dictionary + { + ["Bearer"] = new OpenApiSecurityScheme + { + Type = SecuritySchemeType.Http, + Scheme = "bearer", // "bearer" refers to the header name here + In = ParameterLocation.Header, + BearerFormat = "Json Web Token" + } + }; + document.Components ??= new OpenApiComponents(); + document.Components.SecuritySchemes = requirements; + } + document.Info = new() + { + Title = "My API Bearer scheme", + Version = "v1", + Description = "API for Damien" + }; + } +} \ No newline at end of file diff --git a/MyApi/Properties/launchSettings.json b/MyApi/Properties/launchSettings.json index 7ad22d0..1e59059 100644 --- a/MyApi/Properties/launchSettings.json +++ b/MyApi/Properties/launchSettings.json @@ -3,11 +3,11 @@ "MyApi": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "weatherforecast", + "launchUrl": "swagger", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, - "applicationUrl": "https://localhost:44390" + "applicationUrl": "https://localhost:5014" } } } \ No newline at end of file diff --git a/RazorMicrosoftEntraID/CallMyApiOne/MyApiOneService.cs b/RazorMicrosoftEntraID/CallMyApiOne/MyApiOneService.cs index 9eba962..29948bd 100644 --- a/RazorMicrosoftEntraID/CallMyApiOne/MyApiOneService.cs +++ b/RazorMicrosoftEntraID/CallMyApiOne/MyApiOneService.cs @@ -28,13 +28,13 @@ public async Task> GetApiDataAsync() var client = _clientFactory.CreateClient(); var scope = _configuration["MyApiOne:ScopeForAccessToken"]; - var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new List { scope! }); + var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync([scope!]); client.BaseAddress = new Uri(_configuration["MyApiOne:ApiBaseAddress"]!); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - var response = await client.GetAsync("api/AzureADUserOne"); + var response = await client.GetAsync("api/EntraIdUserOne"); if (response.IsSuccessStatusCode) { var responseContent = await response.Content.ReadAsStringAsync(); diff --git a/RazorMicrosoftEntraID/Pages/Shared/_Layout.cshtml b/RazorMicrosoftEntraID/Pages/Shared/_Layout.cshtml index e35cd92..1ba7539 100644 --- a/RazorMicrosoftEntraID/Pages/Shared/_Layout.cshtml +++ b/RazorMicrosoftEntraID/Pages/Shared/_Layout.cshtml @@ -39,7 +39,7 @@
- © 2025 - Microsoft Entra ID + © 2026 - Microsoft Entra ID
diff --git a/RazorMicrosoftEntraID/RazorMicrosoftEntraID.csproj b/RazorMicrosoftEntraID/RazorMicrosoftEntraID.csproj index 3a9673b..0360063 100644 --- a/RazorMicrosoftEntraID/RazorMicrosoftEntraID.csproj +++ b/RazorMicrosoftEntraID/RazorMicrosoftEntraID.csproj @@ -1,18 +1,18 @@  - net9.0 + net10.0 aspnet-RazorAzureAD-C5CB1E61-55D5-4D0D-A192-68F28533B4EA 1 enable - - - - - + + + + + diff --git a/RazorMicrosoftEntraID/appsettings.json b/RazorMicrosoftEntraID/appsettings.json index 508820f..18057a6 100644 --- a/RazorMicrosoftEntraID/appsettings.json +++ b/RazorMicrosoftEntraID/appsettings.json @@ -9,7 +9,7 @@ }, "MyApiOne": { "ScopeForAccessToken": "api://b2a09168-54e2-4bc4-af92-a710a64ef1fa/access_as_user", - "ApiBaseAddress": "https://localhost:44390" + "ApiBaseAddress": "https://localhost:5014" }, "Logging": { "LogLevel": {