public void DecodeToken_WithoutToken_Should_Throw_Exception() { var builder = new JwtBuilder(); Action decodingANullJwt = () => builder.Decode(null); decodingANullJwt.Should() .Throw <ArgumentException>("because null is not a valid value for a token"); }
/// <summary> /// Validate JWT signature. /// This JWT is signed with a MessageBird account unique secret key, ensuring the request is from MessageBird and a specific account. /// The JWT contains the following claims: /// <list type="bullet"> /// <item> /// <term>url_hash</term> /// <description>the raw URL hashed with SHA256 ensuring the URL wasn't altered.</description> /// </item> /// <item> /// <term>payload_hash</term> /// <description>the raw payload hashed with SHA256 ensuring the payload wasn't altered.</description> /// </item> /// <item> /// <term>jti</term> /// <description>a unique token ID to implement an optional non-replay check (NOT validated by default).</description> /// </item> /// <item> /// <term>nbf</term> /// <description>the not before timestamp.</description> /// </item> /// <item> /// <term>exp</term> /// <description>the expiration timestamp is ensuring that a request isn't captured and used at a later time.</description> /// </item> /// <item> /// <term>iss</term> /// <description>the issuer name, always MessageBird.</description> /// </item> /// </list> /// </summary> /// /// <param name="signature">request signature taken from request header MessageBird-Signature-JWT.</param> /// <param name="url">full URL of the receiving endpoint, including scheme, host, path and query.</param> /// <param name="body">request body.</param> /// <returns>A dictionary representing decoded JWT signature token claims</returns> /// <exception cref="RequestValidationException">Throws if signature can not be verified.</exception> public IDictionary <string, string> ValidateSignature(string signature, string url, byte[] body) { IDictionary <string, string> payload; try { payload = _jwtBuilder.Decode <IDictionary <string, string> >(signature); } catch (Exception e) { throw new RequestValidationException(e.Message, e); } string[] requiredClaims = { "iss", "nbf", "exp" }; foreach (string claim in requiredClaims) { if (payload.ContainsKey(claim)) { continue; } throw new RequestValidationException(String.Format("invalid jwt: claim {0} is missing", claim)); } if (!payload["iss"].Equals(TokenIssuer)) { throw new RequestValidationException("invalid jwt: claim iss has wrong value"); } if (!_skipURLValidation) { string expectedURLHash = GetSHA256Hash(Encoding.ASCII.GetBytes(url)); if (!payload["url_hash"].Equals(expectedURLHash)) { throw new RequestValidationException("invalid jwt: claim url_hash is invalid"); } } bool bodyExist = body != null && body.Length > 0; bool payloadHashExist = payload.ContainsKey("payload_hash"); if (!bodyExist && payloadHashExist) { throw new RequestValidationException("invalid jwt: claim payload_hash is set but actual payload is missing"); } if (bodyExist && !payloadHashExist) { throw new RequestValidationException("invalid jwt: claim payload_hash is not set but payload is present"); } if (bodyExist && !payload["payload_hash"].Equals(GetSHA256Hash(body))) { throw new RequestValidationException("invalid jwt: claim payload_hash is invalid"); } return(payload); }
void DecodingWithBuilder_FPs(bool condition) { var builder1 = new JwtBuilder(); Init(); builder1.Decode(invalidToken); // Noncompliant FP, initialization in local function is not tracked void Init() { builder1 = builder1.WithSecret(secret).MustVerifySignature(); } }
public static T Decode <T>(string jsonObject) { var builder = new JwtBuilder(); builder.WithAlgorithm(new HMACSHA256Algorithm()); builder.WithSecret(Environment.GetEnvironmentVariable("secret")); builder.WithVerifySignature(true); T decodedDto = builder.Decode <T>(jsonObject); return(decodedDto); }
void DecodingWithBuilder_FNs(bool condition) { var builder1 = new JwtBuilder(); if (condition) { builder1 = builder1.WithSecret(secret); } builder1.Decode(invalidToken); // FN, this is not SE rule, only linear initialization is considered CreateBuilder().Decode(invalidToken); // FN, cross procedural initialization is not tracked CreateLocalBuilder().Decode(invalidToken); // FN, local function initialization is not tracked JwtBuilder CreateLocalBuilder() => new JwtBuilder().DoNotVerifySignature(); }
/// <summary> /// Attempts to decrypt and deserialize the given encoded string into an object of type T. /// </summary> /// <param name="encodedString"></param> /// <typeparam name="T"></typeparam> /// <returns>On success, returns instance of T. On failure, returns default.</returns> public T Decode <T>(string encodedString) where T : IIdentityProvider { JwtBuilder builder = CreateJwtBuilder().MustVerifySignature(); try { T obj = builder.Decode <T>(encodedString); if (obj.GetExpirationTicks() < DateTime.Now.Ticks) { throw new JwtExpiredException(); } return(obj); } catch (SignatureVerificationException) { return(default);
void DecodingWithBuilder_FNs(bool condition) { var builder1 = new JwtBuilder(); if (condition) { builder1 = builder1.WithSecret(secret); } builder1.Decode(invalidToken); //FP CreateBuilder("abc").Decode(invalidToken); //FP CreateBuilder1("abc").Decode(invalidToken); //FP CreateLocalBuilder().Decode(invalidToken); JwtBuilder CreateLocalBuilder() => new JwtBuilder().DoNotVerifySignature(); }
void DecodingWithBuilder() { var decoded1 = new JwtBuilder() // Noncompliant {{Use only strong cipher algorithms when verifying the signature of this JWT.}} .WithSecret(secret) .Decode(invalidToken); var decoded2 = new JwtBuilder() .WithSecret(secret) .MustVerifySignature() .Decode(invalidToken); var builder1 = new JwtBuilder().WithSecret(secret); builder1.Decode(invalidToken); // Noncompliant try { if (true) { builder1.Decode(invalidToken); // Noncompliant, tracking outside nested block } } finally { } var builder2 = builder1.MustVerifySignature(); builder2.Decode(invalidToken); var builder3 = new JwtBuilder().WithSecret(secret).MustVerifySignature(); builder3.Decode(invalidToken); var builder4 = (((new JwtBuilder()).WithSecret(secret))); builder4.Decode(invalidToken); // Noncompliant var builder5 = new JwtBuilder().WithSecret(secret).DoNotVerifySignature(); builder5.Decode(invalidToken); // Noncompliant var decoded11 = new JwtBuilder() // Noncompliant .WithSecret(secret) .WithVerifySignature(true) .MustVerifySignature() .DoNotVerifySignature() .Decode(invalidToken); var Decoded12 = new JwtBuilder() .WithSecret(secret) .WithVerifySignature(false) .DoNotVerifySignature() .MustVerifySignature() .Decode(invalidToken); var Decoded21 = new JwtBuilder() .WithSecret(secret) .DoNotVerifySignature() .WithVerifySignature(false) .WithVerifySignature(true) .Decode(invalidToken); var Decoded31 = new JwtBuilder() // Noncompliant .WithSecret(secret) .MustVerifySignature() .WithVerifySignature(true) .WithVerifySignature(false) .Decode(invalidToken); }
public IDictionary <string, object> DecodeJwt(string token) { return(_builder.Decode <IDictionary <string, object> >(token)); }
/// <summary> /// The method checks whether the requested operation (i.e. the combination of URL and <see cref="HttpMethod"/>) is protected by one or multiple predefined scopes. Scopes can be defined statically, by applying <see cref="AuthorizationScope"/> attribute /// to the C# method representing a resource or a RESTful operation or dynamically during runtime by using <see cref="DynamicAuthorizationScope"/>s (see <see cref="OAuth2.DynamicScopes"/>). If at least one <see cref="DynamicAuthorizationScope"/> /// is defined for the corresponding method, then all predefined <see cref="AuthorizationScope"/>s are ignored. If there are no scopes defined (dynamically as well as statically), this method grants access even without an access token. /// In case that the resource/operation is protected by scopes, the method checks whether the request contains a valid access token in the Authorization header. Since this framework uses Self-Encoded Access Tokens, the scopes the client is granted to can be decoded from the provided access token and /// compared with the predefined scopes of the method. At least one granted scope must match the list of predefined scopes, otherwise the request is rejected with an <see cref="HttpStatus.Unauthorized"/> immediately (same applies if no access token is included or if the access token is invalid). /// </summary> /// <param name="context">Routed Context</param> public override void Manipulate(RoutedContext context) { //step 1: check whether method has scopes (i.e. is a protected resource) IList <string> scopes = new List <string>(); //check first, whether there are dynamic scopes specifiec foreach (DynamicAuthorizationScope das in _reference.DynamicScopes) { if (das.Path.CompareTo(context.RoutingEntry.Path) == 0) { scopes.Add(das.Scope); } } //if not, check whether there are static scopes via attributes defined if (scopes.Count == 0) { scopes = GetScopesOfMethod(context.RoutingEntry.MethodInfo); } //if there are no scopes, ... if (scopes.Count == 0) { //...do nothing (i.e. the request can be processed as no permission is required for accessing the resource) return; } //step 1a: load access token string token = context.Context.Request.Headers.Get("Authorization"); if (String.IsNullOrWhiteSpace(token)) { throw new HttpRequestException("Missing access token", MimeType.TEXT_PLAN) { Status = HttpStatus.Unauthorized }; } token = token.Replace("bearer ", "").Replace("Bearer ", ""); //step 2: decode token string scope = null; try { IDictionary <string, object> payload = _decoder.Decode <IDictionary <string, object> >(token); if (payload.ContainsKey("scope")) { scope = (string)payload["scope"]; } else { throw new HttpRequestException("Invalid access token", MimeType.TEXT_PLAN) { Status = HttpStatus.Unauthorized }; } } catch (TokenExpiredException) { throw new HttpRequestException("Token has expired", MimeType.TEXT_PLAN) { Status = HttpStatus.Unauthorized }; } catch (SignatureVerificationException) { throw new HttpRequestException("Invalid access token", MimeType.TEXT_PLAN) { Status = HttpStatus.Unauthorized }; } catch (Exception) { throw new HttpRequestException("Invalid access token", MimeType.TEXT_PLAN) { Status = HttpStatus.Unauthorized }; } //step 3: check scope if (String.IsNullOrWhiteSpace(scope)) { throw new HttpRequestException("Invalid scope: you are not allowed to access this resource", MimeType.TEXT_PLAN) { Status = HttpStatus.Forbidden }; } bool match = false; string[] scopess = scope.Split(' '); foreach (string scp in scopess) //for all scopes supported by the access token { foreach (string scpp in scopes) //for all scopes supported by the method { if (scp.CompareTo(scpp) == 0) { match = true; } } } if (!match) { throw new HttpRequestException("Invalid scope: you are not allowed to access this resource", MimeType.TEXT_PLAN) { Status = HttpStatus.Forbidden }; } }