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
}
}