static StateContainer SetupContainer() { StateContainer props = new StateContainer(); new Xrds(props); new Yadis(props); new Html(props); new IdentityAuthentication(props); return props; }
/// <summary> /// Returns the URL to which the User Agent should be redirected for the initial authentication request. /// </summary> /// <param name="props">RequestProperties object holding the current state.</param> /// <param name="discResult">The DiscoveryResult object created from the previous discovery process.</param> /// <param name="immediate">Whether or not an Immediate Mode request is being generated.</param> /// <returns>The complete URL to which the User Agent should be redirected.</returns> internal static Uri GetRedirectURL(StateContainer props, DiscoveryResult discResult, bool immediate) { if (props == null) { throw new ArgumentNullException("props"); } if (discResult == null) { throw new ArgumentNullException("discResult"); } NameValueCollection pms = new NameValueCollection(); if (immediate) { pms["openid.mode"] = "checkid_immediate"; } else { pms["openid.mode"] = "checkid_setup"; } string returl = props.ReturnToUrl.AbsoluteUri; switch (props.AuthMode) { case AuthenticationMode.Stateful: Association assoc = props.AssociationManager.FindByServer(discResult.ServerUrl.AbsoluteUri); Random r = new Random(); props.Nonce = r.Next(); switch (discResult.AuthVersion) { case ProtocolVersion.V1Dot1: pms["openid.assoc_handle"] = assoc.Handle; pms["openid.trust_root"] = props.TrustRoot; break; case ProtocolVersion.V2Dot0: pms["openid.ns"] = ProtocolUri.OpenId2Dot0.AbsoluteUri; pms["openid.assoc_handle"] = assoc.Handle; pms["openid.realm"] = props.TrustRoot; break; } break; case AuthenticationMode.Stateless: switch (discResult.AuthVersion) { case ProtocolVersion.V1Dot1: pms["openid.trust_root"] = props.TrustRoot; break; case ProtocolVersion.V2Dot0: pms["openid.ns"] = ProtocolUri.OpenId2Dot0.AbsoluteUri; pms["openid.realm"] = props.TrustRoot; break; } break; } foreach (IExtension e in props.ExtensionPlugIns) { NameValueCollection authdata = e.BuildAuthorizationData(discResult); foreach (string key in authdata.Keys) { if (key == "esoid.ReturnUrl") { if (returl.Contains("?")) { returl += "&" + authdata[key]; } else { returl += "?" + authdata[key]; } } else { pms[key] = authdata[key]; } } } pms["openid.return_to"] = Utility.AddExtraVariables(returl, props); return Utility.MakeGetURL(discResult.ServerUrl, pms); }
/// <summary> /// Validate a stateless mode response from an OpenID Provider. /// </summary> /// <param name="server">The OpenID Provider</param> /// <param name="fallback">Whether or not this is a fallback check due to a failed stateful validation attempt.</param> /// <param name="rp">The StateContainer object containing the arguments to process.</param> /// <returns>True if the validation is successful, false if not.</returns> internal static bool ValidateStatelessResponse(Uri server, bool fallback, StateContainer rp) { if (server == null) { throw new ArgumentNullException("server"); } if (rp == null) { throw new ArgumentNullException("rp"); } NameValueCollection pms = new NameValueCollection(); if (fallback) { if (!String.IsNullOrEmpty(rp.RequestArguments["openid.invalidate_handle"])) { pms["openid.invalidate_handle"] = rp.RequestArguments["openid.invalidate_handle"]; } } // Run validation checks in each plug-in foreach (IExtension ext in rp.ExtensionPlugIns) { if (!ext.Validation()) { return false; } } // Send only required parameters to confirm validity string[] arr_signed = rp.RequestArguments["openid.signed"].Split(','); for (int i = 0; i < arr_signed.Length; i++) { string s = arr_signed[i]; string c = rp.RequestArguments["openid." + arr_signed[i]]; pms["openid." + s] = c; } if (server == null) { Tracer.Write("No OpenID servers found"); rp.ErrorState = ErrorCondition.NoServersFound; return false; } pms["openid.mode"] = "check_authentication"; pms["openid.assoc_handle"] = rp.RequestArguments["openid.assoc_handle"]; pms["openid.signed"] = rp.RequestArguments["openid.signed"]; pms["openid.sig"] = rp.RequestArguments["openid.sig"]; string actualLocation = null; // Connect to IdP string response = Utility.MakeRequest(server, "POST", pms, out actualLocation); if (String.IsNullOrEmpty(response)) { Tracer.Write("No response from Identity Provider using POST, trying GET."); response = Utility.MakeRequest(server, "GET", pms, out actualLocation); if (String.IsNullOrEmpty(response)) { return false; } } // Parse reponse NameValueCollection data = Utility.SplitResponse(response); // Check for validity of authentication request if (data["is_valid"] == "true") { Tracer.Write("Server has validated authentication response."); return true; } else { StringBuilder sb = new StringBuilder(); foreach (string key in data.Keys) { sb.AppendLine(key + ":" + data[key]); } Tracer.Write("Server has not validated authentication response. Response received: " + sb.ToString()); rp.ErrorState = ErrorCondition.RequestRefused; return false; } }
/// <summary> /// Validate a stateful mode response from an OpenID Provider. /// </summary> /// <param name="server">The OpenID Provider URL.</param> /// <param name="rp">The StateContainer object needed to process the validation.</param> /// <returns>Whether or not validation was successful.</returns> internal static bool ValidateStatefulResponse(Uri server, StateContainer rp) { if (server == null) { throw new ArgumentNullException("server"); } if (rp == null) { throw new ArgumentNullException("rp"); } if (rp.Nonce == -1) { Tracer.Write("Error: The cnonce is not valid."); rp.ErrorState = ErrorCondition.SessionTimeout; return false; } else { if (!String.IsNullOrEmpty(rp.RequestArguments["cnonce"]) && rp.Nonce != Convert.ToInt32(rp.RequestArguments["cnonce"], CultureInfo.InvariantCulture)) { Tracer.Write("Error: The cnonce has expired."); rp.ErrorState = ErrorCondition.SessionTimeout; return false; } } // Run validation checks in each plug-in foreach (IExtension ext in rp.ExtensionPlugIns) { if (!ext.Validation()) { return false; } } Tracer.Write("Looking up association in association table."); Association assoc = rp.AssociationManager.FindByHandle(rp.RequestArguments["openid.assoc_handle"]); if (assoc == null) { // Check to see if the handle has been invalidated if (rp.RequestArguments["openid.invalidate_handle"] != null) { Tracer.Write("Association handle has been invalidated."); return false; } else { Tracer.Write("Association handle was not found in the table."); return false; } } // Ensure association key has not expired if (assoc.Expiration < DateTime.UtcNow) { Tracer.Write("Association has expired, removing from table."); rp.AssociationManager.Remove(assoc); return false; } // Check for someone trying to forge a response from another // OpenID Provider if (server.AbsoluteUri != assoc.Server) { Tracer.Write("Received response handle is not valid for the specified OpenID Provider."); return false; } // Compare data from browser to association handle from server if (assoc.Handle == rp.RequestArguments["openid.assoc_handle"]) { string[] tokens = rp.RequestArguments["openid.signed"].ToString().Split(','); string token_contents = ""; foreach (string token in tokens) { token_contents += token + ":" + rp.RequestArguments["openid." + token] + "\n"; } Tracer.Write("Generating signature for tokens: " + token_contents); string sig = rp.RequestArguments["openid.sig"].ToString(); byte[] secretkey = assoc.Secret; byte[] tokenbyte = ASCIIEncoding.ASCII.GetBytes(token_contents); HashAlgorithm hmac = null; if (assoc.AssociationType == "HMAC-SHA1") { hmac = new HMACSHA1(secretkey); } else if (assoc.AssociationType == "HMAC-SHA256") { hmac = new HMACSHA256(secretkey); } byte[] realHash = hmac.ComputeHash(tokenbyte); string strrealHash = Convert.ToBase64String(realHash); Tracer.Write("Expected signature: " + sig); Tracer.Write("Generated signature: " + strrealHash); if (sig != strrealHash) { Tracer.Write("Received signature does not match generated signature"); return false; } return true; } Tracer.Write("Received association handle does not match cached handle."); return false; }
/// <summary> /// Processes errors received during HTTP requests. /// </summary> /// <param name="response">WebResponse object to handle.</param> /// <param name="rp">StateContainer object where error state will be recorded.</param> internal static void HandleHttpError(WebResponse response, StateContainer rp) { Stream s; StreamReader sr; string body = ""; if (response == null) { Tracer.Write("Error: Received null response to HTTP request."); if (rp != null) rp.ErrorState = ErrorCondition.HttpError; return; } HttpWebResponse hwr = (HttpWebResponse)response; switch (hwr.StatusCode) { case HttpStatusCode.BadRequest: s = response.GetResponseStream(); sr = new StreamReader(s); body = sr.ReadToEnd(); if (body != null) { NameValueCollection data = SplitResponse(body); if (data["error"] != null) { Tracer.Write("Received error: " + data["error"]); if (rp != null) rp.ErrorState = ErrorCondition.RequestRefused; return; } } if (rp != null) rp.ErrorState = ErrorCondition.HttpError; break; default: Tracer.Write("HTTP request failed, response code: " + hwr.StatusCode.ToString()); break; } }
/// <summary> /// Appends the cnonce variable to the end of a URL. /// </summary> /// <param name="url">URL to process.</param> /// <param name="rp">StateContainer to use.</param> /// <returns>A combined URL.</returns> internal static string AddExtraVariables(string url, StateContainer rp) { if (String.IsNullOrEmpty(url)) { return url; } if (rp == null) { throw new ArgumentNullException("rp"); } string retval = url; if (rp.AuthMode == AuthenticationMode.Stateful) { if (retval.Contains("?")) { retval += "&cnonce=" + rp.Nonce; } else { retval += "?cnonce=" + rp.Nonce; } } return retval; }
/// <summary> /// Look at the current arguments and load the extension plugins needed to service /// the request. /// </summary> /// <param name="rp">StateContainer object to store loaded plugins.</param> internal static void AutoLoadExtensionPlugins(StateContainer rp) { if (rp == null) { throw new ArgumentNullException("rp"); } Tracer.Write("Loading extension plugins"); Type[] types = GetRequiredExtensionPlugins(rp.RequestArguments); foreach (Type t in types) { Tracer.Write("Loading plugin '" + t.ToString() + "'"); Activator.CreateInstance(t, rp); } }