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("Details concerning the calling client application cannot be found.");
+
+// Retrieve the permanent authorizations associated with the user and the calling client 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.
+