ASP.NET Core (OpenID Connect)

See the Applications overview for prerequisites, configuration endpoints, and available scopes.

ASP.NET Core includes built-in OpenID Connect middleware via the Microsoft.AspNetCore.Authentication.OpenIdConnect package. Requires .NET 10.0 or later.

Add the package:

dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect

Authentication configuration

Configure cookie and OpenID Connect authentication in Program.cs:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
    options.Authority = "https://us.vouch.sh";
    options.ClientId = Environment.GetEnvironmentVariable("VOUCH_CLIENT_ID");
    options.ClientSecret = Environment.GetEnvironmentVariable("VOUCH_CLIENT_SECRET");
    options.ResponseType = OpenIdConnectResponseType.Code;
    options.UsePkce = true;
    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("email");
    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;

    options.ClaimActions.MapJsonKey("hardware_verified", "hardware_verified");
    options.ClaimActions.MapJsonKey("hardware_aaguid", "hardware_aaguid");
});

builder.Services.AddAuthorization();

The ClaimActions.MapJsonKey calls map the Vouch-specific hardware_verified and hardware_aaguid claims from the UserInfo response into the user’s ClaimsPrincipal.

Login and logout endpoints

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapGet("/login", () =>
    Results.Challenge(
        new AuthenticationProperties { RedirectUri = "/" },
        [OpenIdConnectDefaults.AuthenticationScheme]));

app.MapPost("/logout", async (HttpContext context) =>
{
    await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    return Results.Redirect("/");
});

Accessing claims

After authentication, read claims from the ClaimsPrincipal:

app.MapGet("/", (HttpContext context) =>
{
    if (context.User.Identity?.IsAuthenticated == true)
    {
        var email = context.User.FindFirst("email")?.Value ?? "unknown";
        var hwVerified = context.User.FindFirst("hardware_verified")?.Value == "true";
        // Use email and hwVerified as needed
    }
});

Rich Authorization Requests

To request structured permissions beyond scopes, add authorization_details as an extra parameter in the redirect event:

options.Events = new OpenIdConnectEvents
{
    OnRedirectToIdentityProvider = context =>
    {
        context.ProtocolMessage.SetParameter(
            "authorization_details",
            """[{"type":"account_access","actions":["read","transfer"]}]""");
        return Task.CompletedTask;
    },
};

See the Rich Authorization Requests section for the full authorization_details format.