/// <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> /// Parses JWT. /// </summary> /// <param name="rawToken">Raw JWT encoded in base64 url encoding.</param> /// <returns>New instance of <see cref="JsonWebToken"/>.</returns> /// <remarks>For details refer to Json Web Token specification.</remarks> internal static JsonWebToken ParseRawToken(string rawToken) { JsonWebToken result = null; var tokenParts = rawToken.Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries); if (tokenParts.Length > 3 || tokenParts.Length < 2) { throw new SecurityTokenException("Invalid token format."); } var rawJwtHeader = Encoding.UTF8.GetString(JwtTokenUtility.Base64UrlDecode(tokenParts[0])); var rawJwtClaims = Encoding.UTF8.GetString(JwtTokenUtility.Base64UrlDecode(tokenParts[1])); var signature = tokenParts.Length > 2 ? tokenParts[2] : string.Empty; try { var headerSegment = JsonConvert.DeserializeObject <JwtHeaderSegment>(rawJwtHeader); var claimsSegment = JsonConvert.DeserializeObject <JwtClaimsSegment>(rawJwtClaims); result = new JsonWebToken(headerSegment, claimsSegment, signature); } catch (Exception ex) { throw new SecurityTokenException("Failed to parse token", ex); } return(result); }
/// <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)); }
public override void WriteToken(XmlWriter writer, SecurityToken token) { if (token == null) { throw new ArgumentNullException("token"); } // Json Web Token is presented in binary (to quite binary but whatever) format. // WIF does not care about it, WS-* stack works only with XML. Thus, just // wrapping our JWT with XML. var wrappedElement = JwtTokenUtility.WrapInsideBinarySecurityToken(((JsonWebToken)token).GetRawToken()); wrappedElement.WriteTo(writer); }
/// <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 })); }
/// <summary> /// Processes the request. /// </summary> /// <param name="requestContext">The request context.</param> public override void ProcessRequest(ref RequestContext requestContext) { // This is place where all WIF magic happens. For normal SOAP services // WIF pipeline will handle all this stuff behind the scenes. For // RESTful services we have to intercept request before it reaches // WIF pipeline and perform all requiered actions ourself. try { var httpRequest = requestContext.RequestMessage.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty; if (httpRequest == null) { throw new WebFaultException(HttpStatusCode.BadRequest); } string accessToken = null; try { // Extracting raw token from Authorization HTTP header. accessToken = ExtractToken(httpRequest.Headers[HttpRequestHeader.Authorization]); if (string.IsNullOrWhiteSpace(accessToken)) { throw new WebFaultException(HttpStatusCode.Unauthorized); } } catch (WebFaultException) { throw; } catch (Exception) { throw new WebFaultException(HttpStatusCode.BadRequest); } // Wrapping parsed JWT inside XML envelope so WIF would be able to handle it. var wrappedToken = JwtTokenUtility.WrapInsideBinarySecurityToken(accessToken); using (var reader = wrappedToken.CreateReader()) { // Code bellow performs same actions as WIF pipeline does. // Defining list of token handlers that can be used to validate incoming token. var handlersCollection = _wifCredentials.SecurityTokenHandlerCollectionManager[SecurityTokenHandlerCollectionManager.Usage.Default]; if (!handlersCollection.CanReadToken(reader)) { throw new InvalidOperationException("Security token handler is not found."); } var token = handlersCollection.ReadToken(reader); var identities = handlersCollection.ValidateToken(token); var principal = _wifCredentials.ClaimsAuthenticationManager.Authenticate( httpRequest.Method, new ClaimsPrincipal(identities) ); var identityCollection = principal != null ? principal.Identities : new ClaimsIdentityCollection(); // Creating authorization context. This code will set Thread.CurrentPrincipal // to ClaimsPrincipal defined by security token. var authorizationContext = new List <IAuthorizationPolicy> { new AuthorizationPolicy(identityCollection) }.AsReadOnly(); var security = SecurityMessageProperty.GetOrCreate(requestContext.RequestMessage); security.ServiceSecurityContext = new ServiceSecurityContext(authorizationContext); } } catch (Exception) { throw new WebFaultException(HttpStatusCode.BadRequest); } }