using System; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Security.Claims; using System.Threading.Tasks; using IdentityModel.Client; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.IdentityModel.Tokens; using xCommons.Constants; using xCommons.Extensions; using xExceptions.Constants; using xIdentityModels.Constants; using xIdentityModels.Models; namespace xIdentityModels.Extensions { public static class XTokenExtensions { // #region Identity Token Extensions ... /// /// Convert a SecurityToken to it's Token String /// /// /// /// public static string ToTokenString ( this SecurityToken source, JwtSecurityTokenHandler tokenHandler ) { return tokenHandler.WriteToken (source); } /// /// Validate a Token /// /// /// /// /// public static bool ValidateToken ( this string token, JwtSecurityTokenHandler tokenHandler, TokenValidationParameters tokenValidationParams) { // if (token.IsNullOrEmpty ()) { return false; } // SecurityToken tokenObj = null; try { tokenHandler.ValidateToken (token, tokenValidationParams, out tokenObj); } catch { return false; } // return tokenObj != null; } /// /// Convert a token string to Security Token /// /// /// /// /// public static SecurityToken ToSecurityToken ( this string token, JwtSecurityTokenHandler tokenHandler, TokenValidationParameters tokenValidationParams) { // if (token.IsNullOrEmpty () || !token.ValidateToken (tokenHandler, tokenValidationParams)) { // return null; } // // convert token from String to JWTSecurity Token ... try { return tokenHandler.ReadToken (token); } catch { return null; } } /// /// Parse Action Token and return Corresponding RegistrationRequestDto /// /// /// /// /// public static XActionRequestToken ParseActionRequestToken ( this string token, JwtSecurityTokenHandler tokenHandler, TokenValidationParameters tokenValidationParams) { // if (token.IsNullOrEmpty () || !token.ValidateToken (tokenHandler, tokenValidationParams)) { // return null; } // // convert token from String to JWTSecurity Token ... JwtSecurityToken tokenObj = null; try { tokenObj = tokenHandler.ReadToken (token) as JwtSecurityToken; } catch { return null; } // if (tokenObj == null) { return null; } // // Extract Registered Payload Claims ... var mCurrentStepClaim = tokenObj.Claims .FirstOrDefault (c => c.Type == nameof (XActionRequestToken.Action)); var mContextClaim = tokenObj.Claims .FirstOrDefault (c => c.Type == nameof (XActionRequestToken.Context)); var mTokenClaim = tokenObj.Claims .FirstOrDefault (c => c.Type == nameof (XActionRequestToken.Token)); // // Check All Claims to be NotNull ... if (mCurrentStepClaim == null || mContextClaim == null || mTokenClaim == null) { return null; } // // Reading all Claims Values ... var mCurrentStepStr = mCurrentStepClaim.Value; var mCurrentStep = 0; try { int.TryParse (mCurrentStepStr, out mCurrentStep); } catch { } // var mContext = mContextClaim.Value.IsNullOrEmpty () ? null : mContextClaim.Value.FromJSON (); var mToken = mTokenClaim.Value; // // Create Instance of UserInvite ... var result = new XActionRequestToken { Action = (XAction) mCurrentStep, Context = mContext, Token = mToken }; // return result; } /// /// Check to ActionRequestContext object is same or not /// /// /// /// public static bool IsSameAs ( this XActionRequestContext source, XActionRequestContext dest ) { // if (source == null || dest == null) { return false; } // var result = source.ToJSON () == dest.ToJSON (); // return result; } /// /// Convert a RegistrationRequestDto object to it's jwt token Claims /// /// /// public static Claim[] ToJwtClaims (this XActionRequestToken source) { // // Generate Required Fields ... var action = source.Action; var context = source.Context; var token = source.Token; // // Generate Claims Array ... var claims = new Claim[] { new Claim (nameof (XActionRequestToken.Action), ((int) action).ToString (), ClaimValueTypes.Integer), new Claim (nameof (XActionRequestToken.Context), context == null ? "" : context.ToJSON (), ClaimValueTypes.String), new Claim (nameof (XActionRequestToken.Token), token.IsNullOrEmpty () ? "" : token, ClaimValueTypes.String) }; // // Return Result ... return claims; } /// /// Generate a SecurityToken based on RegistrationRequestDto object /// /// /// /// /// public static SecurityToken ToJwtToken ( this XActionRequestToken source, JwtSecurityTokenHandler tokenHandler, SecurityTokenDescriptor tokenDescriptor) { // // Getting Claims ... var claims = source.ToJwtClaims (); tokenDescriptor.Subject = new ClaimsIdentity (claims); // // Generate Token Object ... var tokenObject = tokenHandler.CreateToken (tokenDescriptor); // // Return Result ... return tokenObject; } #endregion // #region IdentityServer Token Extensions ... /// /// Retrieve Tokens From Headers /// /// /// public static XTokenResponse RetrieveTokens (this HttpRequest source) { // var accessToken = source.Headers[XAuthorization.Header].ToString (); var refreshToken = source.Headers[XAuthorization.RefreshToken].ToString (); // var expiresAtStr = source.Headers[XAuthorization.ExpiresAt].ToString (); long expiresAt = expiresAtStr.ConvertTo (); // if (accessToken .ToNormalString () .Contains (XAuthorization.TokenIdentifier.ToNormalString ())) { accessToken = accessToken.Remove (0, XAuthorization.TokenIdentifier.Length); } // return new XTokenResponse { AccessToken = accessToken, RefreshToken = refreshToken, ExpiresAt = expiresAt }; } /// /// Retrieve Tokens From HttpContext /// /// /// public static async Task RetrieveTokens (this HttpContext source) { // var accessToken = await source.GetTokenAsync (XAuthorization.AccessToken); if (accessToken .ToNormalString () .Contains (XAuthorization.TokenIdentifier.ToNormalString ())) { accessToken = accessToken.Remove (0, XAuthorization.TokenIdentifier.Length); } var refreshToken = await source.GetTokenAsync (XAuthorization.RefreshToken); var expiresAtStr = await source.GetTokenAsync (XAuthorization.ExpiresAt); long expiresAt = expiresAtStr.ConvertTo (); // var headerRequestTokens = source.Request.RetrieveTokens (); // var result = new XTokenResponse { AccessToken = accessToken ?? headerRequestTokens.AccessToken, RefreshToken = refreshToken ?? headerRequestTokens.RefreshToken, ExpiresAt = expiresAt != 0 ? expiresAt : headerRequestTokens.ExpiresAt }; // return result; } /// /// Retrieve Tokens From ActionExecutingContext /// /// /// public static async Task RetrieveTokens (this ActionExecutingContext source) { // var result = await source.HttpContext.RetrieveTokens (); return result; } /// /// Retrieve Tokens From XUserInfo instance /// /// /// public static XTokenResponse RetrieveTokens (this XUserClaimsInfoDto source) { // var result = new XTokenResponse { AccessToken = source.AccessToken, RefreshToken = source.RefreshToken, ExpiresAt = source.ExpiresAt }; // return result; } /// /// Retrieve Tokens From IHeader Dictionary /// /// /// public static XTokenResponse RetrieveTokens (this IHeaderDictionary source) { // var accessToken = source[XAuthorization.Header].ToString () ?? ""; if (accessToken .ToNormalString () .Contains (XAuthorization.TokenIdentifier.ToNormalString ())) { accessToken = accessToken.Remove (0, XAuthorization.TokenIdentifier.Length); } // var refreshToken = source[XAuthorization.RefreshToken].ToString () ?? ""; // var expiresAtStr = source[XAuthorization.ExpiresAt].ToString () ?? ""; var expiresAt = expiresAtStr.ConvertTo (); // var result = new XTokenResponse { AccessToken = accessToken, RefreshToken = refreshToken, ExpiresAt = expiresAt }; // return result; } /// /// Generate XTokenResponse instance based on TokenResponse /// /// /// public static XLoginResponse CreateXLoginResponse (this TokenResponse source) { // if (source.IsNull () || source.IsError) { XException.InvalidToken.Throw (); } // var expiresAt = new DateTimeOffset (DateTime.UtcNow) .ToUnixTimeSeconds () + source.ExpiresIn; // var result = new XLoginResponse { AccessToken = source.AccessToken, RefreshToken = source.RefreshToken, ExpiresAt = expiresAt }; // return result; } /// /// Generate XTokenResponse instance based on TokenResponse /// /// /// public static XTokenResponse CreateXTokenResponse (this TokenResponse source) { // if (source.IsNull () || source.IsError) { XException.InvalidToken.Throw (); } // var expiresAt = new DateTimeOffset (DateTime.UtcNow) .ToUnixTimeSeconds () + source.ExpiresIn; // var result = new XTokenResponse { AccessToken = source.AccessToken, RefreshToken = source.RefreshToken, ExpiresAt = expiresAt }; // return result; } /// /// Convert XLoginResponse to XTokenResponse /// /// /// public static XTokenResponse ToXTokenResponse (this XLoginResponse source) { // if (source.IsNull ()) { return null; } // return new XTokenResponse { AccessToken = source.AccessToken, RefreshToken = source.RefreshToken, ExpiresAt = source.ExpiresAt }; } /// /// Add Authorization Tokens in Headers of /// Specific IHeaderDictionary instance /// /// /// public static void SetXAuthenticationTokens ( this IHeaderDictionary source, XLoginResponse model, string secret = null ) { // source.Remove (XAuthorization.Header); source.Remove (XAuthorization.RefreshToken); source.Remove (XAuthorization.ExpiresAt); source.Remove (XAuthorization.RevisionChecksum); // source.Add (XAuthorization.Header, $"{XAuthorization.TokenIdentifier}{model.AccessToken}"); source.Add (XAuthorization.RefreshToken, $"{model.RefreshToken}"); source.Add (XAuthorization.ExpiresAt, $"{model.ExpiresAt}"); // if (!secret.IsNullOrEmpty ()) { var revisionChecksum = model.GetRevisionChecksum (secret); source.Add (XAuthorization.RevisionChecksum, revisionChecksum); } } /// /// Add Authorization Tokens in Headers of /// Specific IHeaderDictionary instance /// /// /// public static void SetXAuthenticationTokens ( this IHeaderDictionary source, XTokenResponse model, string secret = null ) { // source.Remove (XAuthorization.Header); source.Remove (XAuthorization.RefreshToken); source.Remove (XAuthorization.ExpiresAt); source.Remove (XAuthorization.RevisionChecksum); // source.Add (XAuthorization.Header, $"{XAuthorization.TokenIdentifier}{model.AccessToken}"); source.Add (XAuthorization.RefreshToken, $"{model.RefreshToken}"); source.Add (XAuthorization.ExpiresAt, $"{model.ExpiresAt}"); // if (!secret.IsNullOrEmpty ()) { var revisionChecksum = model.GetRevisionChecksum (secret); source.Add (XAuthorization.RevisionChecksum, revisionChecksum); } } /// /// Add Authorization Tokens in Headers of /// Specific IHeaderDictionary instance /// /// /// public static void SetXAuthenticationTokens ( this HttpRequestHeaders source, XLoginResponse model ) { // source.Remove (XAuthorization.Header); source.Remove (XAuthorization.RefreshToken); source.Remove (XAuthorization.ExpiresAt); source.Remove (XAuthorization.RevisionChecksum); // source.Add (XAuthorization.Header, $"{XAuthorization.TokenIdentifier}{model.AccessToken}"); source.Add (XAuthorization.RefreshToken, $"{model.RefreshToken}"); source.Add (XAuthorization.ExpiresAt, $"{model.ExpiresAt}"); } /// /// Add Authorization Tokens in Headers of /// Specific IHeaderDictionary instance /// /// /// public static void SetXAuthenticationTokens ( this HttpRequestHeaders source, XTokenResponse model ) { // source.Remove (XAuthorization.Header); source.Remove (XAuthorization.RefreshToken); source.Remove (XAuthorization.ExpiresAt); source.Remove (XAuthorization.RevisionChecksum); // source.Add (XAuthorization.Header, $"{XAuthorization.TokenIdentifier}{model.AccessToken}"); source.Add (XAuthorization.RefreshToken, $"{model.RefreshToken}"); source.Add (XAuthorization.ExpiresAt, $"{model.ExpiresAt}"); } /// /// Add Authorization Tokens in Headers of /// Specific HttpRequest instance /// /// /// public static void SetXAuthenticationTokens ( this HttpRequest source, XLoginResponse model ) { source.Headers.SetXAuthenticationTokens (model); } /// /// Add Authorization Tokens in Headers of /// Specific HttpRequest instance /// /// /// public static void SetXAuthenticationTokens ( this HttpRequest source, XTokenResponse model ) { source.Headers.SetXAuthenticationTokens (model); } /// /// Add Authorization Tokens in Headers of /// Specific HttpRequest instance /// /// /// public static void SetXAuthenticationTokens ( this HttpRequestMessage source, XLoginResponse model ) { source.Headers.SetXAuthenticationTokens (model); } /// /// Add Authorization Tokens in Headers of /// Specific HttpRequest instance /// /// /// public static void SetXAuthenticationTokens ( this HttpRequestMessage source, XTokenResponse model ) { source.Headers.SetXAuthenticationTokens (model); } /// /// Add ReNewed Authorization Tokens in Headers of /// Specific Response. /// Later in HttpInterceptors we can Look for AccessToken-ReNewed /// header Value and Renew User Tokens based on it /// /// /// public static void SetXAuthenticationTokens ( this HttpResponse source, XLoginResponse model, string secret ) { source.Headers.SetXAuthenticationTokens (model, secret); } /// /// Add ReNewed Authorization Tokens in Headers of /// Specific Response. /// Later in HttpInterceptors we can Look for AccessToken-ReNewed /// header Value and Renew User Tokens based on it /// /// /// public static void SetXAuthenticationTokens ( this HttpResponse source, XTokenResponse model, string secret ) { source.Headers.SetXAuthenticationTokens (model, secret); } /// /// Determines a Token Can Refrsh or not /// /// /// public static bool IsRefreshable (this XLoginResponse source) { // var containsTokenData = !source.IsNull () && !source.AccessToken.IsNullOrEmpty () && !source.RefreshToken.IsNullOrEmpty (); if (!containsTokenData) { return false; } // var current = new DateTimeOffset (DateTime.UtcNow) .ToUnixTimeSeconds (); // // var result = current > source.ExpiresAt || current > source.ExpiresAt - XAuthorization.ThresholdBeforeTokenExpiration; var result = current > source.ExpiresAt - XAuthorization.ThresholdBeforeTokenExpiration; // return result; } /// /// Determines a Token Can Refrsh or not /// /// /// public static bool IsRefreshable (this XTokenResponse source) { // var containsTokenData = !source.IsNull () && !source.AccessToken.IsNullOrEmpty () && !source.RefreshToken.IsNullOrEmpty (); if (!containsTokenData) { return false; } // var current = new DateTimeOffset (DateTime.UtcNow) .ToUnixTimeSeconds (); // // var result = current > source.ExpiresAt || current > source.ExpiresAt - XAuthorization.ThresholdBeforeTokenExpiration; var result = current > source.ExpiresAt - XAuthorization.ThresholdBeforeTokenExpiration; // return result; } /// /// Generate a Revision Checksum for new Tokens /// /// /// /// public static string GetRevisionChecksum ( this XLoginResponse source, string secret ) { return (source.AccessToken + secret + source.RefreshToken + secret + source.ExpiresAt.ToString () ) .ToMd5String (); } /// /// Generate a Revision Checksum for new Tokens /// /// /// /// public static string GetRevisionChecksum ( this XTokenResponse source, string secret ) { return (source.AccessToken + secret + source.RefreshToken + secret + source.ExpiresAt.ToString () ) .ToMd5String (); } /// /// Check an XLoginResponse instance is Valid with it's revision /// /// /// /// /// public static bool ValidateRevisionChecksum ( this XLoginResponse source, string checksum, string secret ) { // var xChecksum = source.GetRevisionChecksum (secret); return checksum == xChecksum; } /// /// Check an XTokenResponse instance is Valid with it's revision /// /// /// /// /// public static bool ValidateRevisionChecksum ( this XTokenResponse source, string checksum, string secret ) { // var xChecksum = source.GetRevisionChecksum (secret); return checksum == xChecksum; } #endregion } }