/// <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;
        }
示例#2
0
 /// <inheritdoc/>
 public ValueTask HandleAsync(ValidateTokenContext context)
 {
     // If a principal was already attached, don't overwrite it.
     if (context.Principal is not null)
     {
         return(default);
        public async Task <FinishSsoResult> FinishSsoAsync(HttpContext context)
        {
            if (!TryGetSamlResponse(context, out var response, out var binding))
            {
                throw new InvalidOperationException("Bad request.");
            }

            Logger.LogInformation("Finishing SAML2P authentication (SP flow).");
            Trace($"Received SAMLResponse using {binding} binding.", response);
            var partnerId = response.Issuer;
            var partner   = await Partners.GetIdentityProviderAsync(partnerId);

            if (partner == null)
            {
                throw new SecurityException($"Partner idp '{partnerId}' not found.");
            }

            if (!partner.Enabled)
            {
                throw new SecurityException($"Partner idp '{partnerId}' is disabled.");
            }

            var request = null as AuthnRequest;

            if (response.InResponseTo != null)
            {
                request = await Cache.FetchRequestAsync(response.InResponseTo);

                await Cache.RemoveAsync(response.InResponseTo);
            }

            if (request == null && !partner.CanInitiateSso)
            {
                throw new SecurityException($"Partner idp '{partnerId}' is is not allowed to initiate SSO.");
            }

            if (request != null)
            {
                Trace("Found cached SAMLRequest.", request);

                if (request.RelayState != response.RelayState)
                {
                    throw new SecurityException($"Mismatching relay state.");
                }
            }

            var ssoContext = new FinishSsoContext
            {
                PartnerId = partner.Id,
                Partner   = partner,
                Request   = request,
                Response  = response
            };

            await Events.InvokeAsync(Options, partner, e => e.OnFinishSso(context.RequestServices, ssoContext));

            if (response.Status.StatusCode.Value != Saml2pConstants.Statuses.Success)
            {
                return(FinishSsoResult.Fail(partner.Id, response.Status.StatusCode.Value, response.Status.StatusCode?.SubCode.Value));
            }
            var parameters      = _factory.Create(partner);
            var validateContext = new ValidateTokenContext
            {
                PartnerId = partner.Id,
                Partner   = partner,
                Request   = request,
                Response  = response,
                TokenValidationParameters = parameters,
                Handler = _handler
            };
            await Events.InvokeAsync(Options, partner, e => e.OnValidatingToken(context.RequestServices, validateContext));

            if (validateContext.Subject != null && validateContext.SecurityToken == null ||
                validateContext.Subject == null && validateContext.SecurityToken != null)
            {
                Logger.LogWarning($"When manually populating '{nameof(ValidateTokenContext.Subject)}' or '{nameof(ValidateTokenContext.SecurityToken)}' properties of '{nameof(ValidateTokenContext)}', then both must be populated. Otherwise they will be ignored. Clearing values...");
                validateContext.SecurityToken = null;
                validateContext.Subject       = null;
            }

            if (validateContext.Subject == null)
            {
                Logger.LogInformation("Validating incoming token.");
                var subject = validateContext.Handler.ValidateToken(validateContext.Response.XmlSecurityToken, validateContext.TokenValidationParameters, out var token);
                var saml2   = token as Saml2SecurityToken;
                var now     = _clock.UtcNow.DateTime;

                saml2.ValidateResponseToken(validateContext.Request.Id, now);

                validateContext.Subject       = subject;
                validateContext.SecurityToken = saml2;
            }

            await Events.InvokeAsync(Options, partner, e => e.OnValidatedToken(context.RequestServices, validateContext));

            context.User = validateContext.Subject;

            return(FinishSsoResult.Success(partner.Id, validateContext.SecurityToken, validateContext.Subject));
        }
示例#4
0
        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            if (!Options.UseReferenceTokens)
            {
                return await base.HandleAuthenticateAsync();
            }

            var context = new RetrieveTokenContext(Context, Scheme, Options);
            await Events.RetrieveToken(context);

            if (context.Result != null)
            {
                Logger.LogInformation("The default authentication handling was skipped from user code.");

                return context.Result;
            }

            var token = context.Token;

            if (string.IsNullOrEmpty(token))
            {
                // Try to retrieve the access token from the authorization header.
                string header = Request.Headers[HeaderNames.Authorization];
                if (string.IsNullOrEmpty(header))
                {
                    Logger.LogDebug("Authentication was skipped because no bearer token was received.");

                    return AuthenticateResult.NoResult();
                }

                // Ensure that the authorization header contains the mandatory "Bearer" scheme.
                // See https://tools.ietf.org/html/rfc6750#section-2.1
                if (!header.StartsWith(OAuthValidationConstants.Schemes.Bearer + ' ', StringComparison.OrdinalIgnoreCase))
                {
                    Logger.LogDebug("Authentication was skipped because an incompatible " +
                                    "scheme was used in the 'Authorization' header.");

                    return AuthenticateResult.NoResult();
                }

                // Extract the token from the authorization header.
                token = header.Substring(OAuthValidationConstants.Schemes.Bearer.Length + 1).Trim();

                if (string.IsNullOrEmpty(token))
                {
                    Logger.LogDebug("Authentication was skipped because the bearer token " +
                                    "was missing from the 'Authorization' header.");

                    return AuthenticateResult.NoResult();
                }
            }

            // Try to unprotect the token and return an error
            // if the ticket can't be decrypted or validated.
            var result = await CreateTicketAsync(token);
            if (!result.Succeeded)
            {
                Context.Features.Set(new OAuthValidationFeature
                {
                    Error = new OAuthValidationError
                    {
                        Error = OAuthValidationConstants.Errors.InvalidToken,
                        ErrorDescription = "The access token is not valid."
                    }
                });

                return result;
            }

            // Ensure that the authentication ticket is still valid.
            var ticket = result.Ticket;
            if (ticket.Properties.ExpiresUtc.HasValue &&
                ticket.Properties.ExpiresUtc.Value < Options.SystemClock.UtcNow)
            {
                Context.Features.Set(new OAuthValidationFeature
                {
                    Error = new OAuthValidationError
                    {
                        Error = OAuthValidationConstants.Errors.InvalidToken,
                        ErrorDescription = "The access token is no longer valid."
                    }
                });

                return AuthenticateResult.Fail("Authentication failed because the access token was expired.");
            }

            // Ensure that the access token was issued
            // to be used with this resource server.
            if (!ValidateAudience(ticket))
            {
                Context.Features.Set(new OAuthValidationFeature
                {
                    Error = new OAuthValidationError
                    {
                        Error = OAuthValidationConstants.Errors.InvalidToken,
                        ErrorDescription = "The access token is not valid for this resource server."
                    }
                });

                return AuthenticateResult.Fail("Authentication failed because the access token " +
                                               "was not valid for this resource server.");
            }

            var notification = new ValidateTokenContext(Context, Scheme, Options, ticket);
            await Events.ValidateToken(notification);

            if (notification.Result != null)
            {
                Logger.LogInformation("The default authentication handling was skipped from user code.");

                return notification.Result;
            }

            // Optimization: avoid allocating a new AuthenticationTicket
            // if the principal/properties instances were not replaced.
            if (ReferenceEquals(notification.Principal, ticket.Principal) &&
                ReferenceEquals(notification.Properties, ticket.Properties))
            {
                return AuthenticateResult.Success(ticket);
            }

            return AuthenticateResult.Success(new AuthenticationTicket(
                notification.Principal, notification.Properties, Scheme.Name));
        }
 private ValueTask ValidatingToken(IServiceProvider arg1, ValidateTokenContext arg2)
 {
     return(new ValueTask());
 }
 /// <summary>
 /// Invoked when a token is to be validated, before final processing.
 /// </summary>
 public virtual Task ValidateToken(ValidateTokenContext context) => OnValidateToken(context);