// Sign in with the EU Digital Identity Wallet - ASP.NET Core 8 (Program.cs) // CodeB acts as the verifier: it asks the user's wallet for a presentation, // verifies it, and hands your app a signed OpenID Connect id_token. // // dotnet new web -n WalletSignIn && cd WalletSignIn // dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect --version 8.0.* // (replace Program.cs with this file) // CODEB_CLIENT_ID=... CODEB_CLIENT_SECRET=... dotnet run // // Register https:///callback as redirect URI for your client first. using System.Security.Claims; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; var walletAcrs = new[] { "urn:codeb:acr:eudi-wallet", "urn:codeb:acr:eudi-wallet-linked" }; string Env(string k, string d) => Environment.GetEnvironmentVariable(k) is { Length: > 0 } v ? v : d; var builder = WebApplication.CreateBuilder(args); builder.Services.AddAuthentication(o => { o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; o.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie() .AddOpenIdConnect(o => { o.Authority = Env("CODEB_ISSUER", "https://phone.aloaha.com"); o.ClientId = Env("CODEB_CLIENT_ID", ""); o.ClientSecret = Env("CODEB_CLIENT_SECRET", ""); o.CallbackPath = "/callback"; o.ResponseType = "code"; // authorization code flow, PKCE is on by default o.Scope.Clear(); o.Scope.Add("openid"); o.Scope.Add("profile"); o.Scope.Add("email"); o.MapInboundClaims = false; // keep the original claim names (sub, acr) o.ClaimActions.Remove("acr"); // the handler drops acr from the sign-in cookie by default; keep it o.Events = new OpenIdConnectEvents { // straight to the wallet sign-in OnRedirectToIdentityProvider = ctx => { ctx.ProtocolMessage.AcrValues = walletAcrs[0]; return Task.CompletedTask; }, // the id_token is already validated (signature, issuer, audience, nonce, expiry); now require the wallet acr OnTokenValidated = ctx => { var acr = ctx.Principal?.FindFirst("acr")?.Value; if (acr is null || Array.IndexOf(walletAcrs, acr) < 0) { ctx.HandleResponse(); ctx.Response.StatusCode = 403; return ctx.Response.WriteAsync("This page needs a sign-in with the EU Digital Identity Wallet (acr=" + acr + ")."); } return Task.CompletedTask; }, }; }); builder.Services.AddAuthorization(); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); app.MapGet("/", (ClaimsPrincipal user) => user.Identity?.IsAuthenticated == true ? Results.Text("Signed in as " + user.FindFirst("sub")?.Value + " (acr=" + user.FindFirst("acr")?.Value + ")") : Results.Content("Sign in with your EU Digital Identity Wallet", "text/html")); app.MapGet("/login", () => Results.Challenge(new AuthenticationProperties { RedirectUri = "/" }, new[] { OpenIdConnectDefaults.AuthenticationScheme })); app.Run();