private HttpWebResponse SendHello() { // hello message try { var parameters = new SortedDictionary <string, string>(); parameters["username"] = Base64.URI.EncodeUtf8(user); var hello = new AuthMsg("hello", parameters); return(GetAuth(hello)); } catch (WebException e) { var response = e.Response; if (response != null) { var httpresp = (HttpWebResponse)response; // 401 Unauthorized // 500 Internal Server Error, for compatibility with nhaystack if (httpresp.StatusCode == HttpStatusCode.Unauthorized || httpresp.StatusCode == HttpStatusCode.InternalServerError) { return(httpresp); } } throw; } }
private async Task <HttpWebResponse> GetAuthAsync(AuthMsg msg) { return(await ServerCallAsync("about", c => { // set Authorization header c.Headers.Set("Authorization", msg.ToString()); })); }
////////////////////////////////////////////////////////////////////////// // Construction ////////////////////////////////////////////////////////////////////////// /// <summary> /// Parse a List of AuthSchemes such as a List of 'challenge' /// productions for the WWW-Authentication header per RFC 7235. /// </summary> public static AuthMsg[] ListFromStr(string s) { string[] toks = SplitList(s); AuthMsg[] arr = new AuthMsg[toks.Length]; for (int i = 0; i < toks.Length; ++i) { arr[i] = AuthMsg.FromStr(toks[i]); } return(arr); }
/// <summary> /// If the msg contains a handshake token, inject it into the given params </summary> private static SortedDictionary <string, string> InjectHandshakeToken(AuthMsg msg, SortedDictionary <string, string> @params) { string tok = msg.Param("handshakeToken", false); if (tok != null) { @params["handshakeToken"] = tok; } return(@params); }
private HttpWebResponse GetAuth(AuthMsg msg) { try { // all AuthClientContext requests are GET message to the /About uri var c = Prepare(OpenHttpConnection(uri, "GET")); // set Authorization header c.Headers.Set("Authorization", msg.ToString()); return(Get(c)); } catch (IOException e) { throw e; } }
private AuthMsg FirstMsg(IAuthClientContext cx, AuthMsg msg) { // construct client-first-message string c_nonce = GenNonce(); string c1_bare = "n=" + cx.user + ",r=" + c_nonce; string c1_msg = gs2_header + c1_bare; // stash for final msg cx.stash["c_nonce"] = c_nonce; cx.stash["c1_bare"] = c1_bare; // build auth msg SortedDictionary <string, string> @params = new SortedDictionary <string, string>(); @params["data"] = Base64.URI.EncodeUtf8(c1_msg); return(new AuthMsg(name, InjectHandshakeToken(msg, @params))); }
public override bool Equals(object o) { if (this == o) { return(true); } if (o == null || this.GetType() != o.GetType()) { return(false); } AuthMsg authMsg = (AuthMsg)o; if (!scheme.Equals(authMsg.scheme)) { return(false); } var comparer = new SortedDictComparer(); return(comparer.Compare(@params, authMsg.@params) == 1); }
private AuthMsg FinalMsg(IAuthClientContext cx, AuthMsg msg) { // Decode server-first-message string s1_msg = Base64.URI.decodeUTF8(msg.Param("data")); IDictionary data = DecodeMsg(s1_msg); // c2-no-proof string cbind_input = gs2_header; string channel_binding = Base64.URI.EncodeUtf8(cbind_input); string nonce = (string)data["r"]; string c2_no_proof = "c=" + channel_binding + ",r=" + nonce; // proof string hash = msg.Param("hash"); string salt = (string)data["s"]; int iterations = int.Parse((string)data["i"]); string c1_bare = (string)cx.stash["c1_bare"]; string authMsg = c1_bare + "," + s1_msg + "," + c2_no_proof; string c2_msg = null; try { sbyte[] saltedPassword = Pbk(hash, cx.pass, salt, iterations); string clientProof = CreateClientProof(hash, saltedPassword, (sbyte[])(Array)Encoding.UTF8.GetBytes(authMsg)); c2_msg = c2_no_proof + ",p= " + clientProof; } catch (Exception e) { throw new AuthException("Failed to compute scram", e); } // build auth msg SortedDictionary <string, string> @params = new SortedDictionary <string, string>(); @params["data"] = Base64.URI.EncodeUtf8(c2_msg); return(new AuthMsg(name, InjectHandshakeToken(msg, @params))); }
public override AuthMsg OnClient(IAuthClientContext cx, AuthMsg msg) { throw new System.NotSupportedException(); }
/// <summary> /// Attempt standard authentication /// </summary> /// <param name="resp"> The response to the hello message </param> /// <returns> /// true if haystack authentciation was used, false if the /// server does not appear to implement RFC 7235. /// </returns> private bool OpenStd(HttpWebResponse resp) { // must be 401 challenge with WWW-Authenticate header if (resp.StatusCode != HttpStatusCode.Unauthorized) { return(false); } var wwwAuth = ResHeader(resp, "WWW-Authenticate"); // don't Use this mechanism for Basic which we // handle as a non-standard scheme because the headers // don't fit nicely into our restricted AuthMsg format if (string.IsNullOrEmpty(wwwAuth) || wwwAuth.ToLower().StartsWith("basic", StringComparison.Ordinal)) { return(false); } // process res/req messages until we have 200 or non-401 failure AuthScheme scheme = null; for (var loopCount = 0; ; ++loopCount) { // sanity check that we don't loop too many times if (loopCount > 5) { throw new AuthException("Loop count exceeded"); } // parse the WWW-Auth header and Use the first scheme var header = ResHeader(resp, "WWW-Authenticate"); AuthMsg[] resMsgs = AuthMsg.ListFromStr(header); var resMsg = resMsgs[0]; scheme = AuthScheme.Find(resMsg.scheme); // let scheme handle message var reqMsg = scheme.OnClient(this, resMsg); // send request back to the server resp = GetAuth(reqMsg); try { DumpRes(resp, false); } catch (Exception e) { e.ToString(); } // 200 means we are done, 401 means keep looping, // consider anything else a failure if (resp.StatusCode == HttpStatusCode.OK) { break; } if (resp.StatusCode == HttpStatusCode.Unauthorized) { continue; } throw new AuthException((int)resp.StatusCode + " " + resp.GetResponseStream()); } // init the bearer token var authInfo = ResHeader(resp, "Authentication-Info"); AuthMsg authInfoMsg = AuthMsg.FromStr("bearer " + authInfo); // callback to scheme for client Success scheme.OnClientSuccess(this, authInfoMsg); // only keep authToken parameter for Authorization header authInfoMsg = new AuthMsg("bearer", new[] { "authToken", authInfoMsg.Param("authToken") }); headers["Authorization"] = authInfoMsg.ToString(); // we did it! return(true); }
/// <summary> /// Handle a standardized client authentication challenge message from /// the server using RFC 7235. /// </summary> /// <param name="cx">the current AuthClientContext</param> /// <param name="msg">the AuthMsg sent by the server</param> /// <returns>AuthMsg to send back to the server to authenticate</returns> public abstract AuthMsg OnClient(IAuthClientContext cx, AuthMsg msg);
/// <summary> /// Callback after successful authentication with the server. /// The default implementation is a no-op. /// </summary> /// <param name="cx">the current AuthClientContext</param> /// <param name="msg">AuthMsg sent by the server when it authenticated the client</param> public virtual void OnClientSuccess(IAuthClientContext cx, AuthMsg msg) { }
public override AuthMsg OnClient(IAuthClientContext cx, AuthMsg msg) { return(ReferenceEquals(msg.Param("data", false), null) ? FirstMsg(cx, msg) : FinalMsg(cx, msg)); }