public virtual Task Authenticated(GoogleAuthenticatedContext context)
 {
     return OnAuthenticated(context);
 }
Beispiel #2
0
 /// <summary>
 /// Invoked whenever Google succesfully 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(GoogleAuthenticatedContext context)
 {
     return(OnAuthenticated(context));
 }
Beispiel #3
0
        protected override async Task <AuthenticationTicket> AuthenticateCoreAsync()
        {
            AuthenticationProperties properties = null;

            try
            {
                IReadableStringCollection 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));
                }

                Message message = await ParseRequestMessageAsync(query);

                bool 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());
                    HttpResponseMessage response = await _httpClient.PostAsync("https://www.google.com/accounts/o8/ud", requestBody, Request.CallCancelled);

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

                    var verifyBody = new Dictionary <string, string[]>();
                    foreach (var line in responseBody.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries))
                    {
                        int 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), strict: false);
                    Property isValid;
                    if (verifyMessage.Properties.TryGetValue("is_valid.http://specs.openid.net/auth/2.0", out isValid))
                    {
                        if (string.Equals("true", isValid.Value, StringComparison.Ordinal))
                        {
                            messageValidated = true;
                        }
                        else
                        {
                            messageValidated = false;
                        }
                    }
                }

                // 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
                        string 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;
                        }
                    }
                }

                if (messageValidated)
                {
                    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."))
                        {
                            string   qname = "value." + typeProperty.Name.Substring("type.".Length) + "http://openid.net/srv/ax/1.0";
                            Property valueProperty;
                            if (message.Properties.TryGetValue(qname, 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")
                    };

                    IEnumerable <object> 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);
                    XElement 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));
                    }

                    string firstValue;
                    if (attributeExchangeProperties.TryGetValue("http://axschema.org/namePerson/first", out firstValue))
                    {
                        identity.AddClaim(new Claim(ClaimTypes.GivenName, firstValue, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
                    }
                    string lastValue;
                    if (attributeExchangeProperties.TryGetValue("http://axschema.org/namePerson/last", out lastValue))
                    {
                        identity.AddClaim(new Claim(ClaimTypes.Surname, lastValue, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
                    }
                    string nameValue;
                    if (attributeExchangeProperties.TryGetValue("http://axschema.org/namePerson", out nameValue))
                    {
                        identity.AddClaim(new Claim(ClaimTypes.Name, nameValue, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
                    }
                    else if (!string.IsNullOrEmpty(firstValue) && !string.IsNullOrEmpty(lastValue))
                    {
                        identity.AddClaim(new Claim(ClaimTypes.Name, firstValue + " " + lastValue, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
                    }
                    else if (!string.IsNullOrEmpty(firstValue))
                    {
                        identity.AddClaim(new Claim(ClaimTypes.Name, firstValue, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
                    }
                    else if (!string.IsNullOrEmpty(lastValue))
                    {
                        identity.AddClaim(new Claim(ClaimTypes.Name, lastValue, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
                    }
                    string emailValue;
                    if (attributeExchangeProperties.TryGetValue("http://axschema.org/contact/email", out emailValue))
                    {
                        identity.AddClaim(new Claim(ClaimTypes.Email, emailValue, "http://www.w3.org/2001/XMLSchema#string", Options.AuthenticationType));
                    }

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

                    await Options.Provider.Authenticated(context);

                    return(new AuthenticationTicket(context.Identity, context.Properties));
                }

                return(new AuthenticationTicket(null, properties));
            }
            catch (Exception ex)
            {
                _logger.WriteError("Authentication failed", ex);
                return(new AuthenticationTicket(null, properties));
            }
        }
        public override Task Authenticated(GoogleAuthenticatedContext context)
        {
            // custom processing ...

            return base.Authenticated(context);
        }