diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs
index 55a321a3..7373d134 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs
@@ -446,6 +446,7 @@ namespace OpenIddict.Server
var parameters = context.Options.TokenValidationParameters.Clone();
parameters.ValidIssuer ??= context.Issuer?.AbsoluteUri;
+ parameters.ValidateIssuer = !string.IsNullOrEmpty(parameters.ValidIssuer);
parameters.ValidTypes = context.TokenType switch
{
// If no specific token type is expected, accept all token types at this stage.
diff --git a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs
index bc7b2a2a..f265b568 100644
--- a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs
+++ b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs
@@ -9,7 +9,6 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.IO;
-using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
@@ -20,8 +19,8 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
-using OpenIddict.Abstractions;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlerFilters;
using static OpenIddict.Validation.OpenIddictValidationEvents;
@@ -37,8 +36,13 @@ namespace OpenIddict.Validation.AspNetCore
* Request top-level processing:
*/
InferIssuerFromHost.Descriptor,
- ExtractGetOrPostRequest.Descriptor,
- ExtractAccessToken.Descriptor,
+
+ /*
+ * Authentication processing:
+ */
+ ExtractAccessTokenFromAuthorizationHeader.Descriptor,
+ ExtractAccessTokenFromBodyForm.Descriptor,
+ ExtractAccessTokenFromQueryString.Descriptor,
/*
* Challenge processing:
@@ -132,19 +136,19 @@ namespace OpenIddict.Validation.AspNetCore
}
///
- /// Contains the logic responsible of extracting OpenID Connect requests from GET or POST HTTP requests.
+ /// Contains the logic responsible of extracting the access token from the standard HTTP Authorization header.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
///
- public class ExtractGetOrPostRequest : IOpenIddictValidationHandler
+ public class ExtractAccessTokenFromAuthorizationHeader : IOpenIddictValidationHandler
{
///
/// Gets the default descriptor definition assigned to this handler.
///
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
- = OpenIddictValidationHandlerDescriptor.CreateBuilder()
+ = OpenIddictValidationHandlerDescriptor.CreateBuilder()
.AddFilter()
- .UseSingletonHandler()
- .SetOrder(InferIssuerFromHost.Descriptor.Order + 1_000)
+ .UseSingletonHandler()
+ .SetOrder(int.MinValue + 50_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();
@@ -155,13 +159,19 @@ namespace OpenIddict.Validation.AspNetCore
///
/// A that can be used to monitor the asynchronous operation.
///
- public async ValueTask HandleAsync([NotNull] ProcessRequestContext context)
+ public ValueTask HandleAsync([NotNull] ProcessAuthenticationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
+ // If a token was already resolved, don't overwrite it.
+ if (!string.IsNullOrEmpty(context.Token))
+ {
+ return default;
+ }
+
// This handler only applies to ASP.NET Core requests. If the HTTP context cannot be resolved,
// this may indicate that the request was incorrectly processed by another server stack.
var request = context.Transaction.GetHttpRequest();
@@ -170,38 +180,99 @@ namespace OpenIddict.Validation.AspNetCore
throw new InvalidOperationException("The ASP.NET Core HTTP request cannot be resolved.");
}
- if (HttpMethods.IsGet(request.Method))
+ // Resolve the access token from the standard Authorization header.
+ // See https://tools.ietf.org/html/rfc6750#section-2.1 for more information.
+ string header = request.Headers[HeaderNames.Authorization];
+ if (!string.IsNullOrEmpty(header) && header.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
+ {
+ context.Token = header.Substring("Bearer ".Length);
+ context.TokenType = TokenTypeHints.AccessToken;
+
+ return default;
+ }
+
+ return default;
+ }
+ }
+
+ ///
+ /// Contains the logic responsible of extracting the access token from the standard access_token form parameter.
+ /// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
+ ///
+ public class ExtractAccessTokenFromBodyForm : IOpenIddictValidationHandler
+ {
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
+ = OpenIddictValidationHandlerDescriptor.CreateBuilder()
+ .AddFilter()
+ .UseSingletonHandler()
+ .SetOrder(ExtractAccessTokenFromAuthorizationHeader.Descriptor.Order + 1_000)
+ .SetType(OpenIddictValidationHandlerType.BuiltIn)
+ .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] ProcessAuthenticationContext context)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ // If a token was already resolved, don't overwrite it.
+ if (!string.IsNullOrEmpty(context.Token))
{
- context.Request = new OpenIddictRequest(request.Query);
+ return;
}
- else if (HttpMethods.IsPost(request.Method) && !string.IsNullOrEmpty(request.ContentType) &&
- request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
+ // This handler only applies to ASP.NET Core requests. If the HTTP context cannot be resolved,
+ // this may indicate that the request was incorrectly processed by another server stack.
+ var request = context.Transaction.GetHttpRequest();
+ if (request == null)
+ {
+ throw new InvalidOperationException("The ASP.NET Core HTTP request cannot be resolved.");
+ }
+
+ if (string.IsNullOrEmpty(request.ContentType) ||
+ !request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
{
- context.Request = new OpenIddictRequest(await request.ReadFormAsync(request.HttpContext.RequestAborted));
+ return;
}
- else
+ // Resolve the access token from the standard access_token form parameter.
+ // See https://tools.ietf.org/html/rfc6750#section-2.2 for more information.
+ var form = await request.ReadFormAsync(request.HttpContext.RequestAborted);
+ if (form.TryGetValue(Parameters.AccessToken, out StringValues token))
{
- context.Request = new OpenIddictRequest();
+ context.Token = token;
+ context.TokenType = TokenTypeHints.AccessToken;
+
+ return;
}
}
}
///
- /// Contains the logic responsible of extracting an access token from the standard HTTP Authorization header.
+ /// Contains the logic responsible of extracting the access token from the standard access_token query parameter.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
///
- public class ExtractAccessToken : IOpenIddictValidationHandler
+ public class ExtractAccessTokenFromQueryString : IOpenIddictValidationHandler
{
///
/// Gets the default descriptor definition assigned to this handler.
///
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
- = OpenIddictValidationHandlerDescriptor.CreateBuilder()
+ = OpenIddictValidationHandlerDescriptor.CreateBuilder()
.AddFilter()
- .UseSingletonHandler()
- .SetOrder(ExtractGetOrPostRequest.Descriptor.Order + 1_000)
+ .UseSingletonHandler()
+ .SetOrder(ExtractAccessTokenFromBodyForm.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();
@@ -212,13 +283,19 @@ namespace OpenIddict.Validation.AspNetCore
///
/// A that can be used to monitor the asynchronous operation.
///
- public ValueTask HandleAsync([NotNull] ProcessRequestContext context)
+ public ValueTask HandleAsync([NotNull] ProcessAuthenticationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
+ // If a token was already resolved, don't overwrite it.
+ if (!string.IsNullOrEmpty(context.Token))
+ {
+ return default;
+ }
+
// This handler only applies to ASP.NET Core requests. If the HTTP context cannot be resolved,
// this may indicate that the request was incorrectly processed by another server stack.
var request = context.Transaction.GetHttpRequest();
@@ -227,15 +304,16 @@ namespace OpenIddict.Validation.AspNetCore
throw new InvalidOperationException("The ASP.NET Core HTTP request cannot be resolved.");
}
- string header = request.Headers[HeaderNames.Authorization];
- if (string.IsNullOrEmpty(header) || !header.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
+ // Resolve the access token from the standard access_token query parameter.
+ // See https://tools.ietf.org/html/rfc6750#section-2.3 for more information.
+ if (request.Query.TryGetValue(Parameters.AccessToken, out StringValues token))
{
+ context.Token = token;
+ context.TokenType = TokenTypeHints.AccessToken;
+
return default;
}
- // Attach the access token to the request message.
- context.Request.AccessToken = header.Substring("Bearer ".Length);
-
return default;
}
}
diff --git a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs
index a1738790..d237f9fe 100644
--- a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs
+++ b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs
@@ -17,7 +17,6 @@ using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Owin.Security;
-using OpenIddict.Abstractions;
using Owin;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Validation.OpenIddictValidationEvents;
@@ -34,8 +33,13 @@ namespace OpenIddict.Validation.Owin
* Request top-level processing:
*/
InferIssuerFromHost.Descriptor,
- ExtractGetOrPostRequest.Descriptor,
- ExtractAccessToken.Descriptor,
+
+ /*
+ * Authentication processing:
+ */
+ ExtractAccessTokenFromAuthorizationHeader.Descriptor,
+ ExtractAccessTokenFromBodyForm.Descriptor,
+ ExtractAccessTokenFromQueryString.Descriptor,
/*
* Challenge processing:
@@ -129,19 +133,19 @@ namespace OpenIddict.Validation.Owin
}
///
- /// Contains the logic responsible of extracting OpenID Connect requests from GET or POST HTTP requests.
+ /// Contains the logic responsible of extracting the access token from the standard HTTP Authorization header.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
///
- public class ExtractGetOrPostRequest : IOpenIddictValidationHandler
+ public class ExtractAccessTokenFromAuthorizationHeader : IOpenIddictValidationHandler
{
///
/// Gets the default descriptor definition assigned to this handler.
///
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
- = OpenIddictValidationHandlerDescriptor.CreateBuilder()
+ = OpenIddictValidationHandlerDescriptor.CreateBuilder()
.AddFilter()
- .UseSingletonHandler()
- .SetOrder(InferIssuerFromHost.Descriptor.Order + 1_000)
+ .UseSingletonHandler()
+ .SetOrder(int.MinValue + 50_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();
@@ -152,13 +156,19 @@ namespace OpenIddict.Validation.Owin
///
/// A that can be used to monitor the asynchronous operation.
///
- public async ValueTask HandleAsync([NotNull] ProcessRequestContext context)
+ public ValueTask HandleAsync([NotNull] ProcessAuthenticationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
+ // If a token was already resolved, don't overwrite it.
+ if (!string.IsNullOrEmpty(context.Token))
+ {
+ return default;
+ }
+
// This handler only applies to OWIN requests. If The OWIN request cannot be resolved,
// this may indicate that the request was incorrectly processed by another server stack.
var request = context.Transaction.GetOwinRequest();
@@ -167,39 +177,100 @@ namespace OpenIddict.Validation.Owin
throw new InvalidOperationException("The OWIN request cannot be resolved.");
}
- if (string.Equals(request.Method, "GET", StringComparison.OrdinalIgnoreCase))
+ // Resolve the access token from the standard Authorization header.
+ // See https://tools.ietf.org/html/rfc6750#section-2.1 for more information.
+ string header = request.Headers["Authorization"];
+ if (!string.IsNullOrEmpty(header) && header.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
+ {
+ context.Token = header.Substring("Bearer ".Length);
+ context.TokenType = TokenTypeHints.AccessToken;
+
+ return default;
+ }
+
+ return default;
+ }
+ }
+
+ ///
+ /// Contains the logic responsible of extracting the access token from the standard access_token form parameter.
+ /// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
+ ///
+ public class ExtractAccessTokenFromBodyForm : IOpenIddictValidationHandler
+ {
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
+ = OpenIddictValidationHandlerDescriptor.CreateBuilder()
+ .AddFilter()
+ .UseSingletonHandler()
+ .SetOrder(ExtractAccessTokenFromAuthorizationHeader.Descriptor.Order + 1_000)
+ .SetType(OpenIddictValidationHandlerType.BuiltIn)
+ .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] ProcessAuthenticationContext context)
+ {
+ if (context == null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ // If a token was already resolved, don't overwrite it.
+ if (!string.IsNullOrEmpty(context.Token))
{
- context.Request = new OpenIddictRequest(request.Query);
+ return;
}
- else if (string.Equals(request.Method, "POST", StringComparison.OrdinalIgnoreCase) &&
- !string.IsNullOrEmpty(request.ContentType) &&
- request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
+ // This handler only applies to OWIN requests. If The OWIN request cannot be resolved,
+ // this may indicate that the request was incorrectly processed by another server stack.
+ var request = context.Transaction.GetOwinRequest();
+ if (request == null)
+ {
+ throw new InvalidOperationException("The OWIN request cannot be resolved.");
+ }
+
+ if (string.IsNullOrEmpty(request.ContentType) ||
+ !request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
{
- context.Request = new OpenIddictRequest(await request.ReadFormAsync());
+ return;
}
- else
+ // Resolve the access token from the standard access_token form parameter.
+ // See https://tools.ietf.org/html/rfc6750#section-2.2 for more information.
+ var form = await request.ReadFormAsync();
+ string token = form[Parameters.AccessToken];
+ if (!string.IsNullOrEmpty(token))
{
- context.Request = new OpenIddictRequest();
+ context.Token = token;
+ context.TokenType = TokenTypeHints.AccessToken;
+
+ return;
}
}
}
///
- /// Contains the logic responsible of extracting an access token from the standard HTTP Authorization header.
+ /// Contains the logic responsible of extracting the access token from the standard access_token query parameter.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
///
- public class ExtractAccessToken : IOpenIddictValidationHandler
+ public class ExtractAccessTokenFromQueryString : IOpenIddictValidationHandler
{
///
/// Gets the default descriptor definition assigned to this handler.
///
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
- = OpenIddictValidationHandlerDescriptor.CreateBuilder()
+ = OpenIddictValidationHandlerDescriptor.CreateBuilder()
.AddFilter()
- .UseSingletonHandler()
- .SetOrder(ExtractGetOrPostRequest.Descriptor.Order + 1_000)
+ .UseSingletonHandler()
+ .SetOrder(ExtractAccessTokenFromBodyForm.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();
@@ -210,13 +281,19 @@ namespace OpenIddict.Validation.Owin
///
/// A that can be used to monitor the asynchronous operation.
///
- public ValueTask HandleAsync([NotNull] ProcessRequestContext context)
+ public ValueTask HandleAsync([NotNull] ProcessAuthenticationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
+ // If a token was already resolved, don't overwrite it.
+ if (!string.IsNullOrEmpty(context.Token))
+ {
+ return default;
+ }
+
// This handler only applies to OWIN requests. If The OWIN request cannot be resolved,
// this may indicate that the request was incorrectly processed by another server stack.
var request = context.Transaction.GetOwinRequest();
@@ -225,15 +302,17 @@ namespace OpenIddict.Validation.Owin
throw new InvalidOperationException("The OWIN request cannot be resolved.");
}
- string header = request.Headers["Authorization"];
- if (string.IsNullOrEmpty(header) || !header.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
+ // Resolve the access token from the standard access_token query parameter.
+ // See https://tools.ietf.org/html/rfc6750#section-2.3 for more information.
+ string token = request.Query[Parameters.AccessToken];
+ if (!string.IsNullOrEmpty(token))
{
+ context.Token = token;
+ context.TokenType = TokenTypeHints.AccessToken;
+
return default;
}
- // Attach the access token to the request message.
- context.Request.AccessToken = header.Substring("Bearer ".Length);
-
return default;
}
}
diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs
index a62610eb..6d031a2f 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs
@@ -30,7 +30,7 @@ namespace OpenIddict.Validation
/*
* Authentication processing:
*/
- ValidateAccessTokenParameter.Descriptor,
+ ValidateToken.Descriptor,
ValidateReferenceTokenIdentifier.Descriptor,
ValidateIdentityModelToken.Descriptor,
IntrospectToken.Descriptor,
@@ -52,16 +52,16 @@ namespace OpenIddict.Validation
.AddRange(Introspection.DefaultHandlers);
///
- /// Contains the logic responsible of validating the access token resolved from the current request.
+ /// Contains the logic responsible of ensuring a token was correctly resolved from the context.
///
- public class ValidateAccessTokenParameter : IOpenIddictValidationHandler
+ public class ValidateToken : IOpenIddictValidationHandler
{
///
/// Gets the default descriptor definition assigned to this handler.
///
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder()
- .UseSingletonHandler()
+ .UseSingletonHandler()
.SetOrder(int.MinValue + 100_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();
@@ -80,18 +80,22 @@ namespace OpenIddict.Validation
throw new ArgumentNullException(nameof(context));
}
- if (string.IsNullOrEmpty(context.Request.AccessToken))
+ // Note: unlike the equivalent event in the server stack, authentication can be triggered for
+ // arbitrary requests (typically, API endpoints that are not owned by the validation stack).
+ // As such, the token is not directly resolved from the request, that may be null at this stage.
+ // Instead, the token is expected to be populated by one or multiple handlers provided by the host.
+ //
+ // Note: this event can also be triggered by the validation service to validate an arbitrary token.
+
+ if (string.IsNullOrEmpty(context.Token))
{
context.Reject(
error: Errors.MissingToken,
- description: "The access token is missing.");
+ description: "The security token is missing.");
return default;
}
- context.Token = context.Request.AccessToken;
- context.TokenType = TokenTypeHints.AccessToken;
-
return default;
}
}
@@ -121,7 +125,7 @@ namespace OpenIddict.Validation
.AddFilter()
.AddFilter()
.UseScopedHandler()
- .SetOrder(ValidateAccessTokenParameter.Descriptor.Order + 1_000)
+ .SetOrder(ValidateToken.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();
@@ -223,6 +227,7 @@ namespace OpenIddict.Validation
// OpenID Connect server configuration (that can be static or retrieved using discovery).
var parameters = context.Options.TokenValidationParameters.Clone();
parameters.ValidIssuer ??= configuration.Issuer ?? context.Issuer?.AbsoluteUri;
+ parameters.ValidateIssuer = !string.IsNullOrEmpty(parameters.ValidIssuer);
// Combine the signing keys registered statically in the token validation parameters
// with the signing keys resolved from the OpenID Connect server configuration.
diff --git a/src/OpenIddict.Validation/OpenIddictValidationService.cs b/src/OpenIddict.Validation/OpenIddictValidationService.cs
index e019a133..b75cff1b 100644
--- a/src/OpenIddict.Validation/OpenIddictValidationService.cs
+++ b/src/OpenIddict.Validation/OpenIddictValidationService.cs
@@ -14,6 +14,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Abstractions;
+using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Validation.OpenIddictValidationEvents;
namespace OpenIddict.Validation
@@ -568,5 +569,74 @@ namespace OpenIddict.Validation
}
}
}
+
+ ///
+ /// Validates the specified access token and returns the principal extracted from the token.
+ ///
+ /// The access token to validate.
+ /// The that can be used to abort the operation.
+ /// The principal containing the claims extracted from the token.
+ public async ValueTask ValidateAccessTokenAsync(
+ [NotNull] string token, CancellationToken cancellationToken = default)
+ {
+ if (string.IsNullOrEmpty(token))
+ {
+ throw new ArgumentException("The access token cannot be null or empty.", nameof(token));
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Note: this service is registered as a singleton service. As such, it cannot
+ // directly depend on scoped services like the validation provider. To work around
+ // this limitation, a scope is manually created for each method to this service.
+ var scope = _provider.CreateScope();
+
+ // Note: a try/finally block is deliberately used here to ensure the service scope
+ // can be disposed of asynchronously if it implements IAsyncDisposable.
+ try
+ {
+ var dispatcher = scope.ServiceProvider.GetRequiredService();
+ var factory = scope.ServiceProvider.GetRequiredService();
+ var transaction = await factory.CreateTransactionAsync();
+
+ var context = new ProcessAuthenticationContext(transaction)
+ {
+ Token = token,
+ TokenType = TokenTypeHints.AccessToken
+ };
+
+ await dispatcher.DispatchAsync(context);
+
+ if (context.IsRejected)
+ {
+ var message = new StringBuilder()
+ .AppendLine("An error occurred while validating the access token.")
+ .AppendFormat("Error: {0}", context.Error ?? "(not available)")
+ .AppendLine()
+ .AppendFormat("Error description: {0}", context.ErrorDescription ?? "(not available)")
+ .AppendLine()
+ .AppendFormat("Error URI: {0}", context.ErrorUri ?? "(not available)")
+ .ToString();
+
+ throw new OpenIddictExceptions.GenericException(message,
+ context.Error, context.ErrorDescription, context.ErrorUri);
+ }
+
+ return context.Principal;
+ }
+
+ finally
+ {
+ if (scope is IAsyncDisposable disposable)
+ {
+ await disposable.DisposeAsync();
+ }
+
+ else
+ {
+ scope.Dispose();
+ }
+ }
+ }
}
}