File: Program.cs
Web Access
Project: src\src\Identity\samples\IdentitySample.PasskeyUI\IdentitySample.PasskeyUI.csproj (IdentitySample.PasskeyUI)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Text.Json;
using IdentitySample.PasskeyUI;
using IdentitySample.PasskeyUI.Components;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.Test;
using Microsoft.AspNetCore.Mvc;
 
var builder = WebApplication.CreateBuilder(args);
 
builder.Services.AddRazorComponents();
builder.Services.AddCascadingAuthenticationState();
 
builder.Services.AddAuthentication(options =>
    {
        options.DefaultScheme = IdentityConstants.ApplicationScheme;
        options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
    })
    .AddIdentityCookies();
 
builder.Services.ConfigureApplicationCookie(options =>
{
    options.Events.OnRedirectToLogin = options =>
    {
        options.HttpContext.Response.Redirect("/");
        return Task.CompletedTask;
    };
});
 
builder.Services.AddAuthorization();
 
builder.Services.AddIdentityCore<PocoUser>()
    .AddSignInManager()
    .AddDefaultTokenProviders();
 
builder.Services.AddSingleton<IUserStore<PocoUser>, InMemoryUserStore<PocoUser>>();
builder.Services.AddSingleton<IUserPasskeyStore<PocoUser>, InMemoryUserStore<PocoUser>>();
 
var app = builder.Build();
 
app.UseHttpsRedirection();
app.UseAntiforgery();
app.MapStaticAssets();
app.MapRazorComponents<App>();
 
app.MapPost("attestation/options", async (
    [FromServices] UserManager<PocoUser> userManager,
    [FromServices] SignInManager<PocoUser> signInManager,
    [FromBody] PublicKeyCredentialCreationOptionsRequest request) =>
{
    var userId = (await userManager.FindByNameAsync(request.Username) ?? new PocoUser()).Id;
    var userEntity = new PasskeyUserEntity(userId, request.Username, null);
    var creationArgs = new PasskeyCreationArgs(userEntity)
    {
        AuthenticatorSelection = request.AuthenticatorSelection,
        Extensions = request.Extensions,
    };
 
    if (!string.IsNullOrEmpty(request.Attestation))
    {
        creationArgs.Attestation = request.Attestation;
    }
 
    var options = await signInManager.ConfigurePasskeyCreationOptionsAsync(creationArgs);
    return Results.Content(options.AsJson(), contentType: "application/json");
});
 
app.MapPost("assertion/options", async (
    [FromServices] UserManager<PocoUser> userManager,
    [FromServices] SignInManager<PocoUser> signInManager,
    [FromBody] PublicKeyCredentialGetOptionsRequest request) =>
{
    var user = !string.IsNullOrEmpty(request.Username)
        ? await userManager.FindByNameAsync(request.Username)
        : null;
 
    var requestArgs = new PasskeyRequestArgs<PocoUser>
    {
        User = user,
        Extensions = request.Extensions,
    };
 
    if (!string.IsNullOrEmpty(request.UserVerification))
    {
        requestArgs.UserVerification = request.UserVerification;
    }
 
    var options = await signInManager.ConfigurePasskeyRequestOptionsAsync(requestArgs);
    return Results.Content(options.AsJson(), contentType: "application/json");
});
 
app.MapPost("account/logout", async (
    [FromServices] SignInManager<PocoUser> signInManager) =>
{
    await signInManager.SignOutAsync();
    return TypedResults.LocalRedirect($"~/");
});
 
app.Run();
 
sealed class PublicKeyCredentialCreationOptionsRequest(string username)
{
    public string Username { get; } = username;
    public AuthenticatorSelectionCriteria? AuthenticatorSelection { get; set; }
    public JsonElement? Extensions { get; set; }
    public string? Attestation { get; set; } = "none";
}
 
sealed class PublicKeyCredentialGetOptionsRequest
{
    public string? Username { get; set; }
    public string? UserVerification { get; set; }
    public JsonElement? Extensions { get; set; }
}