예제 #1
0
 /// <summary>
 /// Invoked when a protocol message is first received.
 /// </summary>
 public virtual Task MessageReceived(MessageReceivedContext context) => OnMessageReceived(context);
예제 #2
0
        /// <summary>
        /// Invoked to process incoming authentication messages.
        /// </summary>
        /// <returns></returns>
        protected override async Task <HandleRequestResult> HandleRemoteAuthenticateAsync()
        {
            WsFederationMessage?     wsFederationMessage = null;
            AuthenticationProperties?properties          = null;

            // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small.
            if (HttpMethods.IsPost(Request.Method) &&
                !string.IsNullOrEmpty(Request.ContentType)
                // May have media/type; charset=utf-8, allow partial match.
                && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) &&
                Request.Body.CanRead)
            {
                var form = await Request.ReadFormAsync(Context.RequestAborted);

                // ToArray handles the StringValues.IsNullOrEmpty case. We assume non-empty Value does not contain null elements.
#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
                wsFederationMessage = new WsFederationMessage(form.Select(pair => new KeyValuePair <string, string[]>(pair.Key, pair.Value.ToArray())));
#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
            }

            if (wsFederationMessage == null || !wsFederationMessage.IsSignInMessage)
            {
                if (Options.SkipUnrecognizedRequests)
                {
                    // Not for us?
                    return(HandleRequestResult.SkipHandler());
                }

                return(HandleRequestResult.Fail("No message."));
            }

            try
            {
                // Retrieve our cached redirect uri
                var state = wsFederationMessage.Wctx;
                // WsFed allows for uninitiated logins, state may be missing. See AllowUnsolicitedLogins.
                properties = Options.StateDataFormat.Unprotect(state);

                if (properties == null)
                {
                    if (!Options.AllowUnsolicitedLogins)
                    {
                        return(HandleRequestResult.Fail("Unsolicited logins are not allowed."));
                    }
                }
                else
                {
                    // Extract the user state from properties and reset.
                    properties.Items.TryGetValue(WsFederationDefaults.UserstatePropertiesKey, out var userState);
                    wsFederationMessage.Wctx = userState;
                }

                var messageReceivedContext = new MessageReceivedContext(Context, Scheme, Options, properties)
                {
                    ProtocolMessage = wsFederationMessage
                };
                await Events.MessageReceived(messageReceivedContext);

                if (messageReceivedContext.Result != null)
                {
                    return(messageReceivedContext.Result);
                }
                wsFederationMessage = messageReceivedContext.ProtocolMessage;
                properties          = messageReceivedContext.Properties !; // Provides a new instance if not set.

                // If state did flow from the challenge then validate it. See AllowUnsolicitedLogins above.
                if (properties.Items.TryGetValue(CorrelationProperty, out string?correlationId) &&
                    !ValidateCorrelationId(properties))
                {
                    return(HandleRequestResult.Fail("Correlation failed.", properties));
                }

                if (wsFederationMessage.Wresult == null)
                {
                    Logger.SignInWithoutWResult();
                    return(HandleRequestResult.Fail(Resources.SignInMessageWresultIsMissing, properties));
                }

                var token = wsFederationMessage.GetToken();
                if (string.IsNullOrEmpty(token))
                {
                    Logger.SignInWithoutToken();
                    return(HandleRequestResult.Fail(Resources.SignInMessageTokenIsMissing, properties));
                }

                var securityTokenReceivedContext = new SecurityTokenReceivedContext(Context, Scheme, Options, properties)
                {
                    ProtocolMessage = wsFederationMessage
                };
                await Events.SecurityTokenReceived(securityTokenReceivedContext);

                if (securityTokenReceivedContext.Result != null)
                {
                    return(securityTokenReceivedContext.Result);
                }
                wsFederationMessage = securityTokenReceivedContext.ProtocolMessage;
                properties          = messageReceivedContext.Properties !;

                if (_configuration == null)
                {
                    _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
                }

                // Copy and augment to avoid cross request race conditions for updated configurations.
                var tvp     = Options.TokenValidationParameters.Clone();
                var issuers = new[] { _configuration.Issuer };
                tvp.ValidIssuers      = (tvp.ValidIssuers == null ? issuers : tvp.ValidIssuers.Concat(issuers));
                tvp.IssuerSigningKeys = (tvp.IssuerSigningKeys == null ? _configuration.SigningKeys : tvp.IssuerSigningKeys.Concat(_configuration.SigningKeys));

                ClaimsPrincipal?principal   = null;
                SecurityToken?  parsedToken = null;
                foreach (var validator in Options.SecurityTokenHandlers)
                {
                    if (validator.CanReadToken(token))
                    {
                        principal = validator.ValidateToken(token, tvp, out parsedToken);
                        break;
                    }
                }

                if (principal == null)
                {
                    throw new SecurityTokenException(Resources.Exception_NoTokenValidatorFound);
                }

                if (Options.UseTokenLifetime && parsedToken != null)
                {
                    // Override any session persistence to match the token lifetime.
                    var issued = parsedToken.ValidFrom;
                    if (issued != DateTime.MinValue)
                    {
                        properties.IssuedUtc = issued.ToUniversalTime();
                    }
                    var expires = parsedToken.ValidTo;
                    if (expires != DateTime.MinValue)
                    {
                        properties.ExpiresUtc = expires.ToUniversalTime();
                    }
                    properties.AllowRefresh = false;
                }

                var securityTokenValidatedContext = new SecurityTokenValidatedContext(Context, Scheme, Options, principal, properties)
                {
                    ProtocolMessage = wsFederationMessage,
                    SecurityToken   = parsedToken,
                };

                await Events.SecurityTokenValidated(securityTokenValidatedContext);

                if (securityTokenValidatedContext.Result != null)
                {
                    return(securityTokenValidatedContext.Result);
                }

                // Flow possible changes
                principal  = securityTokenValidatedContext.Principal !;
                properties = securityTokenValidatedContext.Properties;

                return(HandleRequestResult.Success(new AuthenticationTicket(principal, properties, Scheme.Name)));
            }
            catch (Exception exception)
            {
                Logger.ExceptionProcessingMessage(exception);

                // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification.
                if (Options.RefreshOnIssuerKeyNotFound && exception is SecurityTokenSignatureKeyNotFoundException)
                {
                    Options.ConfigurationManager.RequestRefresh();
                }

                var authenticationFailedContext = new AuthenticationFailedContext(Context, Scheme, Options)
                {
                    ProtocolMessage = wsFederationMessage,
                    Exception       = exception
                };
                await Events.AuthenticationFailed(authenticationFailedContext);

                if (authenticationFailedContext.Result != null)
                {
                    return(authenticationFailedContext.Result);
                }

                return(HandleRequestResult.Fail(exception, properties));
            }
        }
예제 #3
0
        /// <summary>
        /// Invoked to process incoming authentication messages.
        /// </summary>
        /// <returns></returns>
        protected override async Task <HandleRequestResult> HandleRemoteAuthenticateAsync()
        {
            WsFederationMessage wsFederationMessage = null;

            // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small.
            if (HttpMethods.IsPost(Request.Method) &&
                !string.IsNullOrWhiteSpace(Request.ContentType)
                // May have media/type; charset=utf-8, allow partial match.
                && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) &&
                Request.Body.CanRead)
            {
                var form = await Request.ReadFormAsync();

                wsFederationMessage = new WsFederationMessage(form.Select(pair => new KeyValuePair <string, string[]>(pair.Key, pair.Value)));
            }

            if (wsFederationMessage == null || !wsFederationMessage.IsSignInMessage)
            {
                if (Options.SkipUnrecognizedRequests)
                {
                    // Not for us?
                    return(HandleRequestResult.SkipHandler());
                }

                return(HandleRequestResult.Fail("No message."));
            }

            try
            {
                // Retrieve our cached redirect uri
                string state = wsFederationMessage.Wctx;
                // WsFed allows for uninitiated logins, state may be missing.
                var properties = GetPropertiesFromWctx(state);

                var messageReceivedContext = new MessageReceivedContext(Context, Scheme, Options, properties)
                {
                    ProtocolMessage = wsFederationMessage
                };
                await Options.Events.MessageReceived(messageReceivedContext);

                if (messageReceivedContext.Result != null)
                {
                    return(messageReceivedContext.Result);
                }

                if (wsFederationMessage.Wresult == null)
                {
                    Logger.LogWarning("Received a sign-in message without a WResult.");
                    return((HandleRequestResult)HandleRequestResult.NoResult());
                }

                string token = wsFederationMessage.GetToken();
                if (string.IsNullOrWhiteSpace(token))
                {
                    Logger.LogWarning("Received a sign-in message without a token.");
                    return((HandleRequestResult)HandleRequestResult.NoResult());
                }

                var securityTokenReceivedContext = new SecurityTokenReceivedContext(Context, Scheme, Options, properties)
                {
                    ProtocolMessage = wsFederationMessage
                };
                await Options.Events.SecurityTokenReceived(securityTokenReceivedContext);

                if (securityTokenReceivedContext.Result != null)
                {
                    return(securityTokenReceivedContext.Result);
                }

                if (_configuration == null)
                {
                    _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
                }

                // Copy and augment to avoid cross request race conditions for updated configurations.
                var tvp = Options.TokenValidationParameters.Clone();
                IEnumerable <string> issuers = new[] { _configuration.Issuer };
                tvp.ValidIssuers      = (tvp.ValidIssuers == null ? issuers : tvp.ValidIssuers.Concat(issuers));
                tvp.IssuerSigningKeys = (tvp.IssuerSigningKeys == null ? _configuration.SigningKeys : tvp.IssuerSigningKeys.Concat(_configuration.SigningKeys));

                ClaimsPrincipal principal   = null;
                SecurityToken   parsedToken = null;
                foreach (var validator in Options.SecurityTokenHandlers)
                {
                    if (validator.CanReadToken(token))
                    {
                        principal = validator.ValidateToken(token, tvp, out parsedToken);
                        break;
                    }
                }

                if (principal == null)
                {
                    throw new SecurityTokenException("no validator found");
                }

                if (Options.UseTokenLifetime)
                {
                    // Override any session persistence to match the token lifetime.
                    DateTime issued = parsedToken.ValidFrom;
                    if (issued != DateTime.MinValue)
                    {
                        properties.IssuedUtc = issued.ToUniversalTime();
                    }
                    DateTime expires = parsedToken.ValidTo;
                    if (expires != DateTime.MinValue)
                    {
                        properties.ExpiresUtc = expires.ToUniversalTime();
                    }
                    properties.AllowRefresh = false;
                }

                var securityTokenValidatedContext = new SecurityTokenValidatedContext(Context, Scheme, Options, principal, properties)
                {
                    ProtocolMessage = wsFederationMessage,
                };

                await Options.Events.SecurityTokenValidated(securityTokenValidatedContext);

                if (securityTokenValidatedContext.Result != null)
                {
                    return(securityTokenValidatedContext.Result);
                }

                // Flow possible changes
                principal  = securityTokenValidatedContext.Principal;
                properties = securityTokenValidatedContext.Properties;

                return(HandleRequestResult.Success(new AuthenticationTicket(principal, properties, Scheme.Name)));
            }
            catch (Exception exception)
            {
                Logger.LogError("Exception occurred while processing message: ", exception);

                // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification.
                if (Options.RefreshOnIssuerKeyNotFound && exception.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
                {
                    Options.ConfigurationManager.RequestRefresh();
                }

                var authenticationFailedContext = new AuthenticationFailedContext(Context, Scheme, Options)
                {
                    ProtocolMessage = wsFederationMessage,
                    Exception       = exception
                };
                await Options.Events.AuthenticationFailed(authenticationFailedContext);

                if (authenticationFailedContext.Result != null)
                {
                    return(authenticationFailedContext.Result);
                }

                throw;
            }
        }