/// <summary> /// Creates security token. /// </summary> /// <param name="tokenDescriptor">The token descriptor.</param> /// <returns>Security token.</returns> public override SecurityToken CreateToken(SecurityTokenDescriptor tokenDescriptor) { if (tokenDescriptor == null) { throw new ArgumentNullException("tokenDescriptor"); } // See details in Json Web Token specification. var seconds = (tokenDescriptor.Lifetime.Expires - tokenDescriptor.Lifetime.Created) ?? new TimeSpan(0, 0, 3600); var header = new JwtHeaderSegment(); var claims = new JwtClaimsSegment( tokenDescriptor.TokenIssuerName, tokenDescriptor.AppliesToAddress, DateTime.UtcNow, DateTime.UtcNow + seconds, tokenDescriptor.Subject.Claims ); // See details in Json Web Signature specification. var key = (InMemorySymmetricSecurityKey)tokenDescriptor.SigningCredentials.SigningKey; var mac = new HMACSHA256(key.GetSymmetricKey()); var hash = mac.ComputeHash(Encoding.UTF8.GetBytes(JsonWebToken.GetSigningInput(header, claims))); var jwsCryptoOutput = JwtTokenUtility.Base64UrlEncode(hash); return(new JsonWebToken(header, claims, jwsCryptoOutput)); }
/// <summary> /// Retrieves token signing input. /// </summary> /// <param name="header">JWT header section.</param> /// <param name="payload">JWT payload section.</param> /// <returns>Signing input.</returns> /// <remarks>For details refer to Json Web Signature specification.</remarks> internal static string GetSigningInput(JwtHeaderSegment header, JwtClaimsSegment payload) { var decodedJwsHeaderInput = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header)); var jwsHeaderInput = JwtTokenUtility.Base64UrlEncode(decodedJwsHeaderInput); var decodedJwsPayloadInput = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload)); var jwsPaloadInput = JwtTokenUtility.Base64UrlEncode(decodedJwsPayloadInput); return(string.Format("{0}.{1}", jwsHeaderInput, jwsPaloadInput)); }
/// <summary> /// Validates security token. /// </summary> /// <param name="token">Security token.</param> /// <returns><see cref="ClaimsIdentityCollection"/> stored inside security token.</returns> public override ClaimsIdentityCollection ValidateToken(SecurityToken token) { if (token == null) { throw new ArgumentNullException("token"); } var jwt = (JsonWebToken)token; // Stage 1: Validating token signature. InMemorySymmetricSecurityKey key = null; try { key = (InMemorySymmetricSecurityKey)Configuration.IssuerTokenResolver.ResolveSecurityKey( new KeyNameIdentifierClause(jwt.ClaimsSection.Issuer) ); } catch (Exception) { throw new InvalidOperationException("Failed to resolver isser's key"); } // Signature is checked according to JSON Web Signature specification. var mac = new HMACSHA256(key.GetSymmetricKey()); var cryptoInput = JwtTokenUtility.Base64UrlEncode( mac.ComputeHash(Encoding.UTF8.GetBytes(jwt.GetSigningInput())) ); if (!string.Equals(jwt.Signature, cryptoInput, StringComparison.Ordinal)) { throw new SignatureVerificationFailedException("Token contents do not match signature."); } // Stage 2: Checking whether token is up to date. var utcNow = DateTime.UtcNow; if (utcNow + Configuration.MaxClockSkew < token.ValidFrom) { throw new SecurityTokenNotYetValidException(); } if (utcNow + Configuration.MaxClockSkew > token.ValidTo) { throw new SecurityTokenExpiredException(); } // Stage 3: Checking whether we should even bother talking to specified RP. if (Configuration.AudienceRestriction.AudienceMode != AudienceUriMode.Never) { if (string.IsNullOrWhiteSpace(jwt.ClaimsSection.Audience)) { throw new AudienceUriValidationFailedException("Token does not contain Audience uri."); } var uri = new Uri(jwt.ClaimsSection.Audience); if (!Configuration.AudienceRestriction.AllowedAudienceUris.Contains(uri)) { throw new AudienceUriValidationFailedException( string.Format("Uri {0} is not spceified in audience uri section", uri.ToString()) ); } } // Stage 4: If configured, let WIF check "Replay Token" attack. if (Configuration.DetectReplayedTokens) { DetectReplayedTokens(token); } // Stage 5: Extracting claims from security token. var inputIdentity = new ClaimsIdentity(jwt.ClaimsSection.Claims); // Stage 6: If configured, saving bootstrap token that may be // used by RP for delegation (ActAs and BehalfOf calls). if (Configuration.SaveBootstrapTokens) { inputIdentity.BootstrapToken = token; } return(new ClaimsIdentityCollection(new [] { inputIdentity })); }