### Description I would like to use oAuth in my Minimal API project and found no guide to that for the very basic steps of it. Not in the minimal api and not in the detailed ones, like for the RemoteAuthenticationProvider/Handler issued there: - #35842 I were looking into this [`OpenIdDict`+Minimal API using Server Sample](https://github.com/IlGalvo/WinUI3-OAuth2Manager-Sample/blob/main/Server/Server/Program.cs) and that were using direct Minimal API endpoints in the Programm.cs like added below (1. Codeblock), which seemed to work in general, but as I seen in the ToDo Sample of the Minimal API docs, I wanted to put them into `.MapGroup("/connect", )` and use TypedResults, but now that `RequestDelegate` or `Delegate` on `.MapGet("/authorize",Authorize)` is somehow making problems 🤔 Thats why I were searching the minimal api docs through and came finally to the authentication docs here, which I hoped maybe could tell me how to do that request, which seems to require that HttpContext but nothing else and by that could show my user then this simplistic html templated page telling him about the success of his login. From that it should be easy going, isnt it? Possibly I dont even need that context, but I would not know a alternative way to do that for showing my user that for CallbackUri page... Can you complete the auth docs or/and tell me where I made that mistake? ## Sample Code: ```csharp app.MapGet("/connect/authorize", async context => { var request = context.GetOpenIddictServerRequest() ?? throw new InvalidOperationException("Invalid request"); var identity = new ClaimsIdentity(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, Claims.Name, Claims.Role); identity.AddClaim(Claims.Subject, "dummy_user_id"); identity.AddClaim(Claims.Name, "Test User"); var principal = new ClaimsPrincipal(identity); principal.SetScopes(Scopes.OpenId, Scopes.Profile, Scopes.Email); await context.SignInAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, principal); context.Response.Clear(); var template = await File.ReadAllTextAsync("page.html"); var code = Uri.EscapeDataString(context.GetOpenIddictServerResponse()!.Code!); var state = Uri.EscapeDataString(context.GetOpenIddictServerResponse()!.State!); var redirect = new UriBuilder(request.RedirectUri!) { Query = $"code={code}&state={state}" }.Uri.ToString(); var html = string.Format(template, redirect); context.Response.ContentType = "text/html; charset=utf-8"; await context.Response.WriteAsync(html); }); app.MapPost("/connect/token", async context => { var request = context.GetOpenIddictServerRequest() ?? throw new InvalidOperationException("Invalid request."); if (request.IsAuthorizationCodeGrantType()) { // Normally you retrieve the principal associated with the code. // For simplicity, here you recreate it – in production, check that the code has not // already been consumed and perform all necessary validations. var identity = new ClaimsIdentity(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); identity.AddClaim(Claims.Subject, "dummy_user_id"); identity.AddClaim(Claims.Name, "Test User"); var principal = new ClaimsPrincipal(identity); principal.SetScopes(request.GetScopes()); await context.SignInAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, principal); } else { // If the grant type is not recognized, trigger a Challenge or // return an error. await context.ChallengeAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } }); app.MapGet("/connect/userinfo", async context => { // Check that the request is authenticated var user = context.User; if (user?.Identity is null || !user.Identity.IsAuthenticated) { context.Response.StatusCode = 401; await context.Response.WriteAsync("Unauthorized."); return; } // Create the object to return. You can include more claims if necessary. var userInfo = new { sub = user.FindFirst(Claims.Subject)?.Value, name = user.FindFirst(Claims.Name)?.Value, email = user.FindFirst(Claims.Email)?.Value }; // Return the JSON with the user's information await context.Response.WriteAsJsonAsync(userInfo); }); ``` ## Refactored code This is the code I came up with, trying my best to apply the learning from the ToDo Sample, but that did not include such setup at all. And this is the Linting I get which I not know how to act on: <img width="1518" height="175" alt="Image" src="https://github.com/user-attachments/assets/73f3f0e2-20ea-404d-a611-f8b749a56ef9" /> Below I added comments on each of the Lines where I get the linting ```csharp public static RouteGroupBuilder MapAuthenticationEndpoints(this IEndpointRouteBuilder routes) { // Map the authentication endpoints var group = routes.MapGroup("/connect") .WithTags("Authentication"); group.MapGet("/authorize", Authorize) // <-- Linted .WithName("Authorize") .WithSummary("Authorize user") .WithDescription("Authorize a user and return an HTML page with the authorization code") .AllowAnonymous(); group.MapPost("/token", Token) // <-- Linted .RequireAuthorization() .WithName("Token") .WithSummary("Exchange authorization code for access token") .WithDescription("Exchange an authorization code for an access token"); group.MapGet("/userinfo", UserInfo) // <-- Linted .RequireAuthorization() .WithName("UserInfo") .WithSummary("Get user information") .WithDescription("Retrieve user information based on the authenticated user's claims"); return group; } private static async Task<Results<ContentHttpResult, BadRequest>> Authorize(HttpContext context) { var request = context.GetOpenIddictServerRequest(); if (request is null) return TypedResults.BadRequest(); var identity = new ClaimsIdentity(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, Claims.Name, Claims.Role); identity.AddClaim(Claims.Subject, "dummy_user_id"); identity.AddClaim(Claims.Name, "Test User"); var principal = new ClaimsPrincipal(identity); principal.SetScopes(Scopes.OpenId, Scopes.Profile, Scopes.Email); await context.SignInAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, principal); context.Response.Clear(); var template = await File.ReadAllTextAsync("page.html"); var code = Uri.EscapeDataString(context.GetOpenIddictServerResponse()!.Code!); var state = Uri.EscapeDataString(context.GetOpenIddictServerResponse()!.State!); var redirect = new UriBuilder(request.RedirectUri!) { Query = $"code={code}&state={state}" }.Uri.ToString(); var html = string.Format(template, redirect); return TypedResults.Content(html, MediaTypeNames.Text.Html, Encoding.UTF8, StatusCodes.Status200OK); } private static async Task<Results<Ok, UnauthorizedHttpResult, BadRequest>> Token(HttpContext context) { var request = context.GetOpenIddictServerRequest(); if (request is null) return TypedResults.BadRequest(); if (request.IsAuthorizationCodeGrantType()) { // Normally you retrieve the principal associated with the code. // For simplicity, here you recreate it – in production, check that the code has not // already been consumed and perform all necessary validations. var identity = new ClaimsIdentity(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); identity.AddClaim(Claims.Subject, "dummy_user_id"); identity.AddClaim(Claims.Name, "Test User"); var principal = new ClaimsPrincipal(identity); principal.SetScopes(request.GetScopes()); await context.SignInAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, principal); return TypedResults.Ok(); } else { // If the grant type is not recognized, trigger a Challenge or // return an error. await context.ChallengeAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); return TypedResults.Unauthorized(); } } private static Task<Results<Ok<object>, UnauthorizedHttpResult>> UserInfo(HttpContext context) { // Check that the request is authenticated var user = context.User; if (user?.Identity is null || !user.Identity.IsAuthenticated) { return Task.FromResult<Results<Ok<object>, UnauthorizedHttpResult>>(TypedResults.Unauthorized()); } // Create the object to return. You can include more claims if necessary. var userInfo = new { sub = user.FindFirst(Claims.Subject)?.Value, name = user.FindFirst(Claims.Name)?.Value, email = user.FindFirst(Claims.Email)?.Value }; return Task.FromResult<Results<Ok<object>, UnauthorizedHttpResult>>(TypedResults.Ok((object)userInfo)); } } ``` ## Related Issue First mentioned but unanswered as maybe it requires a seperate issue it now got: - https://github.com/dotnet/AspNetCore.Docs/issues/35835 ### Page URL https://learn.microsoft.com/de-de/aspnet/core/fundamentals/minimal-apis/security?view=aspnetcore-9.0 ### Content source URL https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/fundamentals/minimal-apis/security.md ### Document ID 3a9d7eb8-6c1f-4619-00fa-9b69dbe3dcea ### Platform Id d905a302-a7a8-ce53-f765-5ab6bef661e7 ### Article author @captainsafia ### Metadata * ID: 3a9d7eb8-6c1f-4619-00fa-9b69dbe3dcea * PlatformId: d905a302-a7a8-ce53-f765-5ab6bef661e7 * Service: **aspnet-core** * Sub-service: **fundamentals** [Related Issues](https://github.com/dotnet/AspNetCore.Docs/issues?q=is%3Aissue+is%3Aopen+3a9d7eb8-6c1f-4619-00fa-9b69dbe3dcea)