Table of Contents

Getting started

A complete authorization server in one Program.cs: an in-memory catalog of clients and scopes, one test user, a signing key, the interactive pages, and the protocol endpoints. It is the samples/MinimalServer project, and every block of code on this page is included from it — the solution builds it and a test boots it and asks it for its discovery document, so the code here is code that compiles and starts against this release.

Reference the ASP.NET Core package and, for the sign-in and consent pages, the UI package:

dotnet add package Fdyay.IdentityServer.AspNetCore
dotnet add package Fdyay.IdentityServer.UI
var builder = WebApplication.CreateBuilder(args);

// The interactive pages (sign-in, consent, sign-out, …) and the security headers they are served with.
builder.Services.AddRazorPages().AddIdentityServerUi();

// The cookie schemes the session and an external sign-in live in.
builder.Services.AddIdentityServerCookieAuthentication();

AddIdentityServerUi adds the Razor class library's pages — sign-in, consent, sign-out, device and backchannel approval — together with the Content-Security-Policy they must be served with. AddIdentityServerCookieAuthentication registers the cookie schemes a signed-in user's session and an external sign-in are kept in. A server that only issues tokens to machines still needs it: the authorize endpoint refuses to start without a scheme to sign users in with.

The server and what it knows about

var identityServer = builder.Services
    .AddIdentityServer(options =>
    {
        // Every option can come from configuration: the "IdentityServer" section of appsettings.json.
        builder.Configuration.GetSection("IdentityServer").Bind(options);

        // This sample signs with the key registered below instead of a managed one.
        options.KeyManagement.Enabled = false;
    })
    .AddInMemoryIdentityResources(
    [
        new IdentityResources.OpenId(),
        new IdentityResources.Profile(),
    ])
    .AddInMemoryApiScopes(
    [
        new ApiScope("api", "Sample API"),
    ])
    .AddInMemoryClients(
    [
        // A web application signing users in: authorization code with PKCE.
        new Client
        {
            ClientId = "web",
            ClientSecrets = { new Secret("web-secret".Sha256()) },
            AllowedGrantTypes = { OidcConstants.GrantTypes.AuthorizationCode },
            RedirectUris = { "https://localhost:5002/signin-oidc" },
            PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },
            AllowedScopes = { "openid", "profile", "api" },
        },

        // A service calling the API as itself: client credentials.
        new Client
        {
            ClientId = "service",
            ClientSecrets = { new Secret("service-secret".Sha256()) },
            AllowedGrantTypes = { OidcConstants.GrantTypes.ClientCredentials },
            AllowedScopes = { "api" },
        },
    ])
    .AddTestUsers(
    [
        new TestUser
        {
            SubjectId = "1",
            Username = "alice",
            Password = "alice",
            Claims = { new Claim(JwtClaimTypes.Name, "Alice Smith") },
        },
    ]);

AddIdentityServer registers the server and returns an IIdentityServerBuilder the rest is chained onto:

  • Identity resources (IdentityResource) are what a client may learn about the user — openid for the subject id, profile for the name and the rest.
  • API scopes (ApiScope) are what a client may be allowed to do.
  • Clients (Client) are the applications allowed to ask: here a web application using the authorization code flow, which requires PKCE by default, and a service using client credentials.
  • Test users (TestUser) are what the bundled sign-in page checks a username and password against. Replace them with your own user store, or with ASP.NET Core Identity, before production.

Clients and resources covers each of these in more depth.

Options from configuration

The options lambda binds the IdentityServer section of configuration onto IdentityServerOptions, so any setting can be changed per environment without a rebuild. This is the sample's appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "IdentityServer": {
    "Authentication": {
      "CookieLifetime": "08:00:00",
      "CookieSlidingExpiration": true
    },
    "Events": {
      "RaiseFailureEvents": true,
      "RaiseErrorEvents": true
    }
  }
}

Every key, its type and its default is listed in the configuration reference.

The signing key

// A key made at startup, for development only: tokens it signed stop validating when the process restarts.
// Leave KeyManagement.Enabled on instead and the server creates, stores and rotates its own keys; or register
// a key you own here, from a certificate store or a vault.
var developmentKey = new RsaSecurityKey(RSA.Create(2048)) { KeyId = "minimal-server-development-key" };
identityServer.AddSigningCredential(developmentKey, SecurityAlgorithms.RsaSha256);

AddSigningCredential registers a key the host owns. With KeyManagement.Enabled left on instead — the default — the server creates its own signing key, keeps it in KeyManagement.KeyPath and rotates it on the schedule in KeyManagementOptions; persistence shows the keys kept in a database, which several instances need.

The pipeline

var app = builder.Build();

app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();   // the protocol endpoints: /.well-known/*, /connect/*
app.UseAuthorization();
app.MapRazorPages();       // the pages AddIdentityServerUi() brought in

app.Run();

UseIdentityServer serves the protocol endpoints at the host's root. It goes after UseAuthentication, so the endpoints see the signed-in user, and before UseAuthorization.

Run it

dotnet run --project samples/MinimalServer --urls https://localhost:5001
curl https://localhost:5001/.well-known/openid-configuration
curl -d grant_type=client_credentials -d client_id=service -d client_secret=service-secret -d scope=api \
  https://localhost:5001/connect/token

The client libraries sample is a web application that signs in against this server as its web client.