/// <summary> /// Returns the string representation of the bewit, which is a base64 URL encoded string of format /// id\exp\mac\ext, where id is the user identifier, exp is the UNIX time until which bewit is /// valid, mac is the HMAC of the bewit to protect integrity, and ext is the application specific data. /// </summary> public string ToBewitString() { if (request.Method != HttpMethod.Get) // Not supporting HEAD throw new InvalidOperationException("Bewit not allowed for methods other than GET"); ulong now = utcNow.ToUnixTime() + Convert.ToUInt64(this.localOffset); var artifacts = new ArtifactsContainer() { Id = credential.Id, Timestamp = now + (ulong)lifeSeconds, Nonce = String.Empty, ApplicationSpecificData = this.applicationSpecificData ?? String.Empty }; var normalizedRequest = new NormalizedRequest(request, artifacts) { IsBewit = true }; var crypto = new Cryptographer(normalizedRequest, artifacts, credential); // Sign the request crypto.Sign(); // Bewit is for GET and GET must have no request body // bewit: id\exp\mac\ext string bewit = String.Format(@"{0}\{1}\{2}\{3}", credential.Id, artifacts.Timestamp, artifacts.Mac.ToBase64String(), artifacts.ApplicationSpecificData); return bewit.ToBytesFromUtf8().ToBase64UrlString(); }
internal Cryptographer(NormalizedRequest request, ArtifactsContainer artifacts, Credential credential) { this.normalizedRequest = request; this.artifacts = artifacts; this.credential = credential; this.hasher = new Hasher(credential.Algorithm); }
/// <summary> /// Returns the string representation of the bewit, which is a base64 URL encoded string of format /// id\exp\mac\ext, where id is the user identifier, exp is the UNIX time until which bewit is /// valid, mac is the HMAC of the bewit to protect integrity, and ext is the application specific data. /// </summary> public async Task<string> ToBewitStringAsync() { if (request.Method != HttpMethod.Get) // Not supporting HEAD throw new InvalidOperationException("Bewit not allowed for methods other than GET"); ulong now = utcNow.ToUnixTime() + UInt64.Parse(ConfigurationManager.AppSettings["LocalTimeOffsetMillis"]); var artifacts = new ArtifactsContainer() { Id = credential.Id, Timestamp = now + (ulong)lifeSeconds, Nonce = String.Empty, ApplicationSpecificData = this.applicationSpecificData ?? String.Empty }; var normalizedRequest = new NormalizedRequest(request, artifacts) { IsBewit = true }; var crypto = new Cryptographer(normalizedRequest, artifacts, credential); // Sign the request await crypto.SignAsync(null); // bewit: id\exp\mac\ext string bewit = String.Format(@"{0}\{1}\{2}\{3}", credential.Id, artifacts.Timestamp, artifacts.Mac.ToBase64String(), artifacts.ApplicationSpecificData); return bewit.ToBytesFromUtf8().ToBase64UrlString(); }
/// <summary> /// Returns the string representation of the bewit, which is a base64 URL encoded string of format /// id\exp\mac\ext, where id is the user identifier, exp is the UNIX time until which bewit is /// valid, mac is the HMAC of the bewit to protect integrity, and ext is the application specific data. /// </summary> public string ToBewitString() { if (request.Method != HttpMethod.Get) // Not supporting HEAD { throw new InvalidOperationException("Bewit not allowed for methods other than GET"); } ulong now = utcNow.ToUnixTime() + Convert.ToUInt64(this.localOffset); var artifacts = new ArtifactsContainer() { Id = credential.Id, Timestamp = now + (ulong)lifeSeconds, Nonce = String.Empty, ApplicationSpecificData = this.applicationSpecificData ?? String.Empty }; var normalizedRequest = new NormalizedRequest(request, artifacts) { IsBewit = true }; var crypto = new Cryptographer(normalizedRequest, artifacts, credential); // Sign the request crypto.Sign(); // Bewit is for GET and GET must have no request body // bewit: id\exp\mac\ext string bewit = String.Format(@"{0}\{1}\{2}\{3}", credential.Id, artifacts.Timestamp, artifacts.Mac.ToBase64String(), artifacts.ApplicationSpecificData); return(bewit.ToBytesFromUtf8().ToBase64UrlString()); }
/// <summary> /// Adds the WWW-Authenticate header in case of a 401 - Unauthorized response and /// the Server-Authorization header in case of a successful request. /// </summary> public async Task CreateServerAuthorizationAsync(HttpResponseMessage response, Func <HttpResponseMessage, string> normalizationCallback) { if (response.StatusCode == HttpStatusCode.Unauthorized) { var header = new AuthenticationHeaderValue(HawkConstants.Scheme, request.GetChallengeParameter()); response.Headers.WwwAuthenticate.Add(header); } else { if (this.result != null && this.result.IsAuthentic && !this.isBewitRequest) // No Server-Authorization header for bewit requests { if (normalizationCallback != null) { this.result.Artifacts.ApplicationSpecificData = normalizationCallback(response); } // Sign the response var normalizedRequest = new NormalizedRequest(request, this.result.Artifacts); var crypto = new Cryptographer(normalizedRequest, this.result.Artifacts, this.result.Credential); await crypto.SignAsync(response.Content); string authorization = this.result.Artifacts.ToServerAuthorizationHeaderParameter(); if (!String.IsNullOrWhiteSpace(authorization)) { response.Headers.Add(HawkConstants.ServerAuthorizationHeaderName, HawkConstants.Scheme + " " + authorization); } } } }
/// <summary> /// Returns the name of the response header (WWW-Authenticate or Server-Authorization) and the corresponding /// value, respectively for an unauthorized and a successful request. /// </summary> public async Task <Tuple <string, string> > CreateServerAuthorizationAsync(IResponseMessage response) { if (response.StatusCode == HttpStatusCode.Unauthorized) { string challenge = String.Format(" {0}", request.ChallengeParameter ?? String.Empty); string headerValue = HawkConstants.Scheme + challenge.TrimEnd(' '); return(new Tuple <string, string>(HawkConstants.WwwAuthenticateHeaderName, headerValue)); } else { // No Server-Authorization header for the following: // (1) There is no result or failed authentication. // (2) The credential is a bewit. // (3) The server is configured to not send the header. bool createHeader = this.result != null && this.result.IsAuthentic && (!this.isBewitRequest) && options.EnableServerAuthorization; if (createHeader) { if (options.NormalizationCallback != null) { this.result.Artifacts.ApplicationSpecificData = options.NormalizationCallback(response); } // Sign the response var normalizedRequest = new NormalizedRequest(request, this.result.Artifacts) { IsServerAuthorization = true }; var crypto = new Cryptographer(normalizedRequest, this.result.Artifacts, this.result.Credential); // Response body is needed only if payload hash must be included in the response MAC. string body = null; if (options.ResponsePayloadHashabilityCallback != null && options.ResponsePayloadHashabilityCallback(this.request)) { body = await response.ReadBodyAsStringAsync(); } crypto.Sign(body, response.ContentType); string authorization = this.result.Artifacts.ToServerAuthorizationHeaderParameter(); if (!String.IsNullOrWhiteSpace(authorization)) { return(new Tuple <string, string>(HawkConstants.ServerAuthorizationHeaderName, String.Format("{0} {1}", HawkConstants.Scheme, authorization))); } } } return(null); }
/// <summary> /// Returns an AuthenticationResult object corresponding to the result of authentication done /// using the client supplied artifacts in the HTTP authorization header in hawk scheme. /// </summary> /// <param name="now">Current UNIX time in milliseconds.</param> /// <param name="request">Request object.</param> /// <param name="options">Hawk authentication options</param> /// <returns></returns> internal static async Task <AuthenticationResult> AuthenticateAsync(ulong now, IRequestMessage request, Options options) { ArtifactsContainer artifacts = null; Credential credential = null; if (request.HasValidHawkScheme()) { if (ArtifactsContainer.TryParse(request.Authorization.Parameter, out artifacts)) { if (artifacts != null && artifacts.AreClientArtifactsValid) { credential = options.CredentialsCallback(artifacts.Id); if (credential != null && credential.IsValid) { var normalizedRequest = new NormalizedRequest(request, artifacts); var crypto = new Cryptographer(normalizedRequest, artifacts, credential); // Request body is needed only when payload hash is present in the request string body = null; if (artifacts.PayloadHash != null && artifacts.PayloadHash.Length > 0) { body = await request.ReadBodyAsStringAsync(); } if (crypto.IsSignatureValid(body, request.ContentType)) // MAC and hash checks { if (IsTimestampFresh(now, artifacts, options)) { // If you get this far, you are authentic. Welcome and thanks for flying Hawk! return(new AuthenticationResult() { IsAuthentic = true, Artifacts = artifacts, Credential = credential, ApplicationSpecificData = artifacts.ApplicationSpecificData }); } else { // Authentic but for the timestamp freshness. // Give a chance to the client to correct the clocks skew. var timestamp = new NormalizedTimestamp(DateTime.UtcNow, credential, options.LocalTimeOffsetMillis); request.ChallengeParameter = timestamp.ToWwwAuthenticateHeaderParameter(); } } } } } } return(new AuthenticationResult() { IsAuthentic = false }); }
/// <summary> /// Returns an AuthenticationResult object corresponding to the result of authentication done /// using the client supplied artifacts in the HTTP authorization header in hawk scheme. /// </summary> /// <param name="now">Current UNIX time in milliseconds.</param> /// <param name="request">Request object.</param> /// <param name="options">Hawk authentication options</param> /// <returns></returns> internal static async Task<AuthenticationResult> AuthenticateAsync(ulong now, IRequestMessage request, Options options) { ArtifactsContainer artifacts = null; Credential credential = null; if (request.HasValidHawkScheme()) { if (ArtifactsContainer.TryParse(request.Authorization.Parameter, out artifacts)) { if (artifacts != null && artifacts.AreClientArtifactsValid) { credential = options.CredentialsCallback(artifacts.Id); if (credential != null && credential.IsValid) { var normalizedRequest = new NormalizedRequest(request, artifacts); var crypto = new Cryptographer(normalizedRequest, artifacts, credential); // Request body is needed only when payload hash is present in the request string body = null; if (artifacts.PayloadHash != null && artifacts.PayloadHash.Length > 0) { body = await request.ReadBodyAsStringAsync(); } if (crypto.IsSignatureValid(body, request.ContentType)) // MAC and hash checks { if (IsTimestampFresh(now, artifacts, options)) { // If you get this far, you are authentic. Welcome and thanks for flying Hawk! return new AuthenticationResult() { IsAuthentic = true, Artifacts = artifacts, Credential = credential, ApplicationSpecificData = artifacts.ApplicationSpecificData }; } else { // Authentic but for the timestamp freshness. // Give a chance to the client to correct the clocks skew. var timestamp = new NormalizedTimestamp(DateTime.UtcNow, credential, options.LocalTimeOffsetMillis); request.ChallengeParameter = timestamp.ToWwwAuthenticateHeaderParameter(); } } } } } } return new AuthenticationResult() { IsAuthentic = false }; }
public void HttpsMustSetPortTo443() { var request = new HttpRequestMessage(); request.RequestUri = new Uri("https://server/api/values"); var normalizedRequest = new NormalizedRequest(request, null); PrivateObject po = new PrivateObject(normalizedRequest); var port = (string)po.GetField("port"); Assert.AreEqual("443", port); }
public void PortMustDefaultTo443ForHttpsWhenHostHeaderDoesNotContainPort() { var request = new HttpRequestMessage(); request.RequestUri = new Uri("https://server/api/values"); request.Headers.Host = "myhost"; var normalizedRequest = new NormalizedRequest(request, null); PrivateObject po = new PrivateObject(normalizedRequest); var port = (string)po.GetField("port"); Assert.AreEqual("443", port); }
/// <summary> /// Returns an AuthenticationResult object corresponding to the result of authentication done /// using the client supplied artifacts in the HTTP authorization header in hawk scheme. /// </summary> /// <param name="now">Current UNIX time in milliseconds.</param> /// <param name="request">Request object.</param> /// <param name="callback">The callback function that returns a Credential object corresponding to the identifier passed in.</param> /// <returns></returns> internal static async Task <AuthenticationResult> AuthenticateAsync(ulong now, HttpRequestMessage request, Func <string, Credential> callback) { ArtifactsContainer artifacts = null; Credential credential = null; if (request.HasValidHawkScheme()) { if (ArtifactsContainer.TryParse(request.Headers.Authorization.Parameter, out artifacts)) { if (artifacts != null && artifacts.AreClientArtifactsValid) { credential = callback(artifacts.Id); if (credential != null && credential.IsValid) { var normalizedRequest = new NormalizedRequest(request, artifacts); var crypto = new Cryptographer(normalizedRequest, artifacts, credential); if (await crypto.IsSignatureValidAsync(request.Content)) // MAC and hash checks { if (IsTimestampFresh(now, artifacts)) { // If you get this far, you are authentic. Welcome and thanks for flying Hawk! return(new AuthenticationResult() { IsAuthentic = true, Artifacts = artifacts, Credential = credential, ApplicationSpecificData = artifacts.ApplicationSpecificData }); } else { // Authentic but for the timestamp freshness. // Give a chance to the client to correct the clocks skew. var timestamp = new NormalizedTimestamp(DateTime.UtcNow, credential); request.PutChallengeParameter(timestamp.ToWwwAuthenticateHeaderParameter()); } } } } } } return(new AuthenticationResult() { IsAuthentic = false }); }
public void HostAndPortMustMatchWhatIsInRequestWhenHostAndXffHeadersAreAbsent() { var request = new HttpRequestMessage(); request.RequestUri = new Uri("http://server/api/values"); var normalizedRequest = new NormalizedRequest(request, null); PrivateObject po = new PrivateObject(normalizedRequest); var hostName = (string)po.GetField("hostName"); var port = (string)po.GetField("port"); Assert.AreEqual("server", hostName); Assert.AreEqual("80", port); }
public void HostAndPortMustMatchWhatIsInHostHeaderWhenPresent() { var request = new HttpRequestMessage(); request.RequestUri = new Uri("http://server/api/values"); request.Headers.Host = "myhost:899"; var normalizedRequest = new NormalizedRequest(new WebApiRequestMessage(request), null); PrivateObject po = new PrivateObject(normalizedRequest); var hostName = (string)po.GetField("hostName"); var port = (string)po.GetField("port"); Assert.AreEqual("myhost", hostName); Assert.AreEqual("899", port); }
/// <summary> /// Returns an AuthenticationResult object corresponding to the result of authentication done /// using the client supplied artifacts in the HTTP authorization header in hawk scheme. /// </summary> /// <param name="now">Current UNIX time in milliseconds.</param> /// <param name="request">Request object.</param> /// <param name="callback">The callback function that returns a Credential object corresponding to the identifier passed in.</param> /// <returns></returns> internal static async Task<AuthenticationResult> AuthenticateAsync(ulong now, HttpRequestMessage request, Func<string, Credential> callback) { ArtifactsContainer artifacts = null; Credential credential = null; if (request.HasValidHawkScheme()) { if (ArtifactsContainer.TryParse(request.Headers.Authorization.Parameter, out artifacts)) { if (artifacts != null && artifacts.AreClientArtifactsValid) { credential = callback(artifacts.Id); if (credential != null && credential.IsValid) { var normalizedRequest = new NormalizedRequest(request, artifacts); var crypto = new Cryptographer(normalizedRequest, artifacts, credential); if (await crypto.IsSignatureValidAsync(request.Content)) // MAC and hash checks { if (IsTimestampFresh(now, artifacts)) { // If you get this far, you are authentic. Welcome and thanks for flying Hawk! return new AuthenticationResult() { IsAuthentic = true, Artifacts = artifacts, Credential = credential, ApplicationSpecificData = artifacts.ApplicationSpecificData }; } else { // Authentic but for the timestamp freshness. // Give a chance to the client to correct the clocks skew. var timestamp = new NormalizedTimestamp(DateTime.UtcNow, credential); request.PutChallengeParameter(timestamp.ToWwwAuthenticateHeaderParameter()); } } } } } } return new AuthenticationResult() { IsAuthentic = false }; }
/// <summary> /// Returns the string representation of the bewit, which is a base64 URL encoded string of format /// id\exp\mac\ext, where id is the user identifier, exp is the UNIX time until which bewit is /// valid, mac is the HMAC of the bewit to protect integrity, and ext is the application specific data. /// </summary> public async Task <string> ToBewitStringAsync() { if (request.Method != HttpMethod.Get) // Not supporting HEAD { throw new InvalidOperationException("Bewit not allowed for methods other than GET"); } ulong now = utcNow.ToUnixTime() + UInt64.Parse(ConfigurationManager.AppSettings["LocalTimeOffsetMillis"]); var artifacts = new ArtifactsContainer() { Id = credential.Id, Timestamp = now + (ulong)lifeSeconds, Nonce = String.Empty, ApplicationSpecificData = this.applicationSpecificData ?? String.Empty }; var normalizedRequest = new NormalizedRequest(request, artifacts) { IsBewit = true }; var crypto = new Cryptographer(normalizedRequest, artifacts, credential); // Sign the request await crypto.SignAsync(null); // bewit: id\exp\mac\ext string bewit = String.Format(@"{0}\{1}\{2}\{3}", credential.Id, artifacts.Timestamp, artifacts.Mac.ToBase64String(), artifacts.ApplicationSpecificData); return(bewit.ToBytesFromUtf8().ToBase64UrlString()); }
/// <summary> /// Returns an AuthenticationResult object corresponding to the result of authentication done /// using the client supplied artifacts in the bewit query string parameter. /// </summary> /// <param name="bewit">Value of the query string parameter with the name of 'bewit'.</param> /// <param name="now">Date and time in UTC to be used as the base for computing bewit life.</param> /// <param name="request">Request object.</param> /// <param name="options">Hawk authentication options</param> internal static AuthenticationResult Authenticate(string bewit, ulong now, IRequestMessage request, Options options) { if (!String.IsNullOrWhiteSpace(bewit)) { if (request.Method == HttpMethod.Get) { if (options != null && options.CredentialsCallback != null) { var parts = bewit.ToUtf8StringFromBase64Url().Split('\\'); if (parts.Length == 4) { ulong timestamp = 0; if (UInt64.TryParse(parts[1], out timestamp) && timestamp * 1000 > now) { string id = parts[0]; string mac = parts[2]; string ext = parts[3]; if (!String.IsNullOrWhiteSpace(id) && !String.IsNullOrWhiteSpace(mac)) { RemoveBewitFromUri(request); Credential credential = options.CredentialsCallback(id); if (credential != null && credential.IsValid) { var artifacts = new ArtifactsContainer() { Id = id, Nonce = String.Empty, Timestamp = timestamp, Mac = mac.ToBytesFromBase64(), ApplicationSpecificData = ext ?? String.Empty }; var normalizedRequest = new NormalizedRequest(request, artifacts) { IsBewit = true }; var crypto = new Cryptographer(normalizedRequest, artifacts, credential); if (crypto.IsSignatureValid()) // Bewit is for GET and GET must have no request body { return(new AuthenticationResult() { IsAuthentic = true, Credential = credential, Artifacts = artifacts, ApplicationSpecificData = ext }); } } } } } } } } return(new AuthenticationResult() { IsAuthentic = false }); }
/// <summary> /// Returns the name of the response header (WWW-Authenticate or Server-Authorization) and the corresponding /// value, respectively for an unauthorized and a successful request. /// </summary> public async Task<Tuple<string, string>> CreateServerAuthorizationAsync(IResponseMessage response) { if (response.StatusCode == HttpStatusCode.Unauthorized) { string challenge = String.Format(" {0}", request.ChallengeParameter ?? String.Empty); string headerValue = HawkConstants.Scheme + challenge.TrimEnd(' '); return new Tuple<string, string>(HawkConstants.WwwAuthenticateHeaderName, headerValue); } else { // No Server-Authorization header for the following: // (1) There is no result or failed authentication. // (2) The credential is a bewit. // (3) The server is configured to not send the header. bool createHeader = this.result != null && this.result.IsAuthentic && (!this.isBewitRequest) && options.EnableServerAuthorization; if (createHeader) { if (options.NormalizationCallback != null) this.result.Artifacts.ApplicationSpecificData = options.NormalizationCallback(response); // Sign the response var normalizedRequest = new NormalizedRequest(request, this.result.Artifacts); var crypto = new Cryptographer(normalizedRequest, this.result.Artifacts, this.result.Credential); // Response body is needed only if payload hash must be included in the response MAC. string body = null; if (options.ResponsePayloadHashabilityCallback != null && options.ResponsePayloadHashabilityCallback(this.request)) { body = await response.ReadBodyAsStringAsync(); } crypto.Sign(body, response.ContentType); string authorization = this.result.Artifacts.ToServerAuthorizationHeaderParameter(); if (!String.IsNullOrWhiteSpace(authorization)) { return new Tuple<string, string>(HawkConstants.ServerAuthorizationHeaderName, String.Format("{0} {1}", HawkConstants.Scheme, authorization)); } } } return null; }
/// <summary> /// Adds the WWW-Authenticate header in case of a 401 - Unauthorized response and /// the Server-Authorization header in case of a successful request. /// </summary> public async Task CreateServerAuthorizationAsync(HttpResponseMessage response, Func<HttpResponseMessage, string> normalizationCallback) { if (response.StatusCode == HttpStatusCode.Unauthorized) { var header = new AuthenticationHeaderValue(HawkConstants.Scheme, request.GetChallengeParameter()); response.Headers.WwwAuthenticate.Add(header); } else { if (this.result != null && this.result.IsAuthentic && !this.isBewitRequest) // No Server-Authorization header for bewit requests { if (normalizationCallback != null) this.result.Artifacts.ApplicationSpecificData = normalizationCallback(response); // Sign the response var normalizedRequest = new NormalizedRequest(request, this.result.Artifacts); var crypto = new Cryptographer(normalizedRequest, this.result.Artifacts, this.result.Credential); await crypto.SignAsync(response.Content); string authorization = this.result.Artifacts.ToServerAuthorizationHeaderParameter(); if (!String.IsNullOrWhiteSpace(authorization)) response.Headers.Add(HawkConstants.ServerAuthorizationHeaderName, HawkConstants.Scheme + " " + authorization); } } }
public void HostAndPortMustMatchWhatIsInTheFirstXffHeaderWhenMultipleXffHeadersArePresentWithIpv6Address() { var request = new HttpRequestMessage(); request.RequestUri = new Uri("http://server/api/values"); request.Headers.Host = "myhost:899"; request.Headers.Add("X-Forwarded-For", "[111:111:111]:1111"); request.Headers.Add("X-Forwarded-For", "[222:222:222]:2222"); // Same as "[111:111:111]:1111, [222:222:222]:2222" var normalizedRequest = new NormalizedRequest(request, null); PrivateObject po = new PrivateObject(normalizedRequest); var hostName = (string)po.GetField("hostName"); var port = (string)po.GetField("port"); Assert.AreEqual("[111:111:111]", hostName); Assert.AreEqual("1111", port); }
public void HostAndPortMustMatchWhatIsInXffHeaderWhenPresent() { var request = new HttpRequestMessage(); request.RequestUri = new Uri("http://server/api/values"); request.Headers.Host = "myhost:899"; request.Headers.Add("X-Forwarded-For", "xffhost:4444"); var normalizedRequest = new NormalizedRequest(request, null); PrivateObject po = new PrivateObject(normalizedRequest); var hostName = (string)po.GetField("hostName"); var port = (string)po.GetField("port"); Assert.AreEqual("xffhost", hostName); Assert.AreEqual("4444", port); }
/// <summary> /// Creates the HTTP Authorization header in hawk scheme. /// </summary> internal async Task CreateClientAuthorizationInternalAsync(IRequestMessage request, DateTime utcNow) { var credential = options.CredentialsCallback(); this.artifacts = new ArtifactsContainer() { Id = credential.Id, Timestamp = utcNow.AddSeconds(HawkClient.CompensatorySeconds).ToUnixTime(), Nonce = NonceGenerator.Generate() }; if (options.NormalizationCallback != null) this.artifacts.ApplicationSpecificData = options.NormalizationCallback(request); var normalizedRequest = new NormalizedRequest(request, this.artifacts); this.crypto = new Cryptographer(normalizedRequest, this.artifacts, credential); // Sign the request bool includePayloadHash = options.RequestPayloadHashabilityCallback != null && options.RequestPayloadHashabilityCallback(request); string payload = includePayloadHash ? await request.ReadBodyAsStringAsync() : null; crypto.Sign(payload, request.ContentType); request.Authorization = new AuthenticationHeaderValue(HawkConstants.Scheme, this.artifacts.ToAuthorizationHeaderParameter()); }
/// <summary> /// Returns an AuthenticationResult object corresponding to the result of authentication done /// using the client supplied artifacts in the bewit query string parameter. /// </summary> /// <param name="bewit">Value of the query string parameter with the name of 'bewit'.</param> /// <param name="now">Date and time in UTC to be used as the base for computing bewit life.</param> /// <param name="request">Request object.</param> /// <param name="options">Hawk authentication options</param> internal static AuthenticationResult Authenticate(string bewit, ulong now, IRequestMessage request, Options options) { if (!String.IsNullOrWhiteSpace(bewit)) { if (request.Method == HttpMethod.Get) { if (options != null && options.CredentialsCallback != null) { var parts = bewit.ToUtf8StringFromBase64Url().Split('\\'); if (parts.Length == 4) { ulong timestamp = 0; if (UInt64.TryParse(parts[1], out timestamp) && timestamp * 1000 > now) { string id = parts[0]; string mac = parts[2]; string ext = parts[3]; if (!String.IsNullOrWhiteSpace(id) && !String.IsNullOrWhiteSpace(mac)) { RemoveBewitFromUri(request); Credential credential = options.CredentialsCallback(id); if (credential != null && credential.IsValid) { var artifacts = new ArtifactsContainer() { Id = id, Nonce = String.Empty, Timestamp = timestamp, Mac = mac.ToBytesFromBase64(), ApplicationSpecificData = ext ?? String.Empty }; var normalizedRequest = new NormalizedRequest(request, artifacts) { IsBewit = true }; var crypto = new Cryptographer(normalizedRequest, artifacts, credential); if (crypto.IsSignatureValid()) // Bewit is for GET and GET must have no request body { return new AuthenticationResult() { IsAuthentic = true, Credential = credential, Artifacts = artifacts, ApplicationSpecificData = ext }; } } } } } } } } return new AuthenticationResult() { IsAuthentic = false }; }