Exemplo n.º 1
0
 /// <summary>
 /// Invoked whenever OpenID successfully authenticates a user
 /// </summary>
 /// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
 /// <returns>A <see cref="Task"/> representing the completed operation.</returns>
 public virtual Task Authenticated(OpenIDAuthenticatedContext context)
 {
     return(OnAuthenticated(context));
 }
        protected override async Task <AuthenticationTicket> AuthenticateCoreAsync()
        {
            AuthenticationProperties properties = null;

            try
            {
                var query = Request.Query;

                properties = UnpackStateParameter(query);
                if (properties == null)
                {
                    Logger.WriteWarning("Invalid return state");
                    return(null);
                }

                // Anti-CSRF
                if (!ValidateCorrelationId(properties, Logger))
                {
                    return(new AuthenticationTicket(null, properties));
                }

                var message = await ParseRequestMessageAsync(query);

                var messageValidated = false;

                Property mode;
                if (!message.Properties.TryGetValue("mode.http://specs.openid.net/auth/2.0", out mode))
                {
                    Logger.WriteWarning("Missing mode parameter");
                    return(new AuthenticationTicket(null, properties));
                }

                if (string.Equals("cancel", mode.Value, StringComparison.Ordinal))
                {
                    Logger.WriteWarning("User cancelled signin request");
                    return(new AuthenticationTicket(null, properties));
                }

                if (string.Equals("id_res", mode.Value, StringComparison.Ordinal))
                {
                    mode.Value = "check_authentication";

                    var requestBody = new FormUrlEncodedContent(message.ToFormValues());

                    var response = await HTTPClient.PostAsync(Options.ProviderLoginUri, requestBody, Request.CallCancelled);

                    response.EnsureSuccessStatusCode();
                    var responseBody = await response.Content.ReadAsStringAsync();

                    var verifyBody = new Dictionary <string, string[]>();
                    foreach (var line in responseBody.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries))
                    {
                        var delimiter = line.IndexOf(':');
                        if (delimiter != -1)
                        {
                            verifyBody.Add("openid." + line.Substring(0, delimiter), new[] { line.Substring(delimiter + 1) });
                        }
                    }
                    var      verifyMessage = new Message(new ReadableStringCollection(verifyBody), false);
                    Property isValid;
                    if (verifyMessage.Properties.TryGetValue("is_valid.http://specs.openid.net/auth/2.0", out isValid))
                    {
                        messageValidated = string.Equals("true", isValid.Value, StringComparison.Ordinal);
                    }
                }

                // http://openid.net/specs/openid-authentication-2_0.html#verify_return_to
                // To verify that the "openid.return_to" URL matches the URL that is processing this assertion:
                // * The URL scheme, authority, and path MUST be the same between the two URLs.
                // * Any query parameters that are present in the "openid.return_to" URL MUST also
                //   be present with the same values in the URL of the HTTP request the RP received.
                if (messageValidated)
                {
                    // locate the required return_to parameter
                    string actualReturnTo;
                    if (!message.TryGetValue("return_to.http://specs.openid.net/auth/2.0", out actualReturnTo))
                    {
                        Logger.WriteWarning("openid.return_to parameter missing at return address");
                        messageValidated = false;
                    }
                    else
                    {
                        // create the expected return_to parameter based on the URL that is processing
                        // the assertion, plus exactly and only the the query string parameter (state)
                        // that this RP must have received
                        var expectedReturnTo = BuildReturnTo(GetStateParameter(query));

                        if (!string.Equals(actualReturnTo, expectedReturnTo, StringComparison.Ordinal))
                        {
                            Logger.WriteWarning("openid.return_to parameter not equal to expected value based on return address");
                            messageValidated = false;
                        }
                    }
                }

                // Allow protocol extensions to add custom message validation rules
                foreach (var protocolExtension in Options.ProtocolExtensions)
                {
                    if (!await protocolExtension.OnValidateMessageAsync(message))
                    {
                        messageValidated = false;
                    }
                }

                if (!messageValidated)
                {
                    return(new AuthenticationTicket(null, properties));
                }
                {
                    IDictionary <string, string> attributeExchangeProperties = new Dictionary <string, string>();
                    foreach (var typeProperty in message.Properties.Values)
                    {
                        if (typeProperty.Namespace != "http://openid.net/srv/ax/1.0" || !typeProperty.Name.StartsWith("type."))
                        {
                            continue;
                        }
                        var      name = "value." + typeProperty.Name.Substring("type.".Length) + "http://openid.net/srv/ax/1.0";
                        Property valueProperty;
                        if (message.Properties.TryGetValue(name, out valueProperty))
                        {
                            attributeExchangeProperties.Add(typeProperty.Value, valueProperty.Value);
                        }
                    }

                    var responseNamespaces = new object[]
                    {
                        new XAttribute(XNamespace.Xmlns + "openid", "http://specs.openid.net/auth/2.0"),
                        new XAttribute(XNamespace.Xmlns + "openid.ax", "http://openid.net/srv/ax/1.0")
                    };

                    var responseProperties = message.Properties
                                             .Where(p => p.Value.Namespace != null)
                                             .Select(p => (object)new XElement(XName.Get(p.Value.Name.Substring(0, p.Value.Name.Length - 1), p.Value.Namespace), p.Value.Value));

                    var responseMessage = new XElement("response", responseNamespaces.Concat(responseProperties).ToArray());

                    var identity  = new ClaimsIdentity(Options.AuthenticationType);
                    var claimedId = responseMessage.Element(XName.Get("claimed_id", "http://specs.openid.net/auth/2.0"));
                    if (claimedId != null)
                    {
                        identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, claimedId.Value, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
                    }

                    SetIdentityInformations(identity, claimedId.Value, attributeExchangeProperties);

                    var context = new OpenIDAuthenticatedContext(
                        Context,
                        identity,
                        properties,
                        responseMessage,
                        attributeExchangeProperties);


                    // Let protocol extensions to extract the results from the message
                    foreach (var protocolExtension in Options.ProtocolExtensions)
                    {
                        var result = await protocolExtension.OnExtractResultsAsync(identity, claimedId.Value, message);

                        context.ProtocolExtensionData[protocolExtension.GetType()] = result;
                    }

                    await Options.Provider.Authenticated(context);

                    return(new AuthenticationTicket(context.Identity, context.Properties));
                }
            }
            catch (Exception ex)
            {
                Logger.WriteError("Authentication failed", ex);
                return(new AuthenticationTicket(null, properties));
            }
        }
 /// <summary>
 /// Invoked whenever OpenID successfully authenticates a user
 /// </summary>
 /// <param name="context">Contains information about the login session as well as the user <see cref="System.Security.Claims.ClaimsIdentity"/>.</param>
 /// <returns>A <see cref="Task"/> representing the completed operation.</returns>
 public virtual Task Authenticated(OpenIDAuthenticatedContext context)
 {
     return OnAuthenticated(context);
 }