/// <inheritdoc/>
        public async ValueTask HandleAsync(ProcessAuthenticationContext context)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.AccessTokenPrincipal is not null)
            {
                return;
            }

            if (string.IsNullOrEmpty(context.AccessToken))
            {
                if (context.RequireAccessToken)
                {
                    context.Reject(
                        error: Errors.MissingToken,
                        description: SR.GetResourceString(SR.ID2000),
                        uri: SR.FormatID8000(SR.ID2000));

                    return;
                }

                return;
            }

            var notification = new ValidateTokenContext(context.Transaction)
            {
                Token = context.AccessToken,
                ValidTokenTypes = { TokenTypeHints.AccessToken }
            };

            await _dispatcher.DispatchAsync(notification);

            if (notification.IsRequestHandled)
            {
                context.HandleRequest();
                return;
            }

            else if (notification.IsRequestSkipped)
            {
                context.SkipRequest();
                return;
            }

            else if (notification.IsRejected)
            {
                context.Reject(
                    error: notification.Error ?? Errors.InvalidRequest,
                    description: notification.ErrorDescription,
                    uri: notification.ErrorUri);
                return;
            }

            context.AccessTokenPrincipal = notification.Principal;
        }
Ejemplo n.º 2
0
        protected override async Task <AuthenticationTicket> AuthenticateCoreAsync()
        {
            var transaction = Context.Get <OpenIddictValidationTransaction>(typeof(OpenIddictValidationTransaction).FullName) ??
                              throw new InvalidOperationException("An unknown error occurred while retrieving the OpenIddict validation context.");

            // Note: in many cases, the authentication token was already validated by the time this action is called
            // (generally later in the pipeline, when using the pass-through mode). To avoid having to re-validate it,
            // the authentication context is resolved from the transaction. If it's not available, a new one is created.
            var context = transaction.GetProperty <ProcessAuthenticationContext>(typeof(ProcessAuthenticationContext).FullName);

            if (context == null)
            {
                context = new ProcessAuthenticationContext(transaction);
                await _dispatcher.DispatchAsync(context);

                // Store the context object in the transaction so it can be later retrieved by handlers
                // that want to access the authentication result without triggering a new authentication flow.
                transaction.SetProperty(typeof(ProcessAuthenticationContext).FullName, context);
            }

            if (context.IsRequestHandled || context.IsRequestSkipped)
            {
                return(null);
            }

            else if (context.IsRejected)
            {
                // Note: the missing_token error is special-cased to indicate to Katana
                // that no authentication result could be produced due to the lack of token.
                // This also helps reducing the logging noise when no token is specified.
                if (string.Equals(context.Error, Errors.MissingToken, StringComparison.Ordinal))
                {
                    return(null);
                }

                var properties = new AuthenticationProperties(new Dictionary <string, string>
                {
                    [OpenIddictValidationOwinConstants.Properties.Error]            = context.Error,
                    [OpenIddictValidationOwinConstants.Properties.ErrorDescription] = context.ErrorDescription,
                    [OpenIddictValidationOwinConstants.Properties.ErrorUri]         = context.ErrorUri
                });

                return(new AuthenticationTicket(null, properties));
            }

            else
            {
                // Store the token to allow any OWIN/Katana component (e.g a controller)
                // to retrieve it (e.g to make an API request to another application).
                var properties = new AuthenticationProperties(new Dictionary <string, string>
                {
                    [context.TokenType] = context.Token
                });

                return(new AuthenticationTicket((ClaimsIdentity)context.Principal.Identity, properties));
            }
        }
            /// <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] ProcessAuthenticationContext context)
            {
                if (context == null)
                {
                    throw new ArgumentNullException(nameof(context));
                }

                // If a principal was already attached, don't overwrite it.
                if (context.Principal != null)
                {
                    return(default);
Ejemplo n.º 4
0
            public ValueTask HandleAsync([NotNull] ProcessAuthenticationContext context)
            {
                if (context == null)
                {
                    throw new ArgumentNullException(nameof(context));
                }

                // If token validation parameters were already attached, don't overwrite them.
                if (context.TokenValidationParameters != null)
                {
                    return(default);
Ejemplo n.º 5
0
        protected override async Task <AuthenticationTicket> AuthenticateCoreAsync()
        {
            var transaction = Context.Get <OpenIddictValidationTransaction>(typeof(OpenIddictValidationTransaction).FullName);

            if (transaction?.Request == null)
            {
                throw new InvalidOperationException("An identity cannot be extracted from this request.");
            }

            var context = new ProcessAuthenticationContext(transaction);
            await _provider.DispatchAsync(context);

            if (context.Principal == null || context.IsRequestHandled || context.IsRequestSkipped)
            {
                return(null);
            }

            else if (context.IsRejected)
            {
                _logger.LogError("An error occurred while authenticating the current request: {Error} ; {Description}",
                                 /* Error: */ context.Error ?? Errors.InvalidToken,
                                 /* Description: */ context.ErrorDescription);

                return(new AuthenticationTicket(identity: null, new AuthenticationProperties
                {
                    Dictionary =
                    {
                        [Parameters.Error] = context.Error,
                        [Parameters.ErrorDescription] = context.ErrorDescription,
                        [Parameters.ErrorUri] = context.ErrorUri
                    }
                }));
            }

            return(new AuthenticationTicket((ClaimsIdentity)context.Principal.Identity, new AuthenticationProperties()));
        }
            public async ValueTask HandleAsync([NotNull] ProcessAuthenticationContext context)
            {
                if (context == null)
                {
                    throw new ArgumentNullException(nameof(context));
                }

                // If a principal was already attached, don't overwrite it.
                if (context.Principal != null)
                {
                    return;
                }

                var identifier = context.EndpointType switch
                {
                    OpenIddictServerEndpointType.Introspection => context.Request.Token,
                    OpenIddictServerEndpointType.Revocation => context.Request.Token,

                    OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
                    => context.Request.Code,
                    OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
                    => context.Request.RefreshToken,

                    OpenIddictServerEndpointType.Userinfo => context.Request.AccessToken,

                    _ => null
                };

                // If the token cannot be validated, don't return an error to allow another handle to validate it.
                if (string.IsNullOrEmpty(identifier))
                {
                    return;
                }

                var token = await _tokenManager.FindByReferenceIdAsync(identifier);

                if (token == null || !await IsTokenTypeValidAsync(token))
                {
                    return;
                }

                var payload = await _tokenManager.GetPayloadAsync(token);

                if (string.IsNullOrEmpty(payload))
                {
                    throw new InvalidOperationException(new StringBuilder()
                                                        .AppendLine("The payload associated with a reference token cannot be retrieved.")
                                                        .Append("This may indicate that the token entry was corrupted.")
                                                        .ToString());
                }

                var principal = context.EndpointType switch
                {
                    OpenIddictServerEndpointType.Introspection => ValidateToken(payload, TokenUsages.AccessToken) ??
                    ValidateToken(payload, TokenUsages.RefreshToken) ??
                    ValidateToken(payload, TokenUsages.AuthorizationCode),

                    OpenIddictServerEndpointType.Revocation => ValidateToken(payload, TokenUsages.AccessToken) ??
                    ValidateToken(payload, TokenUsages.RefreshToken) ??
                    ValidateToken(payload, TokenUsages.AuthorizationCode),

                    OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
                    => ValidateToken(payload, TokenUsages.AuthorizationCode),

                    OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
                    => ValidateToken(payload, TokenUsages.RefreshToken),

                    OpenIddictServerEndpointType.Userinfo => ValidateToken(payload, TokenUsages.AccessToken),

                    _ => null
                };

                // If the token cannot be validated, don't return an error to allow another handle to validate it.
                if (principal == null)
                {
                    return;
                }

                // Attach the principal extracted from the authorization code to the parent event context
                // and restore the creation/expiration dates/identifiers from the token entry metadata.
                context.Principal = principal
                                    .SetCreationDate(await _tokenManager.GetCreationDateAsync(token))
                                    .SetExpirationDate(await _tokenManager.GetExpirationDateAsync(token))
                                    .SetInternalAuthorizationId(await _tokenManager.GetAuthorizationIdAsync(token))
                                    .SetInternalTokenId(await _tokenManager.GetIdAsync(token))
                                    .SetClaim(Claims.Private.TokenUsage, await _tokenManager.GetTypeAsync(token));

                context.Logger.LogTrace("The reference DP token '{Token}' was successfully validated and the following " +
                                        "claims could be extracted: {Claims}.", payload, context.Principal.Claims);

                ClaimsPrincipal ValidateToken(string token, string type)
                {
                    // Create a Data Protection protector using the provider registered in the options.
                    var protector = _options.CurrentValue.DataProtectionProvider.CreateProtector(
                        Purposes.Handlers.Server,
                        type switch
                    {
                        TokenUsages.AccessToken => Purposes.Formats.AccessToken,
                        TokenUsages.AuthorizationCode => Purposes.Formats.AuthorizationCode,
                        TokenUsages.RefreshToken => Purposes.Formats.RefreshToken,

                        _ => throw new InvalidOperationException("The specified token type is not supported.")
                    },
                        Purposes.Features.ReferenceTokens,
                        Purposes.Schemes.Server);

                    try
                    {
                        using var buffer = new MemoryStream(protector.Unprotect(Base64UrlEncoder.DecodeBytes(token)));
                        using var reader = new BinaryReader(buffer);

                        // 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.
                        return(_options.CurrentValue.Formatter.ReadToken(reader)?.SetClaim(Claims.Private.TokenUsage, type));
                    }

                    catch (Exception exception)
                    {
                        context.Logger.LogTrace(exception, "An exception occured while deserializing the token '{Token}'.", token);

                        return(null);
                    }
                }

                async ValueTask <bool> IsTokenTypeValidAsync(object token) => context.EndpointType switch
                {
                    // All types of tokens are accepted by the introspection and revocation endpoints.
                    OpenIddictServerEndpointType.Introspection => true,
                    OpenIddictServerEndpointType.Revocation => true,

                    OpenIddictServerEndpointType.Token => await _tokenManager.GetTypeAsync(token) switch
                    {
                        TokenUsages.AuthorizationCode when context.Request.IsAuthorizationCodeGrantType() => true,
                        TokenUsages.RefreshToken      when context.Request.IsRefreshTokenGrantType() => true,

                        _ => false
                    },
            public async ValueTask HandleAsync([NotNull] ProcessAuthenticationContext context)
            {
                if (context == null)
                {
                    throw new ArgumentNullException(nameof(context));
                }

                // If a principal was already attached, don't overwrite it.
                if (context.Principal != null)
                {
                    return;
                }

                // If the token cannot be validated, don't return an error to allow another handle to validate it.
                var identifier = context.Request.AccessToken;

                if (string.IsNullOrEmpty(identifier))
                {
                    return;
                }

                var token = await _tokenManager.FindByReferenceIdAsync(identifier);

                if (token == null || !string.Equals(await _tokenManager.GetTypeAsync(token),
                                                    TokenUsages.AccessToken, StringComparison.OrdinalIgnoreCase))
                {
                    return;
                }

                var payload = await _tokenManager.GetPayloadAsync(token);

                if (string.IsNullOrEmpty(payload))
                {
                    throw new InvalidOperationException(new StringBuilder()
                                                        .AppendLine("The payload associated with a reference token cannot be retrieved.")
                                                        .Append("This may indicate that the token entry was corrupted.")
                                                        .ToString());
                }

                // Create a Data Protection protector using the provider registered in the options.
                var protector = _options.CurrentValue.DataProtectionProvider.CreateProtector(
                    Purposes.Handlers.Server,
                    Purposes.Formats.AccessToken,
                    Purposes.Features.ReferenceTokens,
                    Purposes.Schemes.Server);

                ClaimsPrincipal principal = null;

                try
                {
                    using var buffer = new MemoryStream(protector.Unprotect(Base64UrlEncoder.DecodeBytes(payload)));
                    using var reader = new BinaryReader(buffer);

                    principal = _options.CurrentValue.Formatter.ReadToken(reader);
                }

                catch (Exception exception)
                {
                    context.Logger.LogTrace(exception, "An exception occured while deserializing a token.");
                }

                // If the token cannot be validated, don't return an error to allow another handle to validate it.
                if (principal == null)
                {
                    return;
                }

                // Attach the principal extracted from the authorization code to the parent event context
                // and restore the creation/expiration dates/identifiers from the token entry metadata.
                context.Principal = principal
                                    .SetCreationDate(await _tokenManager.GetCreationDateAsync(token))
                                    .SetExpirationDate(await _tokenManager.GetExpirationDateAsync(token))
                                    .SetInternalAuthorizationId(await _tokenManager.GetAuthorizationIdAsync(token))
                                    .SetInternalTokenId(await _tokenManager.GetIdAsync(token))
                                    .SetClaim(Claims.Private.TokenUsage, await _tokenManager.GetTypeAsync(token));
            }