49 changed files with 2754 additions and 554 deletions
@ -1,14 +0,0 @@ |
|||
<?xml version="1.0"?> |
|||
<configuration> |
|||
<system.webServer> |
|||
<handlers> |
|||
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" /> |
|||
</handlers> |
|||
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" hostingModel="InProcess"> |
|||
<environmentVariables> |
|||
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" /> |
|||
<environmentVariable name="COMPLUS_ForceENC" value="1" /> |
|||
</environmentVariables> |
|||
</aspNetCore> |
|||
</system.webServer> |
|||
</configuration> |
|||
@ -1,14 +0,0 @@ |
|||
<?xml version="1.0"?> |
|||
<configuration> |
|||
<system.webServer> |
|||
<handlers> |
|||
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" /> |
|||
</handlers> |
|||
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" hostingModel="InProcess"> |
|||
<environmentVariables> |
|||
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" /> |
|||
<environmentVariable name="COMPLUS_ForceENC" value="1" /> |
|||
</environmentVariables> |
|||
</aspNetCore> |
|||
</system.webServer> |
|||
</configuration> |
|||
@ -1,16 +0,0 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
namespace OpenIddict.Validation.SystemNetHttp |
|||
{ |
|||
public static class OpenIddictValidationSystemNetHttpConstants |
|||
{ |
|||
public static class Clients |
|||
{ |
|||
public const string Discovery = "OpenIddict.Validation.SystemNetHttp.Discovery"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System.Collections.Immutable; |
|||
using static OpenIddict.Validation.OpenIddictValidationEvents; |
|||
|
|||
namespace OpenIddict.Validation.SystemNetHttp |
|||
{ |
|||
public static partial class OpenIddictValidationSystemNetHttpHandlers |
|||
{ |
|||
public static class Discovery |
|||
{ |
|||
public static ImmutableArray<OpenIddictValidationHandlerDescriptor> DefaultHandlers { get; } = ImmutableArray.Create( |
|||
/* |
|||
* Configuration request processing: |
|||
*/ |
|||
PrepareGetHttpRequest<PrepareConfigurationRequestContext>.Descriptor, |
|||
SendHttpRequest<ApplyConfigurationRequestContext>.Descriptor, |
|||
|
|||
/* |
|||
* Configuration response processing: |
|||
*/ |
|||
ExtractJsonHttpResponse<ExtractConfigurationResponseContext>.Descriptor, |
|||
|
|||
/* |
|||
* Cryptography request processing: |
|||
*/ |
|||
PrepareGetHttpRequest<PrepareCryptographyRequestContext>.Descriptor, |
|||
SendHttpRequest<ApplyCryptographyRequestContext>.Descriptor, |
|||
|
|||
/* |
|||
* Configuration response processing: |
|||
*/ |
|||
ExtractJsonHttpResponse<ExtractCryptographyResponseContext>.Descriptor); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System.Collections.Immutable; |
|||
using static OpenIddict.Validation.OpenIddictValidationEvents; |
|||
|
|||
namespace OpenIddict.Validation.SystemNetHttp |
|||
{ |
|||
public static partial class OpenIddictValidationSystemNetHttpHandlers |
|||
{ |
|||
public static class Introspection |
|||
{ |
|||
public static ImmutableArray<OpenIddictValidationHandlerDescriptor> DefaultHandlers { get; } = ImmutableArray.Create( |
|||
/* |
|||
* Introspection request processing: |
|||
*/ |
|||
PreparePostHttpRequest<PrepareIntrospectionRequestContext>.Descriptor, |
|||
SendHttpRequest<ApplyIntrospectionRequestContext>.Descriptor, |
|||
|
|||
/* |
|||
* Introspection response processing: |
|||
*/ |
|||
ExtractJsonHttpResponse<ExtractIntrospectionResponseContext>.Descriptor); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using JetBrains.Annotations; |
|||
using OpenIddict.Validation; |
|||
|
|||
namespace System.Net.Http |
|||
{ |
|||
/// <summary>
|
|||
/// Exposes companion extensions for the OpenIddict/ASP.NET Core integration.
|
|||
/// </summary>
|
|||
public static class OpenIddictValidationSystemNetHttpHelpers |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the <see cref="HttpRequestMessage"/> associated with the current context.
|
|||
/// </summary>
|
|||
/// <param name="transaction">The transaction instance.</param>
|
|||
/// <returns>The <see cref="HttpRequestMessage"/> instance or <c>null</c> if it couldn't be found.</returns>
|
|||
public static HttpRequestMessage GetHttpRequestMessage([NotNull] this OpenIddictValidationTransaction transaction) |
|||
=> transaction.GetProperty<HttpRequestMessage>(typeof(HttpRequestMessage).FullName); |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="HttpResponseMessage"/> associated with the current context.
|
|||
/// </summary>
|
|||
/// <param name="transaction">The transaction instance.</param>
|
|||
/// <returns>The <see cref="HttpResponseMessage"/> instance or <c>null</c> if it couldn't be found.</returns>
|
|||
public static HttpResponseMessage GetHttpResponseMessage([NotNull] this OpenIddictValidationTransaction transaction) |
|||
=> transaction.GetProperty<HttpResponseMessage>(typeof(HttpResponseMessage).FullName); |
|||
} |
|||
} |
|||
@ -0,0 +1,143 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using JetBrains.Annotations; |
|||
using Microsoft.IdentityModel.Protocols.OpenIdConnect; |
|||
using Microsoft.IdentityModel.Tokens; |
|||
|
|||
namespace OpenIddict.Validation |
|||
{ |
|||
public static partial class OpenIddictValidationEvents |
|||
{ |
|||
/// <summary>
|
|||
/// Represents an event called for each request to the configuration endpoint
|
|||
/// to give the user code a chance to add parameters to the configuration request.
|
|||
/// </summary>
|
|||
public class PrepareConfigurationRequestContext : BaseExternalContext |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="PrepareConfigurationRequestContext"/> class.
|
|||
/// </summary>
|
|||
public PrepareConfigurationRequestContext([NotNull] OpenIddictValidationTransaction transaction) |
|||
: base(transaction) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Represents an event called for each request to the configuration endpoint
|
|||
/// to send the configuration request to the remote authorization server.
|
|||
/// </summary>
|
|||
public class ApplyConfigurationRequestContext : BaseExternalContext |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="ApplyConfigurationRequestContext"/> class.
|
|||
/// </summary>
|
|||
public ApplyConfigurationRequestContext([NotNull] OpenIddictValidationTransaction transaction) |
|||
: base(transaction) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Represents an event called for each configuration response
|
|||
/// to extract the response parameters from the server response.
|
|||
/// </summary>
|
|||
public class ExtractConfigurationResponseContext : BaseExternalContext |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="ExtractConfigurationResponseContext"/> class.
|
|||
/// </summary>
|
|||
public ExtractConfigurationResponseContext([NotNull] OpenIddictValidationTransaction transaction) |
|||
: base(transaction) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Represents an event called for each validated configuration response.
|
|||
/// </summary>
|
|||
public class HandleConfigurationResponseContext : BaseExternalContext |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="HandleConfigurationResponseContext"/> class.
|
|||
/// </summary>
|
|||
public HandleConfigurationResponseContext([NotNull] OpenIddictValidationTransaction transaction) |
|||
: base(transaction) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the OpenID Connect configuration.
|
|||
/// </summary>
|
|||
public OpenIdConnectConfiguration Configuration { get; } = new OpenIdConnectConfiguration(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Represents an event called for each request to the cryptography endpoint
|
|||
/// to give the user code a chance to add parameters to the cryptography request.
|
|||
/// </summary>
|
|||
public class PrepareCryptographyRequestContext : BaseExternalContext |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="PrepareCryptographyRequestContext"/> class.
|
|||
/// </summary>
|
|||
public PrepareCryptographyRequestContext([NotNull] OpenIddictValidationTransaction transaction) |
|||
: base(transaction) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Represents an event called for each request to the cryptography endpoint
|
|||
/// to send the cryptography request to the remote authorization server.
|
|||
/// </summary>
|
|||
public class ApplyCryptographyRequestContext : BaseExternalContext |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="ApplyCryptographyRequestContext"/> class.
|
|||
/// </summary>
|
|||
public ApplyCryptographyRequestContext([NotNull] OpenIddictValidationTransaction transaction) |
|||
: base(transaction) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Represents an event called for each cryptography response
|
|||
/// to extract the response parameters from the server response.
|
|||
/// </summary>
|
|||
public class ExtractCryptographyResponseContext : BaseExternalContext |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="ExtractCryptographyResponseContext"/> class.
|
|||
/// </summary>
|
|||
public ExtractCryptographyResponseContext([NotNull] OpenIddictValidationTransaction transaction) |
|||
: base(transaction) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Represents an event called for each validated cryptography response.
|
|||
/// </summary>
|
|||
public class HandleCryptographyResponseContext : BaseExternalContext |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="HandleCryptographyResponseContext"/> class.
|
|||
/// </summary>
|
|||
public HandleCryptographyResponseContext([NotNull] OpenIddictValidationTransaction transaction) |
|||
: base(transaction) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the security keys.
|
|||
/// </summary>
|
|||
public JsonWebKeySet SecurityKeys { get; } = new JsonWebKeySet(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,98 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System.Security.Claims; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace OpenIddict.Validation |
|||
{ |
|||
public static partial class OpenIddictValidationEvents |
|||
{ |
|||
/// <summary>
|
|||
/// Represents an event called for each request to the introspection endpoint
|
|||
/// to give the user code a chance to add parameters to the introspection request.
|
|||
/// </summary>
|
|||
public class PrepareIntrospectionRequestContext : BaseExternalContext |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="PrepareIntrospectionRequestContext"/> class.
|
|||
/// </summary>
|
|||
public PrepareIntrospectionRequestContext([NotNull] OpenIddictValidationTransaction transaction) |
|||
: base(transaction) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the token sent to the introspection endpoint.
|
|||
/// </summary>
|
|||
public string Token { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the token type sent to the introspection endpoint.
|
|||
/// </summary>
|
|||
public string TokenType { get; set; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Represents an event called for each request to the introspection endpoint
|
|||
/// to send the introspection request to the remote authorization server.
|
|||
/// </summary>
|
|||
public class ApplyIntrospectionRequestContext : BaseExternalContext |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="ApplyIntrospectionRequestContext"/> class.
|
|||
/// </summary>
|
|||
public ApplyIntrospectionRequestContext([NotNull] OpenIddictValidationTransaction transaction) |
|||
: base(transaction) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Represents an event called for each introspection response
|
|||
/// to extract the response parameters from the server response.
|
|||
/// </summary>
|
|||
public class ExtractIntrospectionResponseContext : BaseExternalContext |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="ExtractIntrospectionResponseContext"/> class.
|
|||
/// </summary>
|
|||
public ExtractIntrospectionResponseContext([NotNull] OpenIddictValidationTransaction transaction) |
|||
: base(transaction) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Represents an event called for each validated introspection response.
|
|||
/// </summary>
|
|||
public class HandleIntrospectionResponseContext : BaseExternalContext |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="HandleIntrospectionResponseContext"/> class.
|
|||
/// </summary>
|
|||
public HandleIntrospectionResponseContext([NotNull] OpenIddictValidationTransaction transaction) |
|||
: base(transaction) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the token sent to the introspection endpoint.
|
|||
/// </summary>
|
|||
public string Token { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the token type sent to the introspection endpoint.
|
|||
/// </summary>
|
|||
public string TokenType { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the principal containing the claims resolved from the introspection response.
|
|||
/// </summary>
|
|||
public ClaimsPrincipal Principal { get; set; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,313 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Immutable; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.IdentityModel.Tokens; |
|||
using static OpenIddict.Abstractions.OpenIddictConstants; |
|||
using static OpenIddict.Validation.OpenIddictValidationEvents; |
|||
|
|||
namespace OpenIddict.Validation |
|||
{ |
|||
public static partial class OpenIddictValidationHandlers |
|||
{ |
|||
public static class Discovery |
|||
{ |
|||
public static ImmutableArray<OpenIddictValidationHandlerDescriptor> DefaultHandlers { get; } = ImmutableArray.Create( |
|||
/* |
|||
* Configuration response handling: |
|||
*/ |
|||
HandleErrorResponse<HandleConfigurationResponseContext>.Descriptor, |
|||
ValidateIssuer.Descriptor, |
|||
ExtractCryptographyEndpointUri.Descriptor, |
|||
ExtractIntrospectionEndpointUri.Descriptor, |
|||
|
|||
/* |
|||
* Cryptography response handling: |
|||
*/ |
|||
HandleErrorResponse<HandleCryptographyResponseContext>.Descriptor, |
|||
ExtractSigningKeys.Descriptor); |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible of extracting the issuer from the discovery document.
|
|||
/// </summary>
|
|||
public class ValidateIssuer : IOpenIddictValidationHandler<HandleConfigurationResponseContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>() |
|||
.UseSingletonHandler<ValidateIssuer>() |
|||
.SetOrder(HandleErrorResponse<HandleConfigurationResponseContext>.Descriptor.Order + 1_000) |
|||
.Build(); |
|||
|
|||
/// <summary>
|
|||
/// Processes the event.
|
|||
/// </summary>
|
|||
/// <param name="context">The context associated with the event to process.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public ValueTask HandleAsync([NotNull] HandleConfigurationResponseContext context) |
|||
{ |
|||
if (context == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
// The issuer returned in the discovery document must exactly match the URL used to access it.
|
|||
// See https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation.
|
|||
var issuer = (string) context.Response[Metadata.Issuer]; |
|||
if (string.IsNullOrEmpty(issuer)) |
|||
{ |
|||
context.Reject( |
|||
error: Errors.ServerError, |
|||
description: "No issuer could be found in the discovery document."); |
|||
|
|||
return default; |
|||
} |
|||
|
|||
if (!Uri.TryCreate(issuer, UriKind.Absolute, out Uri address)) |
|||
{ |
|||
context.Reject( |
|||
error: Errors.ServerError, |
|||
description: "A discovery response containing an invalid issuer was returned."); |
|||
|
|||
return default; |
|||
} |
|||
|
|||
if (context.Issuer != null && context.Issuer != address) |
|||
{ |
|||
context.Reject( |
|||
error: Errors.ServerError, |
|||
description: "The issuer returned by the discovery endpoint is not valid."); |
|||
|
|||
return default; |
|||
} |
|||
|
|||
context.Configuration.Issuer = issuer; |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible of extracting the JWKS endpoint address from the discovery document.
|
|||
/// </summary>
|
|||
public class ExtractCryptographyEndpointUri : IOpenIddictValidationHandler<HandleConfigurationResponseContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>() |
|||
.UseSingletonHandler<ExtractCryptographyEndpointUri>() |
|||
.SetOrder(ValidateIssuer.Descriptor.Order + 1_000) |
|||
.Build(); |
|||
|
|||
/// <summary>
|
|||
/// Processes the event.
|
|||
/// </summary>
|
|||
/// <param name="context">The context associated with the event to process.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public ValueTask HandleAsync([NotNull] HandleConfigurationResponseContext context) |
|||
{ |
|||
if (context == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
// Note: the jwks_uri node is required by the OpenID Connect discovery specification.
|
|||
// See https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation.
|
|||
var address = (string) context.Response[Metadata.JwksUri]; |
|||
if (string.IsNullOrEmpty(address)) |
|||
{ |
|||
context.Reject( |
|||
error: Errors.ServerError, |
|||
description: "No JWKS endpoint could be found in the discovery document."); |
|||
|
|||
return default; |
|||
} |
|||
|
|||
if (!Uri.IsWellFormedUriString(address, UriKind.Absolute)) |
|||
{ |
|||
context.Reject( |
|||
error: Errors.ServerError, |
|||
description: "A discovery response containing an invalid JWKS endpoint URL was returned."); |
|||
|
|||
return default; |
|||
} |
|||
|
|||
context.Configuration.JwksUri = address; |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible of extracting the introspection endpoint address from the discovery document.
|
|||
/// </summary>
|
|||
public class ExtractIntrospectionEndpointUri : IOpenIddictValidationHandler<HandleConfigurationResponseContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>() |
|||
.UseSingletonHandler<ExtractIntrospectionEndpointUri>() |
|||
.SetOrder(ExtractCryptographyEndpointUri.Descriptor.Order + 1_000) |
|||
.Build(); |
|||
|
|||
/// <summary>
|
|||
/// Processes the event.
|
|||
/// </summary>
|
|||
/// <param name="context">The context associated with the event to process.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public ValueTask HandleAsync([NotNull] HandleConfigurationResponseContext context) |
|||
{ |
|||
if (context == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
var address = (string) context.Response[Metadata.IntrospectionEndpoint]; |
|||
if (!string.IsNullOrEmpty(address) && !Uri.IsWellFormedUriString(address, UriKind.Absolute)) |
|||
{ |
|||
context.Reject( |
|||
error: Errors.ServerError, |
|||
description: "A discovery response containing an invalid introspection endpoint URL was returned."); |
|||
|
|||
return default; |
|||
} |
|||
|
|||
context.Configuration.IntrospectionEndpoint = address; |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible of extracting the signing keys from the JWKS document.
|
|||
/// </summary>
|
|||
public class ExtractSigningKeys : IOpenIddictValidationHandler<HandleCryptographyResponseContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleCryptographyResponseContext>() |
|||
.UseSingletonHandler<ExtractSigningKeys>() |
|||
.SetOrder(HandleErrorResponse<HandleCryptographyResponseContext>.Descriptor.Order + 1_000) |
|||
.Build(); |
|||
|
|||
/// <summary>
|
|||
/// Processes the event.
|
|||
/// </summary>
|
|||
/// <param name="context">The context associated with the event to process.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public ValueTask HandleAsync([NotNull] HandleCryptographyResponseContext context) |
|||
{ |
|||
if (context == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
var keys = context.Response[JsonWebKeySetParameterNames.Keys]?.GetUnnamedParameters(); |
|||
if (keys == null || keys.Count == 0) |
|||
{ |
|||
context.Reject( |
|||
error: Errors.ServerError, |
|||
description: "The JWKS document didn't contain a valid 'jwks' node with at least one key."); |
|||
|
|||
return default; |
|||
} |
|||
|
|||
for (var index = 0; index < keys.Count; index++) |
|||
{ |
|||
// Note: the "use" parameter is defined as optional by the specification.
|
|||
// To prevent key swapping attacks, OpenIddict requires that this parameter
|
|||
// be present and will ignore keys that don't include a "use" parameter.
|
|||
var use = (string) keys[index][JsonWebKeyParameterNames.Use]; |
|||
if (string.IsNullOrEmpty(use)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
// Ignore security keys that are not used for signing.
|
|||
if (!string.Equals(use, JsonWebKeyUseNames.Sig, StringComparison.Ordinal)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var key = (string) keys[index][JsonWebKeyParameterNames.Kty] switch |
|||
{ |
|||
JsonWebAlgorithmsKeyTypes.RSA => new JsonWebKey |
|||
{ |
|||
Kty = JsonWebAlgorithmsKeyTypes.RSA, |
|||
E = (string) keys[index][JsonWebKeyParameterNames.E], |
|||
N = (string) keys[index][JsonWebKeyParameterNames.N] |
|||
}, |
|||
|
|||
JsonWebAlgorithmsKeyTypes.EllipticCurve => new JsonWebKey |
|||
{ |
|||
Kty = JsonWebAlgorithmsKeyTypes.EllipticCurve, |
|||
Crv = (string) keys[index][JsonWebKeyParameterNames.Crv], |
|||
X = (string) keys[index][JsonWebKeyParameterNames.X], |
|||
Y = (string) keys[index][JsonWebKeyParameterNames.Y] |
|||
}, |
|||
|
|||
_ => null |
|||
}; |
|||
|
|||
if (key == null) |
|||
{ |
|||
context.Reject( |
|||
error: Errors.ServerError, |
|||
description: "A JWKS response containing an unsupported key was returned."); |
|||
|
|||
return default; |
|||
} |
|||
|
|||
key.KeyId = (string) keys[index][JsonWebKeyParameterNames.Kid]; |
|||
key.X5t = (string) keys[index][JsonWebKeyParameterNames.X5t]; |
|||
key.X5tS256 = (string) keys[index][JsonWebKeyParameterNames.X5tS256]; |
|||
|
|||
if (keys[index].TryGetParameter(JsonWebKeyParameterNames.X5c, out var chain)) |
|||
{ |
|||
foreach (var certificate in chain.GetNamedParameters()) |
|||
{ |
|||
var value = (string) certificate.Value; |
|||
if (string.IsNullOrEmpty(value)) |
|||
{ |
|||
context.Reject( |
|||
error: Errors.ServerError, |
|||
description: "A JWKS response containing an invalid key was returned."); |
|||
|
|||
return default; |
|||
} |
|||
|
|||
key.X5c.Add(value); |
|||
} |
|||
} |
|||
|
|||
context.SecurityKeys.Keys.Add(key); |
|||
} |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,504 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Collections.Immutable; |
|||
using System.Globalization; |
|||
using System.Security.Claims; |
|||
using System.Text.Json; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.IdentityModel.JsonWebTokens; |
|||
using OpenIddict.Abstractions; |
|||
using static OpenIddict.Abstractions.OpenIddictConstants; |
|||
using static OpenIddict.Validation.OpenIddictValidationEvents; |
|||
|
|||
namespace OpenIddict.Validation |
|||
{ |
|||
public static partial class OpenIddictValidationHandlers |
|||
{ |
|||
public static class Introspection |
|||
{ |
|||
public static ImmutableArray<OpenIddictValidationHandlerDescriptor> DefaultHandlers { get; } = ImmutableArray.Create( |
|||
/* |
|||
* Introspection response handling: |
|||
*/ |
|||
AttachCredentials.Descriptor, |
|||
AttachAccessToken.Descriptor, |
|||
|
|||
/* |
|||
* Introspection response handling: |
|||
*/ |
|||
HandleErrorResponse<HandleIntrospectionResponseContext>.Descriptor, |
|||
HandleInactiveResponse.Descriptor, |
|||
ValidateWellKnownClaims.Descriptor, |
|||
ValidateIssuer.Descriptor, |
|||
ValidateTokenType.Descriptor, |
|||
PopulateClaims.Descriptor); |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible of attaching the client credentials to the introspection request.
|
|||
/// </summary>
|
|||
public class AttachCredentials : IOpenIddictValidationHandler<PrepareIntrospectionRequestContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictValidationHandlerDescriptor.CreateBuilder<PrepareIntrospectionRequestContext>() |
|||
.UseSingletonHandler<AttachCredentials>() |
|||
.SetOrder(int.MinValue + 100_000) |
|||
.Build(); |
|||
|
|||
/// <summary>
|
|||
/// Processes the event.
|
|||
/// </summary>
|
|||
/// <param name="context">The context associated with the event to process.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public ValueTask HandleAsync([NotNull] PrepareIntrospectionRequestContext context) |
|||
{ |
|||
if (context == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
context.Request.ClientId = context.Options.ClientId; |
|||
context.Request.ClientSecret = context.Options.ClientSecret; |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible of attaching the access token to the introspection request.
|
|||
/// </summary>
|
|||
public class AttachAccessToken : IOpenIddictValidationHandler<PrepareIntrospectionRequestContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictValidationHandlerDescriptor.CreateBuilder<PrepareIntrospectionRequestContext>() |
|||
.UseSingletonHandler<AttachAccessToken>() |
|||
.SetOrder(AttachCredentials.Descriptor.Order + 100_000) |
|||
.Build(); |
|||
|
|||
/// <summary>
|
|||
/// Processes the event.
|
|||
/// </summary>
|
|||
/// <param name="context">The context associated with the event to process.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public ValueTask HandleAsync([NotNull] PrepareIntrospectionRequestContext context) |
|||
{ |
|||
if (context == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
context.Request.Token = context.Token; |
|||
context.Request.TokenTypeHint = context.TokenType; |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible of extracting the active: false marker from the response.
|
|||
/// </summary>
|
|||
public class HandleInactiveResponse : IOpenIddictValidationHandler<HandleIntrospectionResponseContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleIntrospectionResponseContext>() |
|||
.UseSingletonHandler<HandleInactiveResponse>() |
|||
.SetOrder(HandleErrorResponse<HandleIntrospectionResponseContext>.Descriptor.Order + 1_000) |
|||
.Build(); |
|||
|
|||
/// <summary>
|
|||
/// Processes the event.
|
|||
/// </summary>
|
|||
/// <param name="context">The context associated with the event to process.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public ValueTask HandleAsync([NotNull] HandleIntrospectionResponseContext context) |
|||
{ |
|||
if (context == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
// Note: the introspection specification requires that server return "active: false" instead of a proper
|
|||
// OAuth 2.0 error when the token is invalid, expired, revoked or invalid for any other reason.
|
|||
// While OpenIddict's server can be tweaked to return a proper error (by removing NormalizeErrorResponse)
|
|||
// from the enabled handlers, supporting "active: false" is required to ensure total compatibility.
|
|||
|
|||
if (!context.Response.TryGetParameter(Parameters.Active, out var parameter)) |
|||
{ |
|||
context.Reject( |
|||
error: Errors.ServerError, |
|||
description: "The mandatory 'active' parameter couldn't be found in the introspection response."); |
|||
|
|||
return default; |
|||
} |
|||
|
|||
// Note: if the parameter cannot be converted to a boolean instance, the default value
|
|||
// (false) is returned by the static operator, which is appropriate for this check.
|
|||
if (!(bool) parameter) |
|||
{ |
|||
context.Reject( |
|||
error: Errors.InvalidToken, |
|||
description: "The token was rejected by the remote authorization server."); |
|||
|
|||
return default; |
|||
} |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible of validating the well-known claims contained in the introspection response.
|
|||
/// </summary>
|
|||
public class ValidateWellKnownClaims : IOpenIddictValidationHandler<HandleIntrospectionResponseContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleIntrospectionResponseContext>() |
|||
.UseSingletonHandler<ValidateWellKnownClaims>() |
|||
.SetOrder(HandleInactiveResponse.Descriptor.Order + 1_000) |
|||
.Build(); |
|||
|
|||
/// <summary>
|
|||
/// Processes the event.
|
|||
/// </summary>
|
|||
/// <param name="context">The context associated with the event to process.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public ValueTask HandleAsync([NotNull] HandleIntrospectionResponseContext context) |
|||
{ |
|||
if (context == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
foreach (var parameter in context.Response.GetParameters()) |
|||
{ |
|||
if (ValidateClaimType(parameter.Key, parameter.Value)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
context.Reject( |
|||
error: Errors.ServerError, |
|||
description: $"The {parameter.Key} claim is malformed or isn't of the expected type."); |
|||
|
|||
return default; |
|||
} |
|||
|
|||
return default; |
|||
|
|||
static bool ValidateClaimType(string name, OpenIddictParameter value) |
|||
{ |
|||
switch ((name, value.Value)) |
|||
{ |
|||
// The 'aud' claim CAN be represented either as a unique string or as an array of multiple strings.
|
|||
case (Claims.Audience, string _): |
|||
case (Claims.Audience, string[] _): |
|||
case (Claims.Audience, JsonElement element) when element.ValueKind == JsonValueKind.String || |
|||
(element.ValueKind == JsonValueKind.Array && ValidateArrayChildren(element, JsonValueKind.String)): |
|||
return true; |
|||
|
|||
// The 'exp', 'iat' and 'nbf' claims MUST be formatted as numeric date values.
|
|||
case (Claims.ExpiresAt, long _): |
|||
case (Claims.ExpiresAt, JsonElement element) when element.ValueKind == JsonValueKind.Number: |
|||
return true; |
|||
|
|||
case (Claims.IssuedAt, long _): |
|||
case (Claims.IssuedAt, JsonElement element) when element.ValueKind == JsonValueKind.Number: |
|||
return true; |
|||
|
|||
case (Claims.NotBefore, long _): |
|||
case (Claims.NotBefore, JsonElement element) when element.ValueKind == JsonValueKind.Number: |
|||
return true; |
|||
|
|||
// The 'jti' claim MUST be formatted as a unique string.
|
|||
case (Claims.JwtId, string _): |
|||
case (Claims.JwtId, JsonElement element) when element.ValueKind == JsonValueKind.String: |
|||
return true; |
|||
|
|||
// The 'iss' claim MUST be formatted as a unique string.
|
|||
case (Claims.Issuer, string _): |
|||
case (Claims.Issuer, JsonElement element) when element.ValueKind == JsonValueKind.String: |
|||
return true; |
|||
|
|||
// The 'scope' claim MUST be formatted as a unique string.
|
|||
case (Claims.Scope, string _): |
|||
case (Claims.Scope, JsonElement element) when element.ValueKind == JsonValueKind.String: |
|||
return true; |
|||
|
|||
// The 'token_usage' claim MUST be formatted as a unique string.
|
|||
case (Claims.TokenUsage, string _): |
|||
case (Claims.TokenUsage, JsonElement element) when element.ValueKind == JsonValueKind.String: |
|||
return true; |
|||
|
|||
// If the previously listed claims are represented differently,
|
|||
// return false to indicate the claims validation logic failed.
|
|||
case (Claims.Audience, _): |
|||
case (Claims.ExpiresAt, _): |
|||
case (Claims.IssuedAt, _): |
|||
case (Claims.Issuer, _): |
|||
case (Claims.NotBefore, _): |
|||
case (Claims.JwtId, _): |
|||
case (Claims.Scope, _): |
|||
case (Claims.TokenUsage, _): |
|||
return false; |
|||
|
|||
// Claims that are not in the well-known list can be of any type.
|
|||
default: return true; |
|||
} |
|||
} |
|||
|
|||
static bool ValidateArrayChildren(JsonElement element, JsonValueKind kind) |
|||
{ |
|||
foreach (var child in element.EnumerateArray()) |
|||
{ |
|||
if (child.ValueKind != kind) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible of extracting the issuer from the introspection response.
|
|||
/// </summary>
|
|||
public class ValidateIssuer : IOpenIddictValidationHandler<HandleIntrospectionResponseContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleIntrospectionResponseContext>() |
|||
.UseSingletonHandler<ValidateIssuer>() |
|||
.SetOrder(ValidateWellKnownClaims.Descriptor.Order + 1_000) |
|||
.Build(); |
|||
|
|||
/// <summary>
|
|||
/// Processes the event.
|
|||
/// </summary>
|
|||
/// <param name="context">The context associated with the event to process.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public ValueTask HandleAsync([NotNull] HandleIntrospectionResponseContext context) |
|||
{ |
|||
if (context == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
// The issuer claim is optional. If it's not null or empty, validate it to
|
|||
// ensure it matches the issuer registered in the server configuration.
|
|||
var issuer = (string) context.Response[Claims.Issuer]; |
|||
if (!string.IsNullOrEmpty(issuer)) |
|||
{ |
|||
if (!Uri.TryCreate(issuer, UriKind.Absolute, out Uri uri)) |
|||
{ |
|||
context.Reject( |
|||
error: Errors.ServerError, |
|||
description: "An introspection response containing an invalid issuer was returned."); |
|||
|
|||
return default; |
|||
} |
|||
|
|||
if (context.Issuer != null && context.Issuer != uri) |
|||
{ |
|||
context.Reject( |
|||
error: Errors.ServerError, |
|||
description: "The issuer returned in the introspection response is not valid."); |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible of extracting and validating the token type from the introspection response.
|
|||
/// </summary>
|
|||
public class ValidateTokenType : IOpenIddictValidationHandler<HandleIntrospectionResponseContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleIntrospectionResponseContext>() |
|||
.UseSingletonHandler<ValidateTokenType>() |
|||
.SetOrder(ValidateIssuer.Descriptor.Order + 1_000) |
|||
.Build(); |
|||
|
|||
/// <summary>
|
|||
/// Processes the event.
|
|||
/// </summary>
|
|||
/// <param name="context">The context associated with the event to process.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public ValueTask HandleAsync([NotNull] HandleIntrospectionResponseContext context) |
|||
{ |
|||
if (context == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
// OpenIddict-based authorization servers always return the actual token type using
|
|||
// the special "token_usage" claim, that helps resource servers determine whether the
|
|||
// introspected token is an access token and thus prevent token substitution attacks.
|
|||
var usage = (string) context.Response[Claims.TokenUsage]; |
|||
if (!string.IsNullOrEmpty(usage) && !string.Equals(usage, context.TokenType, StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
context.Reject( |
|||
error: Errors.InvalidToken, |
|||
description: "The introspected token is not an access token."); |
|||
|
|||
return default; |
|||
} |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible of extracting the claims from the introspection response.
|
|||
/// </summary>
|
|||
public class PopulateClaims : IOpenIddictValidationHandler<HandleIntrospectionResponseContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictValidationHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictValidationHandlerDescriptor.CreateBuilder<HandleIntrospectionResponseContext>() |
|||
.UseSingletonHandler<PopulateClaims>() |
|||
.SetOrder(ValidateTokenType.Descriptor.Order + 1_000) |
|||
.Build(); |
|||
|
|||
/// <summary>
|
|||
/// Processes the event.
|
|||
/// </summary>
|
|||
/// <param name="context">The context associated with the event to process.</param>
|
|||
/// <returns>
|
|||
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
|
|||
/// </returns>
|
|||
public ValueTask HandleAsync([NotNull] HandleIntrospectionResponseContext context) |
|||
{ |
|||
if (context == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
var identity = new ClaimsIdentity(context.Options.TokenValidationParameters.AuthenticationType); |
|||
|
|||
foreach (var parameter in context.Response.GetParameters()) |
|||
{ |
|||
// Always exclude null keys and values, as they can't be represented as valid claims.
|
|||
if (string.IsNullOrEmpty(parameter.Key) || OpenIddictParameter.IsNullOrEmpty(parameter.Value)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
// Exclude OpenIddict-specific private claims, that MUST NOT be set based on data returned
|
|||
// by the remote authorization server (that may or may not be an OpenIddict server).
|
|||
if (parameter.Key.StartsWith(Claims.Prefixes.Private, StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
switch ((name: parameter.Key, value: parameter.Value.Value)) |
|||
{ |
|||
// Ignore all protocol claims that are not mapped to CLR claims.
|
|||
case (Claims.Active, _): |
|||
case (Claims.Issuer, _): |
|||
case (Claims.NotBefore, _): |
|||
case (Claims.TokenType, _): |
|||
case (Claims.TokenUsage, _): |
|||
continue; |
|||
|
|||
// Claims represented as arrays are split and mapped to multiple CLR claims.
|
|||
case (var name, JsonElement value) when value.ValueKind == JsonValueKind.Array: |
|||
foreach (var element in value.EnumerateArray()) |
|||
{ |
|||
identity.AddClaim(new Claim(name, element.ToString(), GetClaimValueType(value.ValueKind))); |
|||
} |
|||
break; |
|||
|
|||
case (var name, JsonElement value): |
|||
identity.AddClaim(new Claim(name, value.ToString(), GetClaimValueType(value.ValueKind))); |
|||
break; |
|||
|
|||
// Note: in the typical case, the introspection parameters should be deserialized from
|
|||
// a JSON response and thus represented as System.Text.Json.JsonElement instances.
|
|||
// However, to support responses resolved from custom locations and parameters manually added
|
|||
// by the application using the events model, the CLR primitive types are also supported.
|
|||
|
|||
case (var name, bool value): |
|||
identity.AddClaim(new Claim(name, value.ToString(), ClaimValueTypes.Boolean)); |
|||
break; |
|||
|
|||
case (var name, long value): |
|||
identity.AddClaim(new Claim(name, value.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer64)); |
|||
break; |
|||
|
|||
case (var name, string value): |
|||
identity.AddClaim(new Claim(name, value, ClaimValueTypes.String)); |
|||
break; |
|||
|
|||
// Claims represented as arrays are split and mapped to multiple CLR claims.
|
|||
case (var name, string[] value): |
|||
for (var index = 0; index < value.Length; index++) |
|||
{ |
|||
identity.AddClaim(new Claim(name, value[index], ClaimValueTypes.String)); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
|
|||
context.Principal = new ClaimsPrincipal(identity); |
|||
|
|||
return default; |
|||
|
|||
static string GetClaimValueType(JsonValueKind kind) => kind switch |
|||
{ |
|||
JsonValueKind.True => ClaimValueTypes.Boolean, |
|||
JsonValueKind.False => ClaimValueTypes.Boolean, |
|||
JsonValueKind.String => ClaimValueTypes.String, |
|||
JsonValueKind.Number => ClaimValueTypes.Integer64, |
|||
|
|||
JsonValueKind.Array => JsonClaimValueTypes.JsonArray, |
|||
JsonValueKind.Object => JsonClaimValueTypes.Json, |
|||
|
|||
_ => JsonClaimValueTypes.Json |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.IdentityModel.Protocols; |
|||
using Microsoft.IdentityModel.Protocols.OpenIdConnect; |
|||
|
|||
namespace OpenIddict.Validation |
|||
{ |
|||
public class OpenIddictValidationRetriever : IConfigurationRetriever<OpenIdConnectConfiguration> |
|||
{ |
|||
private readonly OpenIddictValidationService _service; |
|||
|
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="OpenIddictValidationRetriever"/> class.
|
|||
/// </summary>
|
|||
/// <param name="service">The validation service.</param>
|
|||
public OpenIddictValidationRetriever([NotNull] OpenIddictValidationService service) |
|||
=> _service = service; |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the OpenID Connect server configuration from the specified address.
|
|||
/// </summary>
|
|||
/// <param name="address">The address of the remote metadata endpoint.</param>
|
|||
/// <param name="retriever">The retriever used by IdentityModel.</param>
|
|||
/// <param name="cancel">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>The OpenID Connect server configuration retrieved from the remote server.</returns>
|
|||
async Task<OpenIdConnectConfiguration> IConfigurationRetriever<OpenIdConnectConfiguration>.GetConfigurationAsync(string address, IDocumentRetriever retriever, CancellationToken cancel) |
|||
{ |
|||
if (string.IsNullOrEmpty(address)) |
|||
{ |
|||
throw new ArgumentException("The address cannot be null or empty.", nameof(address)); |
|||
} |
|||
|
|||
if (!Uri.TryCreate(address, UriKind.Absolute, out Uri uri) || !uri.IsWellFormedOriginalString()) |
|||
{ |
|||
throw new ArgumentException("The address must be a valid absolute URI.", nameof(address)); |
|||
} |
|||
|
|||
cancel.ThrowIfCancellationRequested(); |
|||
|
|||
var configuration = await _service.GetConfigurationAsync(uri, cancel) ?? |
|||
throw new InvalidOperationException("The server configuration couldn't be retrieved."); |
|||
|
|||
if (!Uri.TryCreate(configuration.JwksUri, UriKind.Absolute, out uri) || !uri.IsWellFormedOriginalString()) |
|||
{ |
|||
throw new InvalidOperationException("The JWKS URI couldn't be resolved from the provider metadata."); |
|||
} |
|||
|
|||
configuration.JsonWebKeySet = await _service.GetSecurityKeysAsync(uri, cancel) ?? |
|||
throw new InvalidOperationException("The server JSON Web Key set couldn't be retrieved."); |
|||
|
|||
// Copy the signing keys found in the JSON Web Key Set to the SigningKeys collection.
|
|||
foreach (var key in configuration.JsonWebKeySet.GetSigningKeys()) |
|||
{ |
|||
configuration.SigningKeys.Add(key); |
|||
} |
|||
|
|||
return configuration; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,493 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System; |
|||
using System.Security.Claims; |
|||
using System.Text; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.IdentityModel.Protocols.OpenIdConnect; |
|||
using Microsoft.IdentityModel.Tokens; |
|||
using OpenIddict.Abstractions; |
|||
using static OpenIddict.Validation.OpenIddictValidationEvents; |
|||
|
|||
namespace OpenIddict.Validation |
|||
{ |
|||
public class OpenIddictValidationService |
|||
{ |
|||
private readonly IServiceProvider _provider; |
|||
|
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="OpenIddictValidationService"/> class.
|
|||
/// </summary>
|
|||
/// <param name="provider">The service provider.</param>
|
|||
public OpenIddictValidationService([NotNull] IServiceProvider provider) |
|||
=> _provider = provider; |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the OpenID Connect server configuration from the specified address.
|
|||
/// </summary>
|
|||
/// <param name="address">The address of the remote metadata endpoint.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>The OpenID Connect server configuration retrieved from the remote server.</returns>
|
|||
public async ValueTask<OpenIdConnectConfiguration> GetConfigurationAsync( |
|||
[NotNull] Uri address, CancellationToken cancellationToken = default) |
|||
{ |
|||
if (address == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(address)); |
|||
} |
|||
|
|||
if (!address.IsAbsoluteUri) |
|||
{ |
|||
throw new ArgumentException("The address must be an absolute URI.", nameof(address)); |
|||
} |
|||
|
|||
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 provider = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationProvider>(); |
|||
var transaction = await provider.CreateTransactionAsync(); |
|||
|
|||
var request = new OpenIddictRequest(); |
|||
request = await PrepareConfigurationRequestAsync(); |
|||
request = await ApplyConfigurationRequestAsync(); |
|||
var response = await ExtractConfigurationResponseAsync(); |
|||
|
|||
var configuration = await HandleConfigurationResponseAsync(); |
|||
if (configuration == null) |
|||
{ |
|||
throw new InvalidOperationException("The OpenID Connect server configuration couldn't be retrieved."); |
|||
} |
|||
|
|||
return configuration; |
|||
|
|||
async ValueTask<OpenIddictRequest> PrepareConfigurationRequestAsync() |
|||
{ |
|||
var context = new PrepareConfigurationRequestContext(transaction) |
|||
{ |
|||
Address = address, |
|||
Request = request |
|||
}; |
|||
|
|||
await provider.DispatchAsync(context); |
|||
|
|||
if (context.IsRejected) |
|||
{ |
|||
var message = new StringBuilder() |
|||
.AppendLine("An error occurred while preparing the configuration request.") |
|||
.AppendFormat("Error: {0}", context.Error ?? "(not available)") |
|||
.AppendFormat("Error description: {0}", context.ErrorDescription ?? "(not available)") |
|||
.AppendFormat("Error URI: {0}", context.ErrorUri ?? "(not available)") |
|||
.ToString(); |
|||
|
|||
throw new OpenIddictExceptions.GenericException(message, |
|||
context.Error, context.ErrorDescription, context.ErrorUri); |
|||
} |
|||
|
|||
return context.Request; |
|||
} |
|||
|
|||
async ValueTask<OpenIddictRequest> ApplyConfigurationRequestAsync() |
|||
{ |
|||
var context = new ApplyConfigurationRequestContext(transaction) |
|||
{ |
|||
Request = request |
|||
}; |
|||
|
|||
await provider.DispatchAsync(context); |
|||
|
|||
if (context.IsRejected) |
|||
{ |
|||
var message = new StringBuilder() |
|||
.AppendLine("An error occurred while sending the configuration request.") |
|||
.AppendFormat("Error: {0}", context.Error ?? "(not available)") |
|||
.AppendFormat("Error description: {0}", context.ErrorDescription ?? "(not available)") |
|||
.AppendFormat("Error URI: {0}", context.ErrorUri ?? "(not available)") |
|||
.ToString(); |
|||
|
|||
throw new OpenIddictExceptions.GenericException(message, |
|||
context.Error, context.ErrorDescription, context.ErrorUri); |
|||
} |
|||
|
|||
return context.Request; |
|||
} |
|||
|
|||
async ValueTask<OpenIddictResponse> ExtractConfigurationResponseAsync() |
|||
{ |
|||
var context = new ExtractConfigurationResponseContext(transaction) |
|||
{ |
|||
Request = request |
|||
}; |
|||
|
|||
await provider.DispatchAsync(context); |
|||
|
|||
if (context.IsRejected) |
|||
{ |
|||
var message = new StringBuilder() |
|||
.AppendLine("An error occurred while extracting the configuration response.") |
|||
.AppendFormat("Error: {0}", context.Error ?? "(not available)") |
|||
.AppendFormat("Error description: {0}", context.ErrorDescription ?? "(not available)") |
|||
.AppendFormat("Error URI: {0}", context.ErrorUri ?? "(not available)") |
|||
.ToString(); |
|||
|
|||
throw new OpenIddictExceptions.GenericException(message, |
|||
context.Error, context.ErrorDescription, context.ErrorUri); |
|||
} |
|||
|
|||
return context.Response; |
|||
} |
|||
|
|||
async ValueTask<OpenIdConnectConfiguration> HandleConfigurationResponseAsync() |
|||
{ |
|||
var context = new HandleConfigurationResponseContext(transaction) |
|||
{ |
|||
Request = request, |
|||
Response = response |
|||
}; |
|||
|
|||
await provider.DispatchAsync(context); |
|||
|
|||
if (context.IsRejected) |
|||
{ |
|||
var message = new StringBuilder() |
|||
.AppendLine("An error occurred while handling the configuration response.") |
|||
.AppendFormat("Error: {0}", context.Error ?? "(not available)") |
|||
.AppendFormat("Error description: {0}", context.ErrorDescription ?? "(not available)") |
|||
.AppendFormat("Error URI: {0}", context.ErrorUri ?? "(not available)") |
|||
.ToString(); |
|||
|
|||
throw new OpenIddictExceptions.GenericException(message, |
|||
context.Error, context.ErrorDescription, context.ErrorUri); |
|||
} |
|||
|
|||
return context.Configuration; |
|||
} |
|||
} |
|||
|
|||
finally |
|||
{ |
|||
if (scope is IAsyncDisposable disposable) |
|||
{ |
|||
await disposable.DisposeAsync(); |
|||
} |
|||
|
|||
else |
|||
{ |
|||
scope.Dispose(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Retrieves the security keys exposed by the specified JWKS endpoint.
|
|||
/// </summary>
|
|||
/// <param name="address">The address of the remote metadata endpoint.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>The security keys retrieved from the remote server.</returns>
|
|||
public async ValueTask<JsonWebKeySet> GetSecurityKeysAsync( |
|||
[NotNull] Uri address, CancellationToken cancellationToken = default) |
|||
{ |
|||
if (address == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(address)); |
|||
} |
|||
|
|||
if (!address.IsAbsoluteUri) |
|||
{ |
|||
throw new ArgumentException("The address must be an absolute URI.", nameof(address)); |
|||
} |
|||
|
|||
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 provider = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationProvider>(); |
|||
var transaction = await provider.CreateTransactionAsync(); |
|||
|
|||
var request = new OpenIddictRequest(); |
|||
request = await PrepareCryptographyRequestAsync(); |
|||
request = await ApplyCryptographyRequestAsync(); |
|||
|
|||
var response = await ExtractCryptographyResponseAsync(); |
|||
|
|||
var keys = await HandleCryptographyResponseAsync(); |
|||
if (keys == null) |
|||
{ |
|||
throw new InvalidOperationException("An unknown error occurred while retrieving the JWK set."); |
|||
} |
|||
|
|||
return keys; |
|||
|
|||
async ValueTask<OpenIddictRequest> PrepareCryptographyRequestAsync() |
|||
{ |
|||
var context = new PrepareCryptographyRequestContext(transaction) |
|||
{ |
|||
Address = address, |
|||
Request = request |
|||
}; |
|||
|
|||
await provider.DispatchAsync(context); |
|||
|
|||
if (context.IsRejected) |
|||
{ |
|||
var message = new StringBuilder() |
|||
.AppendLine("An error occurred while preparing the cryptography request.") |
|||
.AppendFormat("Error: {0}", context.Error ?? "(not available)") |
|||
.AppendFormat("Error description: {0}", context.ErrorDescription ?? "(not available)") |
|||
.AppendFormat("Error URI: {0}", context.ErrorUri ?? "(not available)") |
|||
.ToString(); |
|||
|
|||
throw new OpenIddictExceptions.GenericException(message, |
|||
context.Error, context.ErrorDescription, context.ErrorUri); |
|||
} |
|||
|
|||
return context.Request; |
|||
} |
|||
|
|||
async ValueTask<OpenIddictRequest> ApplyCryptographyRequestAsync() |
|||
{ |
|||
var context = new ApplyCryptographyRequestContext(transaction) |
|||
{ |
|||
Request = request |
|||
}; |
|||
|
|||
await provider.DispatchAsync(context); |
|||
|
|||
if (context.IsRejected) |
|||
{ |
|||
var message = new StringBuilder() |
|||
.AppendLine("An error occurred while sending the cryptography request.") |
|||
.AppendFormat("Error: {0}", context.Error ?? "(not available)") |
|||
.AppendFormat("Error description: {0}", context.ErrorDescription ?? "(not available)") |
|||
.AppendFormat("Error URI: {0}", context.ErrorUri ?? "(not available)") |
|||
.ToString(); |
|||
|
|||
throw new OpenIddictExceptions.GenericException(message, |
|||
context.Error, context.ErrorDescription, context.ErrorUri); |
|||
} |
|||
|
|||
return context.Request; |
|||
} |
|||
|
|||
async ValueTask<OpenIddictResponse> ExtractCryptographyResponseAsync() |
|||
{ |
|||
var context = new ExtractCryptographyResponseContext(transaction) |
|||
{ |
|||
Request = request |
|||
}; |
|||
|
|||
await provider.DispatchAsync(context); |
|||
|
|||
if (context.IsRejected) |
|||
{ |
|||
var message = new StringBuilder() |
|||
.AppendLine("An error occurred while extracting the cryptography response.") |
|||
.AppendFormat("Error: {0}", context.Error ?? "(not available)") |
|||
.AppendFormat("Error description: {0}", context.ErrorDescription ?? "(not available)") |
|||
.AppendFormat("Error URI: {0}", context.ErrorUri ?? "(not available)") |
|||
.ToString(); |
|||
|
|||
throw new OpenIddictExceptions.GenericException(message, |
|||
context.Error, context.ErrorDescription, context.ErrorUri); |
|||
} |
|||
|
|||
return context.Response; |
|||
} |
|||
|
|||
async ValueTask<JsonWebKeySet> HandleCryptographyResponseAsync() |
|||
{ |
|||
var context = new HandleCryptographyResponseContext(transaction) |
|||
{ |
|||
Request = request, |
|||
Response = response |
|||
}; |
|||
|
|||
await provider.DispatchAsync(context); |
|||
|
|||
if (context.IsRejected) |
|||
{ |
|||
var message = new StringBuilder() |
|||
.AppendLine("An error occurred while handling the cryptography response.") |
|||
.AppendFormat("Error: {0}", context.Error ?? "(not available)") |
|||
.AppendFormat("Error description: {0}", context.ErrorDescription ?? "(not available)") |
|||
.AppendFormat("Error URI: {0}", context.ErrorUri ?? "(not available)") |
|||
.ToString(); |
|||
|
|||
throw new OpenIddictExceptions.GenericException(message, |
|||
context.Error, context.ErrorDescription, context.ErrorUri); |
|||
} |
|||
|
|||
return context.SecurityKeys; |
|||
} |
|||
} |
|||
|
|||
finally |
|||
{ |
|||
if (scope is IAsyncDisposable disposable) |
|||
{ |
|||
await disposable.DisposeAsync(); |
|||
} |
|||
|
|||
else |
|||
{ |
|||
scope.Dispose(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sends an introspection request to the specified address and returns the corresponding principal.
|
|||
/// </summary>
|
|||
/// <param name="address">The address of the remote metadata endpoint.</param>
|
|||
/// <param name="token">The token to introspect.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>The claims principal created from the claim retrieved from the remote server.</returns>
|
|||
public ValueTask<ClaimsPrincipal> IntrospectTokenAsync( |
|||
[NotNull] Uri address, [NotNull] string token, CancellationToken cancellationToken = default) |
|||
=> IntrospectTokenAsync(address, token, type: null, cancellationToken); |
|||
|
|||
/// <summary>
|
|||
/// Sends an introspection request to the specified address and returns the corresponding principal.
|
|||
/// </summary>
|
|||
/// <param name="address">The address of the remote metadata endpoint.</param>
|
|||
/// <param name="token">The token to introspect.</param>
|
|||
/// <param name="type">The token type to introspect.</param>
|
|||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|||
/// <returns>The claims principal created from the claim retrieved from the remote server.</returns>
|
|||
public async ValueTask<ClaimsPrincipal> IntrospectTokenAsync( |
|||
[NotNull] Uri address, [NotNull] string token, |
|||
[CanBeNull] string type, CancellationToken cancellationToken = default) |
|||
{ |
|||
if (address == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(address)); |
|||
} |
|||
|
|||
if (!address.IsAbsoluteUri) |
|||
{ |
|||
throw new ArgumentException("The address must be an absolute URI.", nameof(address)); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(token)) |
|||
{ |
|||
throw new ArgumentException("The 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 provider = scope.ServiceProvider.GetRequiredService<IOpenIddictValidationProvider>(); |
|||
var transaction = await provider.CreateTransactionAsync(); |
|||
|
|||
var request = new OpenIddictRequest(); |
|||
request = await PrepareIntrospectionRequestAsync(); |
|||
request = await ApplyIntrospectionRequestAsync(); |
|||
var response = await ExtractIntrospectionResponseAsync(); |
|||
|
|||
var principal = await HandleIntrospectionResponseAsync(); |
|||
if (principal == null) |
|||
{ |
|||
throw new InvalidOperationException("An unknown error occurred while introspecting the token."); |
|||
} |
|||
|
|||
return principal; |
|||
|
|||
async ValueTask<OpenIddictRequest> PrepareIntrospectionRequestAsync() |
|||
{ |
|||
var context = new PrepareIntrospectionRequestContext(transaction) |
|||
{ |
|||
Address = address, |
|||
Request = request, |
|||
Token = token, |
|||
TokenType = type |
|||
}; |
|||
|
|||
await provider.DispatchAsync(context); |
|||
|
|||
return context.Request; |
|||
} |
|||
|
|||
async ValueTask<OpenIddictRequest> ApplyIntrospectionRequestAsync() |
|||
{ |
|||
var context = new ApplyIntrospectionRequestContext(transaction) |
|||
{ |
|||
Request = request |
|||
}; |
|||
|
|||
await provider.DispatchAsync(context); |
|||
|
|||
return context.Request; |
|||
} |
|||
|
|||
async ValueTask<OpenIddictResponse> ExtractIntrospectionResponseAsync() |
|||
{ |
|||
var context = new ExtractIntrospectionResponseContext(transaction) |
|||
{ |
|||
Request = request |
|||
}; |
|||
|
|||
await provider.DispatchAsync(context); |
|||
|
|||
return context.Response; |
|||
} |
|||
|
|||
async ValueTask<ClaimsPrincipal> HandleIntrospectionResponseAsync() |
|||
{ |
|||
var context = new HandleIntrospectionResponseContext(transaction) |
|||
{ |
|||
Request = request, |
|||
Response = response, |
|||
Token = token, |
|||
TokenType = type |
|||
}; |
|||
|
|||
await provider.DispatchAsync(context); |
|||
|
|||
return context.Principal; |
|||
} |
|||
} |
|||
|
|||
finally |
|||
{ |
|||
if (scope is IAsyncDisposable disposable) |
|||
{ |
|||
await disposable.DisposeAsync(); |
|||
} |
|||
|
|||
else |
|||
{ |
|||
scope.Dispose(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
namespace OpenIddict.Validation |
|||
{ |
|||
/// <summary>
|
|||
/// Represents the type of validation performed by the OpenIddict validation services.
|
|||
/// </summary>
|
|||
public enum OpenIddictValidationType |
|||
{ |
|||
/// <summary>
|
|||
/// Configures the OpenIddict validation services to use direct validation.
|
|||
/// By default, direct validation uses IdentityModel to validate JWT tokens,
|
|||
/// but a different token format can be used by registering the corresponding
|
|||
/// package (e.g OpenIddict.Validation.DataProtection, for Data Protection tokens).
|
|||
/// </summary>
|
|||
Direct = 0, |
|||
|
|||
/// <summary>
|
|||
/// Configures the OpenIddict validation services to use introspection.
|
|||
/// When using introspection, an OAuth 2.0 introspection request is sent
|
|||
/// to the authorization server to validate the received access token.
|
|||
/// </summary>
|
|||
Introspection = 1 |
|||
} |
|||
} |
|||
Loading…
Reference in new issue