/// <summary> /// Invoked whenever OpenID 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(OpenIDAuthenticatedContext context) { return(OnAuthenticated(context)); }
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(Options.ProviderLoginUri, 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; } } } // Allow protocol extensions to add custom message validation rules foreach (var protocolExtension in Options.ProtocolExtensions) { if (!await protocolExtension.OnValidateMessageAsync(message)) { 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)); } 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)); } return(new AuthenticationTicket(null, properties)); } catch (Exception ex) { _logger.WriteError("Authentication failed", ex); return(new AuthenticationTicket(null, properties)); } }
/// <summary> /// Invoked whenever OpenID 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(OpenIDAuthenticatedContext context) { return OnAuthenticated(context); }