Claude
Skills
Sign in
Back

identityserver-ui-flows

Included with Lifetime
$97 forever

Guide for building login, logout, consent, error, and federation gateway UI pages in Duende IdentityServer, including IIdentityServerInteractionService usage, external provider integration, and Home Realm Discovery strategies.

Design

What this skill does


# IdentityServer UI Flows: Login, Logout, Consent, and Federation

## When to Use This Skill

- Building or customizing the login page (local credentials, MFA, passwordless)
- Integrating external identity providers (Google, Azure AD, SAML, etc.)
- Implementing the consent page for third-party client authorization
- Building the logout flow with session cleanup and client notifications
- Implementing a federation gateway with Home Realm Discovery (HRD)
- Handling and displaying error pages for protocol errors
- Using `IIdentityServerInteractionService` to interact with the protocol engine
- Redirecting users back to clients after login/logout

Docs: https://docs.duendesoftware.com/identityserver/ui

## Architecture Overview

IdentityServer separates the protocol engine from the user interface. The engine handles OAuth/OIDC endpoints and redirects to your UI pages as needed. Your UI code handles all user interaction and then communicates results back to the engine.

```
Browser → IdentityServer Middleware → UI Pages (Login, Consent, Logout, Error)
                                          ↕
                                   IIdentityServerInteractionService
                                          ↕
                                   IdentityServer Protocol Engine
```

### Required Pages

| Page    | Purpose                               | Default URL                               |
| ------- | ------------------------------------- | ----------------------------------------- |
| Login   | Establish authentication session      | Inferred from cookie handler `LoginPath`  |
| Logout  | Terminate session, notify clients     | Set via `opt.UserInteraction.LogoutUrl`   |
| Consent | Grant/deny client access to resources | `/consent`                                |
| Error   | Display protocol error information    | `/home/error`                             |

## Login Page

### Configuring the Login URL

```csharp
// Program.cs — explicit configuration
builder.Services.AddIdentityServer(opt => {
    opt.UserInteraction.LoginUrl = "/path/to/login";
});
```

If not set, IdentityServer infers the URL from the cookie handler's `LoginPath`:

```csharp
// Program.cs — with ASP.NET Identity
builder.Services.AddIdentityServer()
    .AddAspNetIdentity<ApplicationUser>();

builder.Services.ConfigureApplicationCookie(options =>
{
    options.LoginPath = "/path/to/login/for/aspnet_identity";
});
```

### Authorization Context

When IdentityServer redirects to the login page, it passes a `returnUrl` query parameter. Use `IIdentityServerInteractionService.GetAuthorizationContextAsync` to extract the original authorization request parameters:

```csharp
public class LoginModel : PageModel
{
    private readonly IIdentityServerInteractionService _interaction;

    public LoginModel(IIdentityServerInteractionService interaction)
    {
        _interaction = interaction;
    }

    public async Task<IActionResult> OnGet(string returnUrl)
    {
        var context = await _interaction.GetAuthorizationContextAsync(returnUrl);

        // context contains:
        // - Client (the requesting client)
        // - IdP (requested identity provider hint)
        // - AcrValues (requested authentication context)
        // - Tenant (requested tenant)
        // - LoginHint (suggested username)
        // - Parameters (raw protocol parameters)

        // Use context for branding, HRD, MFA decisions, etc.
    }
}
```

**Important**: Do not parse the `returnUrl` yourself. Always use the interaction service.

### Establishing the Authentication Session

After validating credentials, create the authentication session:

```csharp
var user = new IdentityServerUser("unique_id_for_your_user")
{
    DisplayName = "Bob Smith"
};

await HttpContext.SignInAsync(user);

// Redirect back to the authorization endpoint
return Redirect(returnUrl);
```

Or with explicit claims:

```csharp
var claims = new Claim[] {
    new Claim("sub", "unique_id_for_your_user"),
    new Claim("name", "Bob Smith"),
    new Claim("amr", "pwd"),
    new Claim("idp", "local")
};
var identity = new ClaimsIdentity(claims, "pwd");
var principal = new ClaimsPrincipal(identity);

await HttpContext.SignInAsync(principal);
```

### Well-Known Session Claims

| Claim       | Purpose                                                                   | Default                    |
| ----------- | ------------------------------------------------------------------------- | -------------------------- |
| `sub`       | **Required.** Unique user identifier. Must never change or be reassigned. | None — you must provide it |
| `name`      | Display name of the user                                                  | None                       |
| `amr`       | Authentication method reference                                           | `pwd`                      |
| `auth_time` | Time user entered credentials (epoch)                                     | Current time               |
| `idp`       | Identity provider scheme name                                             | `local`                    |
| `tenant`    | Tenant identifier                                                         | None                       |

### Protecting Against Open Redirects

Always validate the `returnUrl` before redirecting:

```csharp
// Option 1: Use ASP.NET Core helper
if (Url.IsLocalUrl(returnUrl))
{
    return Redirect(returnUrl);
}

// Option 2: Use IdentityServer interaction service
if (await _interaction.IsValidReturnUrl(returnUrl))
{
    return Redirect(returnUrl);
}
```

### Completing Login with CompleteLoginAsync

After establishing the authentication session, redirect the user back to the `returnUrl`. This causes the browser to re-issue the original authorize request, allowing IdentityServer to complete the protocol workflow.

## External Login (Federation)

### Registering External Providers

```csharp
// Program.cs
builder.Services.AddIdentityServer();

builder.Services.AddAuthentication()
    .AddOpenIdConnect("AAD", "Employee Login", options =>
    {
        options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
        // configure authority, client ID, etc.
    });
```

### Triggering External Authentication

```csharp
var callbackUrl = Url.Action("MyCallback");

var props = new AuthenticationProperties
{
    RedirectUri = callbackUrl,
    Items =
    {
        { "scheme", "AAD" },
        { "returnUrl", returnUrl }
    }
};

return Challenge("AAD", props);
```

### Handling the Callback

```csharp
// 1. Read external identity from temporary cookie
var result = await HttpContext.AuthenticateAsync(
    IdentityServerConstants.ExternalCookieAuthenticationScheme);

if (result?.Succeeded != true)
    throw new Exception("External authentication error");

var externalUser = result.Principal;
var userId = externalUser.FindFirst("sub").Value;
var scheme = result.Properties.Items["scheme"];
var returnUrl = result.Properties.Items["returnUrl"] ?? "~/";

// 2. Find or provision local user
var user = FindUserFromExternalProvider(scheme, userId);

// 3. Establish session
await HttpContext.SignInAsync(new IdentityServerUser(user.SubjectId)
{
    DisplayName = user.DisplayName,
    IdentityProvider = scheme
});

// 4. Clean up external cookie
await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);

// 5. Return to protocol processing
return Redirect(returnUrl);
```

### SignInScheme and SignOutScheme

| Scenario                 | SignInScheme                                                 | SignOutScheme                           |
| ------------------------ | ------------------------------------------------------------ | --------------------------------------- |
| Without ASP.NET Identity | `IdentityServerConstants.ExternalCookieAuthenticationScheme` | `IdentityServerConstants.SignoutScheme` |
| With ASP.NET Identity    | `IdentityServerConstants.ExternalCookieAuthenticationSche

Related in Design