diff --git a/configuration/authorization-storage.html b/configuration/authorization-storage.html new file mode 100644 index 0000000..8a70b2d --- /dev/null +++ b/configuration/authorization-storage.html @@ -0,0 +1,211 @@ + + + + + + + + Authorization storage + + + + + + + + + + + + + + + +
+
+ +
+
+
+ + + + +
+
+
+
+ +
+
+
+
+
+ +
+
+
    +
  • +
+
+
+
+
+ +
+ 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("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.

+
+
+
+ +
+
+
+
    +
  • + Improve this Doc +
  • +
+
+
+
In This Article
+
+
+
+
+
+
+ +
+
+
+
+ + Back to top + + + Generated by DocFX +
+
+
+
+ + + + + + diff --git a/configuration/index.html b/configuration/index.html index 977ea97..288c510 100644 --- a/configuration/index.html +++ b/configuration/index.html @@ -68,24 +68,8 @@

Configuration and settings

-
-
-
-
-

Application permissions

-

Learn how to leverage permissions to control the OIDC features a client is allowed to use.

-
-
-
-
-
-
-

Token formats

-

Learn more about the token formats supported by OpenIddict.

-
-
-
-
+

OpenIddict 3.0 comes with sensible defaults, but depending on the scenarios, the default settings can be amended to change how OpenIddict reacts to requests.

+
diff --git a/configuration/toc.html b/configuration/toc.html index 94375d5..2f709e6 100644 --- a/configuration/toc.html +++ b/configuration/toc.html @@ -21,6 +21,9 @@
  • Token formats
  • +
  • + Authorization storage +
  • diff --git a/manifest.json b/manifest.json index 80568bc..c512a42 100644 --- a/manifest.json +++ b/manifest.json @@ -9855,13 +9855,25 @@ "is_incremental": false, "version": "" }, + { + "type": "Conceptual", + "source_relative_path": "configuration/authorization-storage.md", + "output": { + ".html": { + "relative_path": "configuration/authorization-storage.html", + "hash": "u/HsIJFZPle5GhsRMjBbeA==" + } + }, + "is_incremental": false, + "version": "" + }, { "type": "Conceptual", "source_relative_path": "configuration/index.md", "output": { ".html": { "relative_path": "configuration/index.html", - "hash": "ZzmqYlosjNAtCeCaemUZYA==" + "hash": "KWMGySGgB+i57JD64wSqtQ==" } }, "is_incremental": false, @@ -9873,7 +9885,7 @@ "output": { ".html": { "relative_path": "configuration/toc.html", - "hash": "3oBYysDtnhzswOqBOlhiEg==" + "hash": "xW+gHghpmAoEGBDFX0tdAQ==" } }, "is_incremental": false, @@ -10682,7 +10694,7 @@ "ConceptualDocumentProcessor": { "can_incremental": false, "incrementalPhase": "build", - "total_file_count": 7, + "total_file_count": 8, "skipped_file_count": 0 }, "ManagedReferenceDocumentProcessor": {