diff --git a/src/OpenIddict.Abstractions/Descriptors/OpenIddictAuthorizationDescriptor.cs b/src/OpenIddict.Abstractions/Descriptors/OpenIddictAuthorizationDescriptor.cs
index 44cc3353..4dbf2ce1 100644
--- a/src/OpenIddict.Abstractions/Descriptors/OpenIddictAuthorizationDescriptor.cs
+++ b/src/OpenIddict.Abstractions/Descriptors/OpenIddictAuthorizationDescriptor.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Security.Claims;
namespace OpenIddict.Abstractions
{
@@ -14,10 +15,10 @@ namespace OpenIddict.Abstractions
public string ApplicationId { get; set; }
///
- /// Gets the claims associated with the authorization.
+ /// Gets or sets the optional principal associated with the authorization.
/// Note: this property is not stored by the default authorization stores.
///
- public IDictionary Claims { get; } = new Dictionary(StringComparer.Ordinal);
+ public ClaimsPrincipal Principal { get; set; }
///
/// Gets the scopes associated with the authorization.
diff --git a/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs b/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs
index 4ffa9861..6cbb1925 100644
--- a/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs
+++ b/src/OpenIddict.Abstractions/Managers/IOpenIddictAuthorizationManager.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.Linq;
+using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@@ -45,7 +46,7 @@ namespace OpenIddict.Abstractions
///
/// Creates a new permanent authorization based on the specified parameters.
///
- /// The claims associated with the authorization.
+ /// The principal associated with the authorization.
/// The subject associated with the authorization.
/// The client associated with the authorization.
/// The authorization type.
@@ -55,7 +56,7 @@ namespace OpenIddict.Abstractions
/// A that can be used to monitor the asynchronous operation, whose result returns the authorization.
///
ValueTask CreateAsync(
- [NotNull] ImmutableDictionary claims, [NotNull] string subject, [NotNull] string client,
+ [NotNull] ClaimsPrincipal principal, [NotNull] string subject, [NotNull] string client,
[NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken = default);
///
diff --git a/src/OpenIddict.Abstractions/OpenIddictConstants.cs b/src/OpenIddict.Abstractions/OpenIddictConstants.cs
index b64994f9..36f48b59 100644
--- a/src/OpenIddict.Abstractions/OpenIddictConstants.cs
+++ b/src/OpenIddict.Abstractions/OpenIddictConstants.cs
@@ -88,6 +88,7 @@ namespace OpenIddict.Abstractions
public static class Private
{
public const string AccessTokenLifetime = "oi_act_lft";
+ public const string AuthorizationId = "oi_au_id";
public const string AuthorizationCodeLifetime = "oi_auc_lft";
public const string ClaimDestinations = "oi_cl_dstn";
public const string CodeChallenge = "oi_cd_chlg";
@@ -97,6 +98,7 @@ namespace OpenIddict.Abstractions
public const string RedirectUri = "oi_reduri";
public const string RefreshTokenLifetime = "oi_reft_lft";
public const string Resource = "oi_rsrc";
+ public const string TokenId = "oi_tkn_id";
public const string TokenUsage = "oi_tkn_use";
}
}
diff --git a/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs b/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs
index f53c040d..fa21789e 100644
--- a/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs
+++ b/src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs
@@ -1246,11 +1246,41 @@ namespace OpenIddict.Abstractions
}
///
- /// Gets the unique identifier associated with the claims principal.
+ /// Gets the internal authorization identifier associated with the claims principal.
///
/// The claims principal.
/// The unique identifier or null if the claim cannot be found.
- public static string GetTokenId([NotNull] this ClaimsPrincipal principal)
+ public static string GetInternalAuthorizationId([NotNull] this ClaimsPrincipal principal)
+ {
+ if (principal == null)
+ {
+ throw new ArgumentNullException(nameof(principal));
+ }
+
+ return principal.GetClaim(Claims.Private.AuthorizationId);
+ }
+
+ ///
+ /// Gets the internal token identifier associated with the claims principal.
+ ///
+ /// The claims principal.
+ /// The unique identifier or null if the claim cannot be found.
+ public static string GetInternalTokenId([NotNull] this ClaimsPrincipal principal)
+ {
+ if (principal == null)
+ {
+ throw new ArgumentNullException(nameof(principal));
+ }
+
+ return principal.GetClaim(Claims.Private.TokenId);
+ }
+
+ ///
+ /// Gets the public token identifier associated with the claims principal.
+ ///
+ /// The claims principal.
+ /// The unique identifier or null if the claim cannot be found.
+ public static string GetPublicTokenId([NotNull] this ClaimsPrincipal principal)
{
if (principal == null)
{
@@ -1731,12 +1761,44 @@ namespace OpenIddict.Abstractions
}
///
- /// Sets the unique identifier associated with the claims principal.
+ /// Sets the internal authorization identifier associated with the claims principal.
+ ///
+ /// The claims principal.
+ /// The unique identifier to store.
+ /// The claims principal.
+ public static ClaimsPrincipal SetInternalAuthorizationId([NotNull] this ClaimsPrincipal principal, string identifier)
+ {
+ if (principal == null)
+ {
+ throw new ArgumentNullException(nameof(principal));
+ }
+
+ return principal.SetClaim(Claims.Private.AuthorizationId, identifier);
+ }
+
+ ///
+ /// Sets the internal token identifier associated with the claims principal.
+ ///
+ /// The claims principal.
+ /// The unique identifier to store.
+ /// The claims principal.
+ public static ClaimsPrincipal SetInternalTokenId([NotNull] this ClaimsPrincipal principal, string identifier)
+ {
+ if (principal == null)
+ {
+ throw new ArgumentNullException(nameof(principal));
+ }
+
+ return principal.SetClaim(Claims.Private.TokenId, identifier);
+ }
+
+ ///
+ /// Sets the public token identifier associated with the claims principal.
///
/// The claims principal.
/// The unique identifier to store.
/// The claims principal.
- public static ClaimsPrincipal SetTokenId([NotNull] this ClaimsPrincipal principal, string identifier)
+ public static ClaimsPrincipal SetPublicTokenId([NotNull] this ClaimsPrincipal principal, string identifier)
{
if (principal == null)
{
diff --git a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
index f20ef182..073d639e 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
@@ -10,6 +10,7 @@ using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.CompilerServices;
+using System.Security.Claims;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -165,7 +166,7 @@ namespace OpenIddict.Core
///
/// Creates a new permanent authorization based on the specified parameters.
///
- /// The claims associated with the authorization.
+ /// The principal associated with the authorization.
/// The subject associated with the authorization.
/// The client associated with the authorization.
/// The authorization type.
@@ -175,12 +176,12 @@ namespace OpenIddict.Core
/// A that can be used to monitor the asynchronous operation, whose result returns the authorization.
///
public virtual ValueTask CreateAsync(
- [NotNull] ImmutableDictionary claims, [NotNull] string subject,
- [NotNull] string client, [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken = default)
+ [NotNull] ClaimsPrincipal principal, [NotNull] string subject, [NotNull] string client,
+ [NotNull] string type, ImmutableArray scopes, CancellationToken cancellationToken = default)
{
- if (claims == null)
+ if (principal == null)
{
- throw new ArgumentNullException(nameof(claims));
+ throw new ArgumentNullException(nameof(principal));
}
if (string.IsNullOrEmpty(subject))
@@ -201,6 +202,7 @@ namespace OpenIddict.Core
var descriptor = new OpenIddictAuthorizationDescriptor
{
ApplicationId = client,
+ Principal = principal,
Status = OpenIddictConstants.Statuses.Valid,
Subject = subject,
Type = type
@@ -208,11 +210,6 @@ namespace OpenIddict.Core
descriptor.Scopes.UnionWith(scopes);
- foreach (var claim in claims)
- {
- descriptor.Claims.Add(claim);
- }
-
return CreateAsync(descriptor, cancellationToken);
}
@@ -1035,8 +1032,6 @@ namespace OpenIddict.Core
throw new ArgumentNullException(nameof(authorization));
}
- var builder = ImmutableArray.CreateBuilder();
-
var type = await Store.GetTypeAsync(authorization, cancellationToken);
if (string.IsNullOrEmpty(type))
{
@@ -1084,8 +1079,8 @@ namespace OpenIddict.Core
ValueTask IOpenIddictAuthorizationManager.CountAsync(Func, IQueryable> query, CancellationToken cancellationToken)
=> CountAsync(query, cancellationToken);
- async ValueTask IOpenIddictAuthorizationManager.CreateAsync(ImmutableDictionary claims, string subject, string client, string type, ImmutableArray scopes, CancellationToken cancellationToken)
- => await CreateAsync(claims, subject, client, type, scopes, cancellationToken);
+ async ValueTask IOpenIddictAuthorizationManager.CreateAsync(ClaimsPrincipal principal, string subject, string client, string type, ImmutableArray scopes, CancellationToken cancellationToken)
+ => await CreateAsync(principal, subject, client, type, scopes, cancellationToken);
async ValueTask IOpenIddictAuthorizationManager.CreateAsync(OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken)
=> await CreateAsync(descriptor, cancellationToken);
diff --git a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionConstants.cs b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionConstants.cs
index 0713e1ed..3b606019 100644
--- a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionConstants.cs
+++ b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionConstants.cs
@@ -18,6 +18,8 @@ namespace OpenIddict.Server.DataProtection
public const string DataProtector = ".data_protector";
public const string Expires = ".expires";
public const string IdentityTokenLifetime = ".identity_token_lifetime";
+ public const string InternalAuthorizationId = ".internal_authorization_id";
+ public const string InternalTokenId = ".internal_token_id";
public const string Issued = ".issued";
public const string Nonce = ".nonce";
public const string OriginalRedirectUri = ".original_redirect_uri";
diff --git a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlers.Serialization.cs b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlers.Serialization.cs
index a8963fd2..05866246 100644
--- a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlers.Serialization.cs
+++ b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlers.Serialization.cs
@@ -116,20 +116,31 @@ namespace OpenIddict.Server.DataProtection
SetProperty(properties, Properties.AccessTokenLifetime,
context.Principal.GetClaim(Claims.Private.AccessTokenLifetime));
+
SetProperty(properties, Properties.AuthorizationCodeLifetime,
context.Principal.GetClaim(Claims.Private.AuthorizationCodeLifetime));
+
SetProperty(properties, Properties.CodeChallenge,
context.Principal.GetClaim(Claims.Private.CodeChallenge));
+
SetProperty(properties, Properties.CodeChallengeMethod,
context.Principal.GetClaim(Claims.Private.CodeChallengeMethod));
+
SetProperty(properties, Properties.Expires,
context.Principal.GetExpirationDate()?.ToString("r", CultureInfo.InvariantCulture));
+
SetProperty(properties, Properties.IdentityTokenLifetime,
context.Principal.GetClaim(Claims.Private.IdentityTokenLifetime));
+
+ SetProperty(properties, Properties.InternalAuthorizationId, context.Principal.GetInternalAuthorizationId());
+ SetProperty(properties, Properties.InternalTokenId, context.Principal.GetInternalTokenId());
+
SetProperty(properties, Properties.Issued,
context.Principal.GetCreationDate()?.ToString("r", CultureInfo.InvariantCulture));
+
SetProperty(properties, Properties.OriginalRedirectUri,
context.Principal.GetClaim(Claims.Private.RedirectUri));
+
SetProperty(properties, Properties.RefreshTokenLifetime,
context.Principal.GetClaim(Claims.Private.RefreshTokenLifetime));
@@ -342,11 +353,13 @@ namespace OpenIddict.Server.DataProtection
.SetClaim(Claims.Private.AccessTokenLifetime, GetProperty(properties, Properties.AccessTokenLifetime))
.SetClaim(Claims.Private.AuthorizationCodeLifetime, GetProperty(properties, Properties.AuthorizationCodeLifetime))
+ .SetClaim(Claims.Private.AuthorizationId, GetProperty(properties, Properties.InternalAuthorizationId))
.SetClaim(Claims.Private.CodeChallenge, GetProperty(properties, Properties.CodeChallenge))
.SetClaim(Claims.Private.CodeChallengeMethod, GetProperty(properties, Properties.CodeChallengeMethod))
.SetClaim(Claims.Private.IdentityTokenLifetime, GetProperty(properties, Properties.IdentityTokenLifetime))
.SetClaim(Claims.Private.RedirectUri, GetProperty(properties, Properties.OriginalRedirectUri))
.SetClaim(Claims.Private.RefreshTokenLifetime, GetProperty(properties, Properties.RefreshTokenLifetime))
+ .SetClaim(Claims.Private.TokenId, GetProperty(properties, Properties.InternalTokenId))
// Note: since the data format relies on a data protector using different "purposes" strings
// per token type, the token processed at this stage is guaranteed to be of the expected type.
diff --git a/src/OpenIddict.Server/OpenIddictServerExtensions.cs b/src/OpenIddict.Server/OpenIddictServerExtensions.cs
index ce56f5ad..62ebed15 100644
--- a/src/OpenIddict.Server/OpenIddictServerExtensions.cs
+++ b/src/OpenIddict.Server/OpenIddictServerExtensions.cs
@@ -45,6 +45,7 @@ namespace Microsoft.Extensions.DependencyInjection
// Register the built-in filters used by the default OpenIddict server event handlers.
builder.Services.TryAddSingleton();
builder.Services.TryAddSingleton();
+ builder.Services.TryAddSingleton();
builder.Services.TryAddSingleton();
builder.Services.TryAddSingleton();
builder.Services.TryAddSingleton();
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs b/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs
index fcb5924d..a45f7491 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlerFilters.cs
@@ -47,6 +47,22 @@ namespace OpenIddict.Server
}
}
+ ///
+ /// Represents a filter that excludes the associated handlers if authorization storage was not enabled.
+ ///
+ public class RequireAuthorizationStorageEnabled : IOpenIddictServerHandlerFilter
+ {
+ public ValueTask IsActiveAsync([NotNull] BaseContext context)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ return new ValueTask(!context.Options.DisableAuthorizationStorage);
+ }
+ }
+
///
/// Represents a filter that excludes the associated handlers when no client identifier is received.
///
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs
index 402bf6a1..9b602216 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs
@@ -60,6 +60,7 @@ namespace OpenIddict.Server
ValidateRedirectUri.Descriptor,
ValidateCodeVerifier.Descriptor,
ValidateGrantedScopes.Descriptor,
+ ValidateAuthorization.Descriptor,
/*
* Token request handling:
@@ -1687,6 +1688,67 @@ namespace OpenIddict.Server
}
}
+ ///
+ /// Contains the logic responsible of rejecting token requests that use an authorization code
+ /// or refresh token whose associated authorization is no longer valid (e.g was revoked).
+ /// Note: this handler is not used when the degraded mode is enabled.
+ ///
+ public class ValidateAuthorization : IOpenIddictServerHandler
+ {
+ private readonly IOpenIddictAuthorizationManager _authorizationManager;
+
+ public ValidateAuthorization() => throw new InvalidOperationException(new StringBuilder()
+ .AppendLine("The core services must be registered when enabling the OpenIddict server feature.")
+ .Append("To register the OpenIddict core services, reference the 'OpenIddict.Core' package ")
+ .AppendLine("and call 'services.AddOpenIddict().AddCore()' from 'ConfigureServices'.")
+ .Append("Alternatively, you can disable the built-in database-based server features by enabling ")
+ .Append("the degraded mode with 'services.AddOpenIddict().AddServer().EnableDegradedMode()'.")
+ .ToString());
+
+ public ValidateAuthorization([NotNull] IOpenIddictAuthorizationManager authorizationManager)
+ => _authorizationManager = authorizationManager;
+
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictServerHandlerDescriptor Descriptor { get; }
+ = OpenIddictServerHandlerDescriptor.CreateBuilder()
+ .AddFilter()
+ .AddFilter()
+ .UseScopedHandler()
+ .SetOrder(ValidateGrantedScopes.Descriptor.Order + 1_000)
+ .Build();
+
+ public async ValueTask HandleAsync([NotNull] ValidateTokenRequestContext context)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ var identifier = context.Principal.GetInternalAuthorizationId();
+ if (string.IsNullOrEmpty(identifier))
+ {
+ return;
+ }
+
+ var authorization = await _authorizationManager.FindByIdAsync(identifier);
+ if (authorization == null || !await _authorizationManager.IsValidAsync(authorization))
+ {
+ context.Logger.LogError("The token '{Identifier}' was rejected because the associated " +
+ "authorization was no longer valid.", context.Principal.GetPublicTokenId());
+
+ context.Reject(
+ error: Errors.InvalidGrant,
+ description: context.Request.IsAuthorizationCodeGrantType() ?
+ "The authorization associated with the authorization code is no longer valid." :
+ "The authorization associated with the refresh token is no longer valid.");
+
+ return;
+ }
+ }
+ }
+
///
/// Contains the logic responsible of attaching the principal extracted
/// from the authorization code/refresh token to the event context.
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs
index c2dd1196..79d7df38 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs
@@ -48,6 +48,7 @@ namespace OpenIddict.Server
ValidateEndpointPermissions.Descriptor,
ValidateToken.Descriptor,
ValidateAuthorizedParty.Descriptor,
+ ValidateAuthorization.Descriptor,
/*
* Introspection request handling:
@@ -918,6 +919,65 @@ namespace OpenIddict.Server
}
}
+ ///
+ /// Contains the logic responsible of rejecting introspection requests that use
+ /// a token whose associated authorization is no longer valid (e.g was revoked).
+ /// Note: this handler is not used when the degraded mode is enabled.
+ ///
+ public class ValidateAuthorization : IOpenIddictServerHandler
+ {
+ private readonly IOpenIddictAuthorizationManager _authorizationManager;
+
+ public ValidateAuthorization() => throw new InvalidOperationException(new StringBuilder()
+ .AppendLine("The core services must be registered when enabling the OpenIddict server feature.")
+ .Append("To register the OpenIddict core services, reference the 'OpenIddict.Core' package ")
+ .AppendLine("and call 'services.AddOpenIddict().AddCore()' from 'ConfigureServices'.")
+ .Append("Alternatively, you can disable the built-in database-based server features by enabling ")
+ .Append("the degraded mode with 'services.AddOpenIddict().AddServer().EnableDegradedMode()'.")
+ .ToString());
+
+ public ValidateAuthorization([NotNull] IOpenIddictAuthorizationManager authorizationManager)
+ => _authorizationManager = authorizationManager;
+
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictServerHandlerDescriptor Descriptor { get; }
+ = OpenIddictServerHandlerDescriptor.CreateBuilder()
+ .AddFilter()
+ .AddFilter()
+ .UseScopedHandler()
+ .SetOrder(ValidateAuthorizedParty.Descriptor.Order + 1_000)
+ .Build();
+
+ public async ValueTask HandleAsync([NotNull] ValidateIntrospectionRequestContext context)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ var identifier = context.Principal.GetInternalAuthorizationId();
+ if (string.IsNullOrEmpty(identifier))
+ {
+ return;
+ }
+
+ var authorization = await _authorizationManager.FindByIdAsync(identifier);
+ if (authorization == null || !await _authorizationManager.IsValidAsync(authorization))
+ {
+ context.Logger.LogError("The token '{Identifier}' was rejected because the associated " +
+ "authorization was no longer valid.", context.Principal.GetPublicTokenId());
+
+ context.Reject(
+ error: Errors.InvalidGrant,
+ description: "The authorization associated with the token is no longer valid.");
+
+ return;
+ }
+ }
+ }
+
///
/// Contains the logic responsible of attaching the principal
/// extracted from the introspected token to the event context.
@@ -984,7 +1044,7 @@ namespace OpenIddict.Server
throw new ArgumentNullException(nameof(context));
}
- context.TokenId = context.Principal.GetTokenId();
+ context.TokenId = context.Principal.GetPublicTokenId();
context.TokenUsage = context.Principal.GetTokenUsage();
context.Subject = context.Principal.GetClaim(Claims.Subject);
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Serialization.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Serialization.cs
index 4977ee76..b27aff1c 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Serialization.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Serialization.cs
@@ -208,6 +208,8 @@ namespace OpenIddict.Server
{
context.Logger.LogTrace("The token '{Token}' could not be validated.", context.Token);
}
+
+ return default;
}
var assertion = ((JsonWebToken) result.SecurityToken)?.InnerToken ?? (JsonWebToken) result.SecurityToken;
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs
index fabd75b0..8b1bcade 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs
@@ -45,6 +45,7 @@ namespace OpenIddict.Server
AttachDefaultPresenters.Descriptor,
InferResources.Descriptor,
EvaluateReturnedTokens.Descriptor,
+ AttachAuthorization.Descriptor,
AttachAccessToken.Descriptor,
AttachAuthorizationCode.Descriptor,
AttachRefreshToken.Descriptor,
@@ -428,6 +429,117 @@ namespace OpenIddict.Server
}
}
+ ///
+ /// Contains the logic responsible of creating an ad-hoc authorization, if necessary.
+ /// Note: this handler is not used when the degraded mode is enabled.
+ ///
+ public class AttachAuthorization : IOpenIddictServerHandler
+ {
+ private readonly IOpenIddictApplicationManager _applicationManager;
+ private readonly IOpenIddictAuthorizationManager _authorizationManager;
+
+ public AttachAuthorization() => throw new InvalidOperationException(new StringBuilder()
+ .AppendLine("The core services must be registered when enabling the OpenIddict server feature.")
+ .Append("To register the OpenIddict core services, reference the 'OpenIddict.Core' package ")
+ .AppendLine("and call 'services.AddOpenIddict().AddCore()' from 'ConfigureServices'.")
+ .Append("Alternatively, you can disable the built-in database-based server features by enabling ")
+ .Append("the degraded mode with 'services.AddOpenIddict().AddServer().EnableDegradedMode()'.")
+ .ToString());
+
+ public AttachAuthorization(
+ [NotNull] IOpenIddictApplicationManager applicationManager,
+ [NotNull] IOpenIddictAuthorizationManager authorizationManager)
+ {
+ _applicationManager = applicationManager;
+ _authorizationManager = authorizationManager;
+ }
+
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictServerHandlerDescriptor Descriptor { get; }
+ = OpenIddictServerHandlerDescriptor.CreateBuilder()
+ .AddFilter()
+ .AddFilter()
+ .UseScopedHandler()
+ .SetOrder(EvaluateReturnedTokens.Descriptor.Order + 1_000)
+ .Build();
+
+ ///
+ /// Processes the event.
+ ///
+ /// The context associated with the event to process.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public async ValueTask HandleAsync([NotNull] ProcessSigninContext context)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ // If no authorization code or refresh token is returned, don't create an authorization.
+ if (!context.IncludeAuthorizationCode && !context.IncludeRefreshToken)
+ {
+ return;
+ }
+
+ // If an authorization identifier was explicitly specified, don't create an ad-hoc authorization.
+ if (!string.IsNullOrEmpty(context.Principal.GetInternalAuthorizationId()))
+ {
+ return;
+ }
+
+ var descriptor = new OpenIddictAuthorizationDescriptor
+ {
+ Principal = context.Principal,
+ Status = Statuses.Valid,
+ Subject = context.Principal.GetClaim(Claims.Subject),
+ Type = AuthorizationTypes.AdHoc
+ };
+
+ descriptor.Scopes.UnionWith(context.Principal.GetScopes());
+
+ // If the client application is known, associate it to the authorization.
+ if (!string.IsNullOrEmpty(context.Request.ClientId))
+ {
+ var application = await _applicationManager.FindByClientIdAsync(context.Request.ClientId);
+ if (application == null)
+ {
+ throw new InvalidOperationException("The application entry cannot be found in the database.");
+ }
+
+ descriptor.ApplicationId = await _applicationManager.GetIdAsync(application);
+ }
+
+ var authorization = await _authorizationManager.CreateAsync(descriptor);
+ if (authorization == null)
+ {
+ return;
+ }
+
+ var identifier = await _authorizationManager.GetIdAsync(authorization);
+
+ if (string.IsNullOrEmpty(context.Request.ClientId))
+ {
+ context.Logger.LogInformation("An ad hoc authorization was automatically created and " +
+ "associated with an unknown application: {Identifier}.", identifier);
+ }
+
+ else
+ {
+ context.Logger.LogInformation("An ad hoc authorization was automatically created and " +
+ "associated with the '{ClientId}' application: {Identifier}.",
+ context.Request.ClientId, identifier);
+ }
+
+ // Attach the unique identifier of the ad hoc authorization to the authentication principal
+ // so that it is attached to all the derived tokens, allowing batched revocations support.
+ context.Principal.SetInternalAuthorizationId(identifier);
+ }
+ }
+
///
/// Contains the logic responsible of generating and attaching an access token.
///
@@ -445,7 +557,7 @@ namespace OpenIddict.Server
= OpenIddictServerHandlerDescriptor.CreateBuilder()
.AddFilter()
.UseScopedHandler()
- .SetOrder(EvaluateReturnedTokens.Descriptor.Order + 1_000)
+ .SetOrder(AttachAuthorization.Descriptor.Order + 1_000)
.Build();
///
@@ -496,7 +608,7 @@ namespace OpenIddict.Server
claim.Properties.Remove(OpenIddictConstants.Properties.Destinations);
}
- principal.SetTokenId(Guid.NewGuid().ToString()).SetCreationDate(DateTimeOffset.UtcNow);
+ principal.SetPublicTokenId(Guid.NewGuid().ToString()).SetCreationDate(DateTimeOffset.UtcNow);
var lifetime = context.Principal.GetAccessTokenLifetime() ?? context.Options.AccessTokenLifetime;
if (lifetime.HasValue)
@@ -583,7 +695,7 @@ namespace OpenIddict.Server
}
var principal = context.Principal.Clone(_ => true)
- .SetTokenId(Guid.NewGuid().ToString())
+ .SetPublicTokenId(Guid.NewGuid().ToString())
.SetCreationDate(DateTimeOffset.UtcNow);
var lifetime = context.Principal.GetAuthorizationCodeLifetime() ?? context.Options.AuthorizationCodeLifetime;
@@ -664,7 +776,7 @@ namespace OpenIddict.Server
}
var principal = context.Principal.Clone(_ => true)
- .SetTokenId(Guid.NewGuid().ToString())
+ .SetPublicTokenId(Guid.NewGuid().ToString())
.SetCreationDate(DateTimeOffset.UtcNow);
var lifetime = context.Principal.GetRefreshTokenLifetime() ?? context.Options.RefreshTokenLifetime;
@@ -753,7 +865,7 @@ namespace OpenIddict.Server
claim.Properties.Remove(OpenIddictConstants.Properties.Destinations);
}
- principal.SetTokenId(Guid.NewGuid().ToString()).SetCreationDate(DateTimeOffset.UtcNow);
+ principal.SetPublicTokenId(Guid.NewGuid().ToString()).SetCreationDate(DateTimeOffset.UtcNow);
var lifetime = context.Principal.GetIdentityTokenLifetime() ?? context.Options.IdentityTokenLifetime;
if (lifetime.HasValue)