Show / Hide Table of Contents

# Authorization storage

To keep track of logical chains of tokens and user consents, OpenIddict supports storing authorizations (also known as "grants" in some OpenID Connect implementations) in the database.

## Types of authorizations

Authorizations can be of two types: permanent and ad-hoc.

### Permanent authorizations

Permanent authorizations are developer-defined authorizations created using the `IOpenIddictAuthorizationManager.CreateAsync()` API and explicitly attached to a `ClaimsPrincipal` using the OpenIddict-specific `principal.SetAuthorizationId()` extension method.

Such authorizations are typically used to remember user consents and avoid displaying a consent view for each authorization request. For that, a "consent type" can be defined per-application, as in the following example:

    // Retrieve the application details from the database.
    var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ??
        throw new InvalidOperationException("The application cannot be found.");

    // Retrieve the permanent authorizations associated with the user and the application.
    var authorizations = await _authorizationManager.FindAsync(
        subject: await _userManager.GetUserIdAsync(user),
        client : await _applicationManager.GetIdAsync(application),
        status : Statuses.Valid,
        type   : AuthorizationTypes.Permanent,
        scopes : request.GetScopes()).ToListAsync();

    switch (await _applicationManager.GetConsentTypeAsync(application))
    {
        // If the consent is external (e.g when authorizations are granted by a sysadmin),
        // immediately return an error if no authorization can be found in the database.
        case ConsentTypes.External when !authorizations.Any():
            return Forbid(
                authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
                properties: new AuthenticationProperties(new Dictionary<string, string>
                {
                    [OpenIddictServerAspNetCoreConstants.Properties.Error] =
                        Errors.ConsentRequired,
                    [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
                        "The logged in user is not allowed to access this client application."
                }));

        // If the consent is implicit or if an authorization was found,
        // return an authorization response without displaying the consent form.
        case ConsentTypes.Implicit:
        case ConsentTypes.External when authorizations.Any():
        case ConsentTypes.Explicit when authorizations.Any() &&
            !request.HasPrompt(Prompts.Consent):
            var principal = await _signInManager.CreateUserPrincipalAsync(user);

            // Note: in this sample, the granted scopes match the requested scope
            // but you may want to allow the user to uncheck specific scopes.
            // For that, simply restrict the list of scopes before calling SetScopes.
            principal.SetScopes(request.GetScopes());
            principal.SetResources(await _scopeManager.ListResourcesAsync(
                principal.GetScopes()).ToListAsync());

            // Automatically create a permanent authorization to avoid requiring explicit consent
            // for future authorization or token requests containing the same scopes.
            var authorization = authorizations.LastOrDefault();
            if (authorization is null)
            {
                authorization = await _authorizationManager.CreateAsync(
                    principal: principal,
                    subject  : await _userManager.GetUserIdAsync(user),
                    client   : await _applicationManager.GetIdAsync(application),
                    type     : AuthorizationTypes.Permanent,
                    scopes   : principal.GetScopes());
            }

            principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization));

            foreach (var claim in principal.Claims)
            {
                claim.SetDestinations(GetDestinations(claim, principal));
            }

            return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);

        // At this point, no authorization was found in the database and an error must be returned
        // if the client application specified prompt=none in the authorization request.
        case ConsentTypes.Explicit   when request.HasPrompt(Prompts.None):
        case ConsentTypes.Systematic when request.HasPrompt(Prompts.None):
            return Forbid(
                authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
                properties: new AuthenticationProperties(new Dictionary<string, string>
                {
                    [OpenIddictServerAspNetCoreConstants.Properties.Error] =
                        Errors.ConsentRequired,
                    [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
                        "Interactive user consent is required."
                }));

        // In every other case, render the consent form.
        default: return View(new AuthorizeViewModel
        {
            ApplicationName = await _applicationManager.GetLocalizedDisplayNameAsync(application),
            Scope = request.Scope
        });
    }

### Ad-hoc authorizations

Ad-hoc authorizations are automatically created by OpenIddict when a chain of tokens needs to be tracked for security reasons, but no explicit permanent authorization was attached by the developer to the `ClaimsPrincipal` used for the sign-in operation.

Such authorizations are typically created in the authorization code flow to link all the tokens associated with the original authorization code, so that they can be automatically revoked if the authorization code was redeemed multiple times (which may indicate a token leakage). In the same vein, ad-hoc authorizations are also created when a refresh token is returned during a resource owner password credentials grant request.

> [!INFO] When using the OpenIddict.Quartz integration, ad-hoc authorizations are automatically removed from the database after a short period of time (14 days by default). Unlike ad-hoc authorizations, permanent authorizations never removed from the database.