/// <summary> /// Checks if a token is valid and signed by the factory. /// </summary> /// <param name="Token">JWT token.</param> /// <returns>If the token is correctly signed and valid.</returns> public bool IsValid(JwtToken Token) { if (Token.Algorithm != JwtAlgorithm.HS256 || Token.Signature == null) { return(false); } if (Token.Expiration != null || Token.NotBefore != null) { DateTime Now = DateTime.UtcNow; if (Token.Expiration != null && Now >= Token.Expiration.Value + this.timeMargin) { return(false); } if (Token.NotBefore != null && Now < Token.NotBefore.Value - this.timeMargin) { return(false); } } byte[] Signature = Token.Signature; byte[] Hash; int i, c; lock (this.hmacSHA256) { Hash = this.hmacSHA256.ComputeHash(Encoding.ASCII.GetBytes(Token.Header + "." + Token.Payload)); } if ((c = Hash.Length) != Signature.Length) { return(false); } for (i = 0; i < c; i++) { if (Hash[i] != Signature[i]) { return(false); } } return(true); }
/// <summary> /// Encodes an object. /// </summary> /// <param name="Object">Object to encode.</param> /// <param name="Encoding">Desired encoding of text. Can be null if no desired encoding is speified.</param> /// <param name="ContentType">Content Type of encoding. Includes information about any text encodings used.</param> /// <param name="AcceptedContentTypes">Optional array of accepted content types. If array is empty, all content types are accepted.</param> /// <returns>Encoded object.</returns> /// <exception cref="ArgumentException">If the object cannot be encoded.</exception> public byte[] Encode(object Object, Encoding Encoding, out string ContentType, params string[] AcceptedContentTypes) { JwtToken Token = Object as JwtToken; if (Token == null) { throw new ArgumentException("Object not a JWT token.", nameof(Object)); } ContentType = JwtCodec.ContentType; string s = Token.Header + "." + Token.Payload + "." + JwtToken.Base64UrlEncode(Token.Signature); if (Encoding == null) { return(Encoding.ASCII.GetBytes(s)); } else { return(Encoding.GetBytes(s)); } }
/// <summary> /// Checks if the request is authorized. /// </summary> /// <param name="Request">Request object.</param> /// <returns>User object, if authenticated, or null otherwise.</returns> public override async Task <IUser> IsAuthenticated(HttpRequest Request) { HttpFieldAuthorization Authorization = Request.Header.Authorization; if (Authorization != null && Authorization.Value.StartsWith("Bearer ", StringComparison.CurrentCultureIgnoreCase)) { try { string TokenStr = Authorization.Value.Substring(7).Trim(); JwtToken Token = new JwtToken(TokenStr); string UserName = Token.Subject; if (!this.factory.IsValid(Token) || UserName is null) { LoginAuditor.Fail("Login attempt failed.", UserName ?? string.Empty, Request.RemoteEndPoint, "HTTP"); return(null); } IUser User = await this.users.TryGetUser(UserName); if (User is null) { LoginAuditor.Fail("Login attempt failed.", UserName, Request.RemoteEndPoint, "HTTP"); return(null); } else { LoginAuditor.Success("Login successful.", UserName, Request.RemoteEndPoint, "HTTP"); return(User); } } catch (Exception) { return(null); } } return(null); }
/// <summary> /// Checks if a token is valid and signed by the factory. /// </summary> /// <param name="Token">JWT token.</param> /// <returns>If the token is correctly signed and valid.</returns> public bool IsValid(JwtToken Token) { if (Token.Algorithm == null || !(Token.Algorithm is HmacSha256) || Token.Signature == null) { return(false); } if (Token.Expiration != null || Token.NotBefore != null) { DateTime Now = DateTime.UtcNow; if (Token.Expiration != null && Now >= Token.Expiration.Value + this.timeMargin) { return(false); } if (Token.NotBefore != null && Now < Token.NotBefore.Value - this.timeMargin) { return(false); } } return(this.algorithm.IsValid(Token.Header, Token.Payload, Token.Signature)); }
/// <summary> /// Creates a new JWT token. /// </summary> /// <param name="Claims">Claims to include in token. /// /// For a list of public claim names, see: /// https://www.iana.org/assignments/jwt/jwt.xhtml</param> /// <returns>JWT token.</returns> public string Create(params KeyValuePair <string, object>[] Claims) { StringBuilder Json = new StringBuilder("{"); bool First = true; foreach (KeyValuePair <string, object> Claim in Claims) { if (First) { First = false; } else { Json.Append(','); } Json.Append('"'); Json.Append(JSON.Encode(Claim.Key)); Json.Append("\":"); if (Claim.Value == null) { Json.Append("null"); } else if (Claim.Value is string s) { Json.Append('"'); Json.Append(JSON.Encode(s)); Json.Append('"'); } else if (Claim.Value is bool b) { if (b) { Json.Append("true"); } else { Json.Append("false"); } } else if (Claim.Value is DateTime TP) { Json.Append(((int)((TP.ToUniversalTime() - JwtToken.epoch).TotalSeconds)).ToString()); } else if (Claim.Value is int i) { Json.Append(i.ToString()); } else if (Claim.Value is long l) { Json.Append(l.ToString()); } else if (Claim.Value is short sh) { Json.Append(sh.ToString()); } else if (Claim.Value is byte bt) { Json.Append(bt.ToString()); } else { Json.Append('"'); Json.Append(JSON.Encode(Claim.Value.ToString())); Json.Append('"'); } } Json.Append('}'); string PayloadStr = Json.ToString(); byte[] PayloadBin = Encoding.UTF8.GetBytes(PayloadStr); string Payload = JwtToken.Base64UrlEncode(PayloadBin); byte[] Signature; string Token = this.header + "." + Payload; lock (this.hmacSHA256) { Signature = this.hmacSHA256.ComputeHash(Encoding.ASCII.GetBytes(Token)); } Token += "." + JwtToken.Base64UrlEncode(Signature); return(Token); }