### Add ClientClaim Example Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/models.md Example of how to add a custom claim to a client. This claim will be issued with the client in tokens. ```csharp client.Claims.Add(new ClientClaim("client_tier", "premium")); ``` -------------------------------- ### Common IdentityServer4 Configuration Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/configuration.md Example of common IdentityServer4 setup, including enabling events and adding in-memory stores for clients, resources, and users. ```csharp services.AddIdentityServer(options => { options.Events.RaiseSuccessEvents = true; options.Events.RaiseFailureEvents = true; options.Events.RaiseErrorEvents = true; }) .AddInMemoryClients(clients) .AddInMemoryIdentityResources(identityResources) .AddInMemoryApiResources(apiResources) .AddTestUsers(testUsers); ``` -------------------------------- ### Custom Client Store Implementation Example Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/stores.md An example of a custom implementation of the IClientStore interface, demonstrating how to fetch client data from an external API. Includes registration of the custom store. ```csharp public class CustomClientStore : IClientStore { private readonly IHttpClientFactory _httpFactory; public async Task FindClientByIdAsync(string clientId) { // Load from database, cache, remote API, etc. using (var client = _httpFactory.CreateClient()) { var response = await client.GetAsync($"https://config-service/clients/{clientId}"); if (!response.IsSuccessStatusCode) return null; var json = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject(json); } } } // Register services.AddIdentityServer() .AddClientStore(); ``` -------------------------------- ### User Info Endpoint Response Example Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/endpoints.md This is an example of a successful response from the User Info endpoint, detailing the claims returned for an authenticated user. ```json { "sub": "user123", "name": "John Doe", "given_name": "John", "family_name": "Doe", "email": "john@example.com", "email_verified": true, "picture": "https://example.com/photo.jpg", "profile": "https://example.com/user/john" } ``` -------------------------------- ### IdentityServerUser Usage Example Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/identity-server-user.md Demonstrates creating an IdentityServerUser, setting its properties and additional claims, and then creating a ClaimsPrincipal for authentication. ```csharp using IdentityServer4; using System.Security.Claims; // Create a user identity var user = new IdentityServerUser("user123") { DisplayName = "John Doe", IdentityProvider = "Google", AuthenticationTime = DateTime.UtcNow, AuthenticationMethods = new[] { "external" } }; // Add custom claims user.AdditionalClaims.Add(new Claim("email", "john@example.com")); user.AdditionalClaims.Add(new Claim("role", "admin")); // Create the claims principal for authentication var principal = user.CreatePrincipal(); // Sign in the user await HttpContext.SignInAsync( IdentityServerConstants.DefaultCookieAuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = true } ); ``` -------------------------------- ### Minimal IdentityServer4 Startup Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/README.md Demonstrates the basic configuration required to start IdentityServer4 with in-memory stores for clients, resources, and users. ```csharp services.AddIdentityServer() .AddInMemoryClients(GetClients()) .AddInMemoryIdentityResources(GetIdentityResources()) .AddInMemoryApiResources(GetApiResources()) .AddTestUsers(GetUsers()); app.UseIdentityServer(); ``` -------------------------------- ### Register IdentityServer with Default Implementations Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/services.md Example of registering IdentityServer with default profile and event sink services. ```csharp // Using default implementations services.AddIdentityServer() .AddProfileService() .AddEventSink(); ``` -------------------------------- ### Custom Profile Service Implementation Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/services.md Example implementation of IProfileService to fetch user claims and check user activity. Register this service in DI. ```csharp public class CustomProfileService : IProfileService { private readonly UserManager _userManager; public async Task GetProfileDataAsync(ProfileDataRequestContext context) { var user = await _userManager.FindByIdAsync(context.Subject.GetSubjectId()); if (user == null) return; // Add requested claims if (context.RequestedClaimTypes.Contains("email")) context.IssuedClaims.Add(new Claim("email", user.Email)); if (context.RequestedClaimTypes.Contains("name")) context.IssuedClaims.Add(new Claim("name", user.UserName)); // Add custom claims based on caller if (context.Caller == IdentityServerConstants.ProfileDataCallers.UserInfoEndpoint) { context.IssuedClaims.Add(new Claim("phone", user.PhoneNumber)); } } public async Task IsActiveAsync(IsActiveContext context) { var user = await _userManager.FindByIdAsync(context.Subject.GetSubjectId()); context.IsActive = user != null && !user.IsDeactivated; } } // Register in DI services.AddIdentityServer() .AddProfileService(); ``` -------------------------------- ### Custom Grant Validator Implementation Example Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/validators.md An example implementation of IExtensionGrantValidator for a custom delegation grant type. It demonstrates how to access request parameters, validate a token, and set the validation result. ```csharp public class CustomGrantValidator : IExtensionGrantValidator { public string GrantType => "urn:custom:delegation"; public Task ValidateAsync(ExtensionGrantValidationContext context) { var token = context.Request.Raw["token"]; if (ValidateToken(token)) { var subjectId = ExtractSubjectFromToken(token); context.Result = new GrantValidationResult( subject: subjectId, authenticationMethod: "delegation" ); } else { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant); } return Task.CompletedTask; } private bool ValidateToken(string token) { // Custom token validation logic return !string.IsNullOrEmpty(token); } private string ExtractSubjectFromToken(string token) { // Extract subject from custom token return "user123"; } } // Register services.AddIdentityServer() .AddExtensionGrantValidator(); ``` -------------------------------- ### Register IdentityServer with Custom Cache Implementation Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/services.md Example of registering IdentityServer with a custom cache implementation for clients. ```csharp // Custom cache implementation services.AddIdentityServer() .AddCache(); ``` -------------------------------- ### Create and Add a Client Secret Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/models.md Example of creating a new Secret object for a client, specifying its value, type, description, and expiration. ```csharp var secret = new Secret { Value = "SharedSecretValue".Sha256(), Type = IdentityServerConstants.SecretTypes.SharedSecret, Description = "Production shared secret", Expiration = DateTime.UtcNow.AddYears(1) }; client.ClientSecrets.Add(secret); ``` -------------------------------- ### Token Introspection Endpoint Response Example Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/endpoints.md This is an example of a successful response from the Token Introspection endpoint, showing metadata about a token. ```json { "active": true, "scope": "openid profile", "client_id": "client1", "username": "john", "token_type": "Bearer", "exp": 1609459200, "iat": 1609455600, "sub": "user123", "aud": "aud1" } ``` -------------------------------- ### Register IdentityServer with In-Memory Stores Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/services.md Example of registering IdentityServer with in-memory stores for clients, identity resources, and API resources. ```csharp // Using in-memory stores services.AddIdentityServer() .AddInMemoryClients(GetClients()) .AddInMemoryIdentityResources(GetIdentityResources()) .AddInMemoryApiResources(GetApiResources()); ``` -------------------------------- ### Raise UserLoginSuccess Event Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/events.md Example of raising an event when a user successfully logs in. This event includes user identification and the authentication provider used. ```csharp var evt = new UserLoginSuccessEvent( subjectId: user.Id, displayName: user.UserName, provider: "Google", providerUserId: googleId ); await _events.RaiseAsync(evt); ``` -------------------------------- ### Register IdentityServer with EntityFramework Operational Store Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/services.md Example of registering IdentityServer to use EntityFramework for persisted grants. ```csharp // Using EntityFramework for persisted grants services.AddIdentityServer() .AddOperationalStore(); ``` -------------------------------- ### Consent Controller Example for IIdentityServerInteractionService Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/services.md Demonstrates how to use IIdentityServerInteractionService to retrieve authorization context, display consent, and handle user consent responses within a controller. ```csharp public class ConsentController : Controller { private readonly IIdentityServerInteractionService _interaction; public async Task Index(string returnUrl) { var context = await _interaction.GetAuthorizationContextAsync(returnUrl); if (context == null) return BadRequest(); var vm = new ConsentViewModel { ClientName = context.ClientName, ClientLogoUrl = context.ClientLogoUrl, IdentityScopes = context.IdentityScopes, ResourceScopes = context.ResourceScopes }; return View(vm); } [HttpPost] public async Task Index(ConsentInputModel model, string returnUrl) { var context = await _interaction.GetAuthorizationContextAsync(returnUrl); if (model.ScopesConsented.Count() == 0) { await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied); } else { var consent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesConsented = model.ScopesConsented }; await _interaction.GrantConsentAsync(context, consent); } return Redirect(returnUrl); } } ``` -------------------------------- ### Custom Client Store Implementation Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/stores.md Provides a custom implementation for IClientStore that fetches client configurations from an external API using HttpClient. This example demonstrates dependency injection for IHttpClientFactory and ILogger. ```csharp public class CustomClientStore : IClientStore { private readonly IHttpClientFactory _httpFactory; private readonly ILogger _logger; public CustomClientStore(IHttpClientFactory httpFactory, ILogger logger) { _httpFactory = httpFactory; _logger = logger; } public async Task FindClientByIdAsync(string clientId) { try { using (var client = _httpFactory.CreateClient()) { var response = await client.GetAsync($"https://config-api/clients/{clientId}"); if (!response.IsSuccessStatusCode) { _logger.LogWarning($"Client {clientId} not found"); return null; } var json = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject(json); } } catch (Exception ex) { _logger.LogError(ex, "Error loading client"); return null; } } } ``` ```csharp // Register services.AddIdentityServer() .AddClientStore(); ``` -------------------------------- ### RaiseAsync Event Logging Example Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/services.md Demonstrates how to use the RaiseAsync method to log custom events or errors within your application logic. Ensure an implementation of IEventService is injected. ```csharp public class MyCustomLogic { private readonly IEventService _events; public MyCustomLogic(IEventService events) { _events = events; } public async Task ProcessAsync() { try { // Do work... var evt = new Event { EventType = EventTypes.User, EventCategory = EventCategories.Authentication, Name = "Custom Event" }; await _events.RaiseAsync(evt); } catch (Exception ex) { await _events.RaiseAsync(new Event { EventType = EventTypes.Error, Name = "Custom Error", Message = ex.Message }); } } } ``` -------------------------------- ### Custom Identity Resource Example Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/models.md Create a custom IdentityResource to define specific user claims beyond the built-in ones. Ensure the Name, DisplayName, and UserClaims are set appropriately. ```csharp var resource = new IdentityResource { Name = "custom", DisplayName = "Custom Claims", UserClaims = { "employee_id", "department" } }; ``` -------------------------------- ### OpenID Connect Discovery Document Example Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/endpoints.md This JSON response provides the OpenID Connect discovery document, detailing endpoint URLs and capabilities of the IdentityServer. It is typically retrieved from the /.well-known/openid-configuration endpoint. ```json { "issuer": "https://auth.example.com", "authorization_endpoint": "https://auth.example.com/connect/authorize", "token_endpoint": "https://auth.example.com/connect/token", "userinfo_endpoint": "https://auth.example.com/connect/userinfo", "end_session_endpoint": "https://auth.example.com/connect/endsession", "jwks_uri": "https://auth.example.com/.well-known/openid-configuration/jwks", "scopes_supported": ["openid", "profile", "email", "api"], "response_types_supported": ["code", "token", "id_token", "code id_token"], "grant_types_supported": ["authorization_code", "client_credentials", "refresh_token"] } ``` -------------------------------- ### Raise UserLoginFailure Event Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/events.md Example of raising an event when user authentication fails. This event captures the username, the reason for failure, and the authentication provider. ```csharp var evt = new UserLoginFailureEvent( username: model.Username, error: "invalid_password", provider: "local" ); await _events.RaiseAsync(evt); ``` -------------------------------- ### Store or Update a Persisted Grant Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/stores.md This example demonstrates how to create and store a new persisted grant, such as a refresh token. Ensure all required properties like Key, Type, SubjectId, ClientId, CreationTime, and Expiration are set. ```csharp var grant = new PersistedGrant { Key = Guid.NewGuid().ToString(), Type = IdentityServerConstants.PersistedGrantTypes.RefreshToken, SubjectId = user.Id, ClientId = client.ClientId, CreationTime = DateTime.UtcNow, Expiration = DateTime.UtcNow.AddDays(30), Data = JsonConvert.SerializeObject(tokenData) }; await _grantStore.StoreAsync(grant); ``` -------------------------------- ### API Resource Usage Example Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/models.md Define an ApiResource to represent an OAuth 2.0 API scope. This includes setting the Name, DisplayName, Description, associated Scopes, and UserClaims. ```csharp var apiResource = new ApiResource { Name = "api1", DisplayName = "Order API", Description = "Access to order management endpoints", Scopes = { new ApiScope("api1.read", "Read access"), new ApiScope("api1.write", "Write access") }, UserClaims = { "role", "department" } }; ``` -------------------------------- ### Example Invalid Client Error Response Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/errors.md Shows a common JSON response for an 'invalid_client' error, typically indicating authentication issues. ```json { "error": "invalid_client", "error_description": "Client authentication failed" } ``` -------------------------------- ### Example Unauthorized Client Error Response Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/errors.md Displays a JSON response for an 'unauthorized_client' error, indicating the client is not permitted to use the requested flow. ```json { "error": "unauthorized_client", "error_description": "Client is not authorized to use implicit flow" } ``` -------------------------------- ### Custom Client Validator Implementation Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/validators.md Example of implementing IClientConfigurationValidator to enforce custom rules like naming conventions, logo URIs for public clients, and PKCE for SPA clients. Register this custom validator using AddClientConfigurationValidator. ```csharp public class CustomClientValidator : IClientConfigurationValidator { public async Task ValidateAsync(ClientConfigurationValidationContext context) { var client = context.Client; // Enforce naming convention if (!client.ClientName.StartsWith("app_")) { context.Errors.Add("Client name must start with 'app_'"); } // Require logo for public clients if (!client.RequireClientSecret && string.IsNullOrEmpty(client.LogoUri)) { context.Errors.Add("Public clients must have a logo URI"); } // Enforce PKCE for SPA clients if (client.AllowedGrantTypes.Contains("code") && client.AllowedGrantTypes.Contains("implicit")) { if (!client.RequirePkce) { context.Errors.Add("SPA clients must require PKCE"); } } await Task.CompletedTask; } } // Register as default validator services.AddIdentityServer() .AddClientConfigurationValidator(); ``` -------------------------------- ### Example Invalid Target Error Response Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/errors.md Shows a JSON response for an 'invalid_target' error, indicating that the requested resource or API is not configured on the server. ```json { "error": "invalid_target", "error_description": "Resource 'api_not_found' is not configured" } ``` -------------------------------- ### Example Unsupported Grant Type Error Response Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/errors.md Shows a JSON response when an 'unsupported_grant_type' error occurs, meaning the server does not support the requested grant type. ```json { "error": "unsupported_grant_type", "error_description": "Grant type 'custom_grant' is not supported" } ``` -------------------------------- ### Example Invalid Request Error Response Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/errors.md Illustrates a typical JSON response when an 'invalid_request' error occurs, often due to missing parameters. ```json { "error": "invalid_request", "error_description": "Missing required parameter: client_id" } ``` -------------------------------- ### Device Authorization Endpoint Response Example Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/endpoints.md This JSON object represents a successful response from the Device Authorization Endpoint, providing codes and URIs for the device authorization flow. It includes details like device code, user code, verification URIs, and expiration information. ```json { "device_code": "ABCD1234", "user_code": "1234", "verification_uri": "https://auth.example.com/device", "verification_uri_complete": "https://auth.example.com/device?user_code=1234", "expires_in": 600, "interval": 5 } ``` -------------------------------- ### Custom Token Request Validation Example Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/validators.md An example implementation of ICustomTokenRequestValidator. This validator checks if a client is in maintenance or if specific claims are present for sensitive scopes. Register your custom validator using AddCustomTokenRequestValidator. ```csharp public class CustomTokenValidator : ICustomTokenRequestValidator { public async Task ValidateAsync(CustomTokenRequestValidationContext context) { var request = context.Result.ValidatedRequest; // Reject if client is in maintenance if (IsClientInMaintenance(request.Client.ClientId)) { context.Result.IsError = true; context.Result.Error = "server_unavailable"; context.Result.ErrorDescription = "Client is in maintenance"; return; } // Require additional claim for sensitive scopes if (request.ValidatedResources.RawScopeString.Contains("admin")) { var mfaClaim = request.Subject.FindFirst("amr"); if (mfaClaim?.Value != "mfa") { context.Result.IsError = true; context.Result.Error = "insufficient_security"; } } await Task.CompletedTask; } private bool IsClientInMaintenance(string clientId) { // Check maintenance status return false; } } // Register services.AddIdentityServer() .AddCustomTokenRequestValidator(); ``` -------------------------------- ### Custom Profile Service Implementation Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/README.md Shows how to implement a custom profile service to add custom claims to the identity token. This requires injecting UserManager and implementing IProfileService. ```csharp public class CustomProfileService : IProfileService { private readonly UserManager _userManager; public async Task GetProfileDataAsync(ProfileDataRequestContext context) { var user = await _userManager.FindByIdAsync(context.Subject.GetSubjectId()); var claims = new List { new Claim("email", user.Email), new Claim("name", user.FullName) }; context.IssuedClaims.AddRange(claims); } public async Task IsActiveAsync(IsActiveContext context) { var user = await _userManager.FindByIdAsync(context.Subject.GetSubjectId()); context.IsActive = user != null && !user.IsDeactivated; } } services.AddIdentityServer() .AddProfileService(); ``` -------------------------------- ### IUserSession.GetSessionIdAsync Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/services.md Gets the session ID for the user. Returns the session ID, or null. ```APIDOC ## IUserSession.GetSessionIdAsync ### Description Gets the session ID for the user. Returns the session ID, or null. ### Method Task ``` -------------------------------- ### IdentityServer4 Initialization Entry Point Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/configuration.md The primary method for configuring IdentityServer4. It returns an IIdentityServerBuilder for further fluent configuration. ```csharp services.AddIdentityServer(Action setupAction) ``` -------------------------------- ### Create a new Client configuration Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/models.md Use this snippet to define a new client application's configuration, specifying its ID, grant types, scopes, redirect URIs, and other security-related settings. ```csharp var client = new Client { ClientId = "spa_client", ClientName = "Single Page Application", AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, RequireClientSecret = false, AllowedScopes = { "openid", "profile", "api" }, RedirectUris = { "https://localhost:3000/callback" }, PostLogoutRedirectUris = { "https://localhost:3000" }, AllowOfflineAccess = true, AccessTokenLifetime = 3600, RefreshTokenExpiration = TokenExpiration.Sliding, AllowedCorsOrigins = { "https://localhost:3000" } }; ``` -------------------------------- ### IUserSession.GetUserAsync Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/services.md Gets the authenticated user from the current request context. Returns the authenticated user, or null if not authenticated. ```APIDOC ## IUserSession.GetUserAsync ### Description Gets the authenticated user from the current request context. Returns the authenticated user, or null if not authenticated. ### Method Task ``` -------------------------------- ### End Session Endpoint Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/endpoints.md Initiates logout and RP-initiated logout (OIDC session management). Supports both GET and POST requests. ```APIDOC ## GET /connect/endsession or POST /connect/endsession ### Description Initiates logout and RP-initiated logout (OIDC session management). ### Method GET, POST ### Endpoint `/connect/endsession` ### Parameters #### Query Parameters - **id_token_hint** (string) - Optional - Previously issued ID token (identifies user session) - **post_logout_redirect_uri** (string) - Optional - URI to redirect after logout (must be registered) - **state** (string) - Optional - Opaque value for CSRF protection - **logout_hint** (string) - Optional - Hint for logout identity ### Response - If `post_logout_redirect_uri` provided: HTTP 302 redirect to that URI with optional `state` - Otherwise: Logout confirmation page (user confirms logout) #### Status Codes - **302 Found** — Redirect to post-logout URI - **200 OK** — Logout page displayed - **400 Bad Request** — Invalid post_logout_redirect_uri - **404 Not Found** — Endpoint disabled ``` -------------------------------- ### Authorization Request Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/endpoints.md Initiates an OpenID Connect or OAuth 2.0 authorization request (interactive flow). Supports both GET and POST methods. ```APIDOC ## GET /connect/authorize or POST /connect/authorize ### Description Initiates an OpenID Connect or OAuth 2.0 authorization request (interactive flow). ### Method GET, POST ### Endpoint /connect/authorize ### Parameters #### Query Parameters - **client_id** (string) - Yes - The client identifier - **response_type** (string) - Yes - Space-separated: code, token, id_token (or combinations) - **redirect_uri** (string) - Yes - Client's registered redirect URI - **scope** (string) - Yes - Space-separated scopes (must include "openid" for OpenID Connect) - **state** (string) - Recommended - Opaque value to prevent CSRF; returned in redirect - **nonce** (string) - For ID token - Value to prevent replay; included in ID token - **response_mode** (string) - No - Response format: query, fragment, form_post - **display** (string) - No - UI display mode: page, popup, touch, wap - **prompt** (string) - No - Space-separated: none, login, consent, select_account - **max_age** (integer) - No - Maximum age of authentication in seconds - **login_hint** (string) - No - Hint for login identity - **acr_values** (string) - No - Authentication context class references - **ui_locales** (string) - No - Preferred language/locales (BCP 47) - **id_token_hint** (string) - No - Hint containing previously issued ID token - **code_challenge** (string) - For PKCE - Base64url-encoded challenge - **code_challenge_method** (string) - For PKCE - S256 (SHA256) or plain ### Response #### Success Response (302 Found) Redirect to `redirect_uri` with parameters in URL (query, fragment, or POST body per `response_mode`). - **code** (string) - When Authorization Code/Hybrid - Authorization code (exchange at token endpoint) - **access_token** (string) - When Implicit/Hybrid - Access token for API - **id_token** (string) - When OpenID Connect implicit/hybrid - Signed JWT containing user claims - **token_type** (string) - When token included - Always "Bearer" - **expires_in** (integer) - When token included - Lifetime of access token in seconds - **scope** (string) - When token included - Granted scopes - **state** (string) - If provided in request - Same state value from request - **nonce** (string) - In ID token - Same nonce from request (if provided) #### Error Response Redirect to `redirect_uri` with error parameters. - **error** (string) - Error code (invalid_request, unauthorized_client, access_denied, unsupported_response_type, server_error, etc.) - **error_description** (string) - Human-readable error description - **error_uri** (string) - URI with more information - **state** (string) - Same state value from request ### Status Codes - 302 Found — Redirect to login, consent, or client - 400 Bad Request — Invalid request parameters - 401 Unauthorized — Client not authenticated - 403 Forbidden — Client not authorized for requested scopes - 404 Not Found — Endpoint disabled ``` -------------------------------- ### Register In-Memory Clients, Resources, Scopes, and Users Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/configuration.md Use this snippet to register in-memory collections for clients, identity resources, API resources, API scopes, and test users. Ensure the `GetClients`, `GetIdentityResources`, `GetApiResources`, `GetApiScopes`, and `GetUsers` methods are implemented to provide the respective data. ```csharp services .AddIdentityServer() .AddInMemoryClients(GetClients()) .AddInMemoryIdentityResources(GetIdentityResources()) .AddInMemoryApiResources(GetApiResources()) .AddInMemoryApiScopes(GetApiScopes()) .AddTestUsers(GetUsers()); ``` -------------------------------- ### Retrieve and Display User Grants with IIdentityServerInteractionService Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/services.md Demonstrates fetching all user grants (consents) and iterating through them to display client names and scopes. ```csharp var grants = await _interaction.GetAllUserGrantsAsync(); foreach (var grant in grants) { Console.WriteLine($"Client: {grant.ClientName}"); Console.WriteLine($"Scopes: {string.Join(", ", grant.Scopes)}"); } ``` -------------------------------- ### Example Invalid Scope Error Response Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/errors.md Illustrates a JSON response for an 'invalid_scope' error, typically when a requested scope is not recognized or allowed for the client. ```json { "error": "invalid_scope", "error_description": "Requested scope 'admin' is not valid for this client" } ``` -------------------------------- ### PKCE (Proof Key for Code Exchange) Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/endpoints.md PKCE parameters to be included for public clients (mobile apps, SPAs) during authorization and token requests. ```APIDOC ## PKCE (Proof Key for Code Exchange) ### Authorization Endpoint **Method:** GET **Endpoint:** `/connect/authorize` **Query Parameters:** - **code_challenge** (string) - Required - The code challenge generated from the code verifier. - **code_challenge_method** (string) - Required - The method used to generate the code challenge (e.g., `S256`). ### Token Endpoint **Method:** POST **Endpoint:** `/connect/token` **Request Body:** - **code** (string) - Required - The authorization code received. - **code_verifier** (string) - Required - The original code verifier used to generate the code challenge. **Note:** The `code_challenge` is the base64url-encoded SHA-256 hash of `code_verifier`. ``` -------------------------------- ### Example Invalid Grant Error Response Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/errors.md Presents a typical JSON response for an 'invalid_grant' error, often related to expired or misused authorization codes. ```json { "error": "invalid_grant", "error_description": "Authorization code is invalid or has expired" } ``` -------------------------------- ### Scope Validation for Token Requests Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/errors.md Provides C# code demonstrating how to retrieve discovery document information and construct a token request with valid scopes. ```csharp var resourceRequest = new DiscoveryDocumentRequest { Address = "https://auth.example.com" }; var disco = await client.GetDiscoveryDocumentAsync(resourceRequest); // Only request scopes from discovery var tokenRequest = new ClientCredentialsTokenRequest { Address = disco.TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "api1" // Must be in disco.ScopesSupported }; ``` -------------------------------- ### PKCE Authorization Request Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/endpoints.md For public clients, include `code_challenge` and `code_challenge_method` in the authorization request to enable Proof Key for Code Exchange (PKCE). ```http GET /connect/authorize code_challenge=E9Mrozoa2owUednvlmQEz2WDn9z6O3swfWMAMR8_Yg code_challenge_method=S256 ``` -------------------------------- ### IDeviceFlowStore Interface Methods Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/stores.md Manages device flow codes, including finding, updating, and removing them. Used for handling the device authorization flow. ```csharp public interface IDeviceFlowStore { Task FindByDeviceCodeAsync(string deviceCode); Task FindByUserCodeAsync(string userCode); Task UpdateByUserCodeAsync(string userCode, DeviceCode data); Task RemoveByDeviceCodeAsync(string deviceCode); } ``` -------------------------------- ### Configure EntityFramework Stores Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/stores.md Configures IdentityServer to use EntityFramework for operational and configuration stores, backed by a SQL Server database. Ensure migrations are configured correctly. ```csharp services.AddIdentityServer() .AddOperationalStore(options => options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly))) .AddConfigurationStore(options => options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString, sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly))) .AddInMemoryPersistedGrantStore(); // Or database store ``` -------------------------------- ### Disable Specific IdentityServer Events Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/events.md Implement a custom `IEventSink` to selectively persist events. This example only persists security-relevant events (failure and error types), logging others to debug. ```csharp services.AddIdentityServer() .AddEventSink(); public class FilteredEventSink : IEventSink { private readonly IEventSink _inner; private readonly ILogger _logger; public FilteredEventSink(IEventSink inner, ILogger logger) { _inner = inner; _logger = logger; } public async Task PersistAsync(Event evt) { // Only persist security-relevant events if (evt.EventType == EventTypes.Failure || evt.EventType == EventTypes.Error) { await _inner.PersistAsync(evt); } else { _logger.LogDebug($"Ignoring event: {evt.Name}"); } } } ``` -------------------------------- ### SecurityKeyInfo Structure Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/models.md Represents information about a signing key, including the key material and the signing algorithm used. ```csharp public class SecurityKeyInfo { public SecurityKey Key { get; set; } public string Algorithm { get; set; } } ``` -------------------------------- ### ISigningCredentialStore Interface Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/stores.md Provides the interface for retrieving signing credentials used in token creation. Called by the token creation service to sign JWTs. ```csharp public interface ISigningCredentialStore { Task GetSigningCredentialsAsync(); } ``` -------------------------------- ### Predefined Identity Resources in C# Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/types.md Defines static classes for standard OpenID Connect identity resources, each mapping to specific claims. These are used to represent common user information scopes. ```csharp public static class IdentityResources { public class OpenId : IdentityResource { } // Contains: sub public class Profile : IdentityResource { } // Contains: name, family_name, given_name, etc. public class Email : IdentityResource { } // Contains: email, email_verified public class Phone : IdentityResource { } // Contains: phone_number, phone_number_verified public class Address : IdentityResource { } // Contains: address } ``` -------------------------------- ### Custom Authorize Request Validator Implementation Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/validators.md Example implementation of ICustomAuthorizeRequestValidator. This validator performs IP-based validation and enforces Multi-Factor Authentication (MFA) for sensitive clients. Register your custom validator using AddCustomAuthorizeRequestValidator. ```csharp public class CustomAuthorizeValidator : ICustomAuthorizeRequestValidator { public async Task ValidateAsync(CustomAuthorizeRequestValidationContext context) { var result = context.Result; // Additional IP-based validation if (!IsIpAllowed(result.Client.ClientId)) { result.IsError = true; result.Error = AuthorizationError.AccessDenied; result.ErrorDescription = "IP address not whitelisted"; return; } // Enforce MFA for sensitive clients if (result.Client.RequiresMfa && !IsUserMfaAuthenticated(result.Subject)) { result.IsError = true; result.Error = AuthorizationError.AccessDenied; result.ErrorDescription = "MFA required"; } await Task.CompletedTask; } private bool IsIpAllowed(string clientId) { return true; } private bool IsUserMfaAuthenticated(ClaimsPrincipal user) { return user.FindFirst("amr")?.Value == "mfa"; } } // Register services.AddIdentityServer() .AddCustomAuthorizeRequestValidator(); ``` -------------------------------- ### Define IConsentService Interface Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/services.md Defines the contract for managing user consent. Use this to retrieve or check consent requirements for a given client and scopes. ```csharp public interface IConsentService { Task GetConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable requestedScopes); Task RequiresConsentAsync(Client client, IEnumerable requestedScopes); } ``` -------------------------------- ### Custom Resource Owner Password Validator Implementation Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/validators.md Example implementation of IResourceOwnerPasswordValidator for validating user credentials against ASP.NET Core Identity. Requires UserManager and SignInManager. Ensure the validator is registered with the IdentityServer services. ```csharp public class CustomResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator { private readonly UserManager _userManager; private readonly SignInManager _signInManager; public CustomResourceOwnerPasswordValidator( UserManager userManager, SignInManager signInManager) { _userManager = userManager; _signInManager = signInManager; } public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { var user = await _userManager.FindByNameAsync(context.UserName); if (user != null && !user.IsDeactivated) { var result = await _signInManager.CheckPasswordSignInAsync(user, context.Password, false); if (result.Succeeded) { context.Result = new GrantValidationResult( subject: user.Id, authenticationMethod: "password", claims: GetClaimsForUser(user) ); } } if (context.Result.IsError) { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant); } } private IEnumerable GetClaimsForUser(ApplicationUser user) { return new[] { new Claim("sub", user.Id), new Claim("name", user.UserName) }; } } // Register services.AddIdentityServer() .AddResourceOwnerPasswordValidator(); ``` -------------------------------- ### IValidationKeysStore Interface Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/stores.md Provides the interface for retrieving public keys used for token validation. Used by the token validation middleware to verify token signatures. ```csharp public interface IValidationKeysStore { Task> GetValidationKeysAsync(); } ``` -------------------------------- ### Built-in Identity Resources Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/models.md Instantiate common built-in identity resources like OpenID, Profile, Email, Phone, and Address. These represent standard OpenID Connect scopes. ```csharp new IdentityResources.OpenId(), // Contains: sub new IdentityResources.Profile(), // Contains: name, family_name, given_name, ... new IdentityResources.Email(), // Contains: email, email_verified new IdentityResources.Phone(), // Contains: phone_number, phone_number_verified new IdentityResources.Address() // Contains: address ``` -------------------------------- ### Analyze Event Logs with LINQ Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/events.md This C# code demonstrates how to use LINQ to query event logs for specific patterns like failed logins, brute force attempts, and token errors. It requires an `eventLogs` collection to be available. ```csharp // Count failed logins per user var failedLogins = eventLogs .Where(e => e.Name == "User Login Failure") .GroupBy(e => e.Details["username"]) .Select(g => new { Username = g.Key, Count = g.Count() }) .Where(x => x.Count >= 5); // Find brute force attempts var suspiciousIps = eventLogs .Where(e => e.EventType == "Failure" && e.CreatedAt > DateTime.UtcNow.AddHours(-1)) .GroupBy(e => e.Details["ip_address"]) .Where(g => g.Count() > 10); // Track token issues var tokenErrors = eventLogs .Where(e => e.Name == "Token Issued Failure") .GroupBy(e => e.Details["error"]) .Select(g => new { Error = g.Key, Count = g.Count() }); ``` -------------------------------- ### Register Custom Profile, Password Validator, and Grant Validator Services Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/configuration.md Integrate custom services for profile management, resource owner password validation, and extension grant types. Ensure that `CustomProfileService`, `CustomPasswordValidator`, and `CustomGrantValidator` are correctly implemented and registered. ```csharp services .AddIdentityServer() .AddProfileService() .AddResourceOwnerPasswordValidator() .AddExtensionGrantValidator(); ``` -------------------------------- ### Client Model Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/models.md Represents an OAuth 2.0 or OpenID Connect client application. This section details the properties available for configuring a client, such as its ID, name, allowed grant types, redirect URIs, scopes, lifetimes, and secrets. ```APIDOC ## Client Model ### Description Represents an OAuth 2.0 or OpenID Connect client application. This section details the properties available for configuring a client, such as its ID, name, allowed grant types, redirect URIs, scopes, lifetimes, and secrets. ### Properties - **ClientId** (string) - Unique identifier for the client - **ClientName** (string) - Display name for the client - **Description** (string) - Description of client (for UI) - **ClientUri** (string) - Client's website URL - **LogoUri** (string) - Client's logo URL - **AllowedGrantTypes** (ICollection) - Enabled grant types (code, implicit, hybrid, client_credentials, password) - **RedirectUris** (ICollection) - Allowed redirect URIs after authorization - **FrontChannelLogoutSessionRequired** (bool) - Whether to require session param in logout - **PostLogoutRedirectUris** (ICollection) - Allowed URIs after logout - **AllowOfflineAccess** (bool) - Whether refresh tokens allowed - **AllowedScopes** (ICollection) - Scopes the client can request - **AlwaysIncludeUserClaimsInIdToken** (bool) - Always include user claims in ID token - **AccessTokenLifetime** (int) - Access token lifetime (seconds, default 3600) - **IdentityTokenLifetime** (int) - ID token lifetime (seconds, default 300) - **AuthorizationCodeLifetime** (int) - Auth code lifetime (seconds, default 300) - **RefreshTokenLifetime** (int) - Refresh token lifetime (seconds, default 2592000 = 30 days) - **RefreshTokenUsage** (TokenUsage) - Reuse (default) or OneTime - **RefreshTokenExpiration** (TokenExpiration) - Absolute (default) or Sliding - **RequireClientSecret** (bool) - Whether client_secret required (default true) - **RequirePkce** (bool) - Whether PKCE code_challenge required - **AllowPlainTextPkce** (bool) - Allow plain (unencrypted) PKCE - **RequireConsent** (bool) - Whether to show consent screen (default true) - **AllowRememberConsent** (bool) - Allow user to remember consent (default true) - **ClientSecrets** (ICollection) - Client secrets for authentication - **Claims** (ICollection) - Custom claims for the client - **Enabled** (bool) - Whether the client is enabled - **AllowAccessTokensViaBrowser** (bool) - Whether to return access token in browser - **Protocol** (string) - Protocol (oidc or wsfed) - **AccessTokenType** (AccessTokenType) - Jwt (default) or Reference - **IncludeJwtId** (bool) - Include jti claim in access token - **AllowedCorsOrigins** (ICollection) - CORS origins allowed ### Usage Example (C#) ```csharp var client = new Client { ClientId = "spa_client", ClientName = "Single Page Application", AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, RequireClientSecret = false, AllowedScopes = { "openid", "profile", "api" }, RedirectUris = { "https://localhost:3000/callback" }, PostLogoutRedirectUris = { "https://localhost:3000" }, AllowOfflineAccess = true, AccessTokenLifetime = 3600, RefreshTokenExpiration = TokenExpiration.Sliding, AllowedCorsOrigins = { "https://localhost:3000" } }; ``` ``` -------------------------------- ### Enable All IdentityServer Events Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/events.md Configure IdentityServer to raise all event types: success, failure, error, and information. Use a custom `IEventSink` to handle these events. ```csharp services.AddIdentityServer(options => { options.Events.RaiseSuccessEvents = true; options.Events.RaiseFailureEvents = true; options.Events.RaiseErrorEvents = true; options.Events.RaiseInformationEvents = true; }) .AddEventSink(); ``` -------------------------------- ### IProfileService Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/services.md Connects IdentityServer to user and profile data stores. Implementations are responsible for providing user claims and determining user activity. ```APIDOC ## IProfileService ### Description Connects IdentityServer to user and profile data stores. This interface is crucial for managing user claims and authentication status. ### Namespace `IdentityServer4.Services` ### Source `src/IdentityServer4/src/Services/IProfileService.cs` ### Methods #### GetProfileDataAsync Called whenever claims about the user are requested (token creation, userinfo endpoint). ##### Parameters - `context` (`ProfileDataRequestContext`) - Required - Request context with user, client, and requested claims. ##### Behavior The implementation must populate `context.IssuedClaims` with the claims to include in the token. Filter claims based on `context.RequestedClaimTypes` and the client's capabilities. ##### Usage Example: ```csharp public class CustomProfileService : IProfileService { private readonly UserManager _userManager; public async Task GetProfileDataAsync(ProfileDataRequestContext context) { var user = await _userManager.FindByIdAsync(context.Subject.GetSubjectId()); if (user == null) return; // Add requested claims if (context.RequestedClaimTypes.Contains("email")) context.IssuedClaims.Add(new Claim("email", user.Email)); if (context.RequestedClaimTypes.Contains("name")) context.IssuedClaims.Add(new Claim("name", user.UserName)); // Add custom claims based on caller if (context.Caller == IdentityServerConstants.ProfileDataCallers.UserInfoEndpoint) { context.IssuedClaims.Add(new Claim("phone", user.PhoneNumber)); } } public async Task IsActiveAsync(IsActiveContext context) { var user = await _userManager.FindByIdAsync(context.Subject.GetSubjectId()); context.IsActive = user != null && !user.IsDeactivated; } } // Register in DI services.AddIdentityServer() .AddProfileService(); ``` #### IsActiveAsync Called to check if user is valid and active (not deactivated, locked, etc.). ##### Parameters - `context` (`IsActiveContext`) - Required - Context with user and caller information. ##### Behavior Set `context.IsActive = true` to allow the token/request to proceed, or `false` to reject it. ``` -------------------------------- ### IHandleGenerationService Interface Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/services.md Defines a method for generating secure random handles for codes and tokens. ```csharp public interface IHandleGenerationService { string GenerateHandle(); } ``` -------------------------------- ### Find Identity Resources by Scope Name Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/stores.md Retrieves identity resources that match the provided scope names. Ensure you have the necessary scope names to query. ```csharp var identityResources = await _resourceStore.FindIdentityResourcesByScopeNameAsync( new[] { "openid", "profile", "email" } ); ``` -------------------------------- ### Query Persisted Grants Source: https://github.com/duendearchive/identityserver4/blob/main/_autodocs/api-reference/stores.md Find all grants for a user or specific grants like refresh tokens for a client. Requires the _grantStore to be initialized. ```csharp // Find all grants for a user var grants = await _grantStore.GetAllAsync(new PersistedGrantFilter { SubjectId = userId }); // Find refresh tokens for a specific client var refreshTokens = await _grantStore.GetAllAsync(new PersistedGrantFilter { ClientId = clientId, Type = IdentityServerConstants.PersistedGrantTypes.RefreshToken }); ```