370 lines
12 KiB
C#
370 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Net.Http;
|
|
using System.Net.Http.Headers;
|
|
using System.Threading.Tasks;
|
|
using IdentityModel.Client;
|
|
using Microsoft.AspNetCore.Authentication;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc.Filters;
|
|
using xCommons.Constants;
|
|
using xCommons.Extensions;
|
|
using xExceptions.Constants;
|
|
using xExceptions.Models;
|
|
using xIdentityModels.Models;
|
|
using xIdentityService.Interfaces;
|
|
|
|
public static partial class XTokenExtensions {
|
|
/// <summary>
|
|
/// Retrieve Tokens From Headers
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <returns></returns>
|
|
public static XLoginResponse 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<long> ();
|
|
|
|
//
|
|
if (accessToken
|
|
.ToNormalString ()
|
|
.Contains (XAuthorization.TokenIdentifier.ToNormalString ())) {
|
|
accessToken = accessToken.Remove (0, XAuthorization.TokenIdentifier.Length);
|
|
}
|
|
|
|
//
|
|
return new XLoginResponse {
|
|
AccessToken = accessToken,
|
|
RefreshToken = refreshToken,
|
|
ExpiresAt = expiresAt
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve Tokens From HttpContext
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <returns></returns>
|
|
public static async Task<XLoginResponse> 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<long> ();
|
|
|
|
//
|
|
var headerRequestTokens = source.Request.RetrieveTokens ();
|
|
|
|
//
|
|
var result = new XLoginResponse {
|
|
AccessToken = accessToken ?? headerRequestTokens.AccessToken,
|
|
RefreshToken = refreshToken ?? headerRequestTokens.RefreshToken,
|
|
ExpiresAt = expiresAt != 0 ? expiresAt : headerRequestTokens.ExpiresAt
|
|
};
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve Tokens From ActionExecutingContext
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <returns></returns>
|
|
public static async Task<XLoginResponse> RetrieveTokens (this ActionExecutingContext source) {
|
|
//
|
|
var result = await source.HttpContext.RetrieveTokens ();
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve Tokens From XUserInfo instance
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <returns></returns>
|
|
public static XTokenResponse RetrieveTokens (this XUserClaimsInfoDto source) {
|
|
//
|
|
var result = new XLoginResponse {
|
|
AccessToken = source.AccessToken,
|
|
RefreshToken = source.RefreshToken,
|
|
ExpiresAt = source.ExpiresAt
|
|
};
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve Tokens From IHeader Dictionary
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <returns></returns>
|
|
public static XLoginResponse 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<long> ();
|
|
|
|
//
|
|
var result = new XLoginResponse {
|
|
AccessToken = accessToken,
|
|
RefreshToken = refreshToken,
|
|
ExpiresAt = expiresAt
|
|
};
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve IXIdentityProvider from HttpContext
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <returns></returns>
|
|
public static IXIdentityProvider GetXApiIdentityProvider (this FilterContext source) {
|
|
//
|
|
var services = source.HttpContext.RequestServices;
|
|
return (IXIdentityProvider) services
|
|
.GetService (typeof (IXIdentityProvider));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate XLoginResponse instance based on TokenResponse
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add Authorization Tokens in Headers of
|
|
/// Specific IHeaderDictionary instance
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="model"></param>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add Authorization Tokens in Headers of
|
|
/// Specific IHeaderDictionary instance
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="model"></param>
|
|
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}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add Authorization Tokens in Headers of
|
|
/// Specific HttpRequest instance
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="model"></param>
|
|
public static void SetXAuthenticationTokens (this HttpRequest source, XLoginResponse model) {
|
|
source.Headers.SetXAuthenticationTokens (model);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add Authorization Tokens in Headers of
|
|
/// Specific HttpRequest instance
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="model"></param>
|
|
public static void SetXAuthenticationTokens (this HttpRequestMessage source, XLoginResponse model) {
|
|
source.Headers.SetXAuthenticationTokens (model);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="model"></param>
|
|
public static void SetXAuthenticationTokens (this HttpResponse source, XLoginResponse model, string secret) {
|
|
source.Headers.SetXAuthenticationTokens (model, secret);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines a Token Can Refrsh or not
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate a Revision Checksum for new Tokens
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="secret"></param>
|
|
/// <returns></returns>
|
|
public static string GetRevisionChecksum (this XLoginResponse source, string secret) {
|
|
return (source.AccessToken +
|
|
secret +
|
|
source.RefreshToken +
|
|
secret +
|
|
source.ExpiresAt.ToString ()
|
|
)
|
|
.ToMd5String ();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate a Revision Checksum for new Tokens
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="secret"></param>
|
|
/// <returns></returns>
|
|
public static string GetRevisionChecksum (this XTokenResponse source, string secret) {
|
|
return (source.AccessToken +
|
|
secret +
|
|
source.RefreshToken +
|
|
secret +
|
|
source.ExpiresAt.ToString ()
|
|
)
|
|
.ToMd5String ();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check an XLoginResponse instance is Valid with it's revision
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="checksum"></param>
|
|
/// <param name="secret"></param>
|
|
/// <returns></returns>
|
|
public static bool ValidateRevisionChecksum (this XLoginResponse source, string checksum, string secret) {
|
|
//
|
|
var xChecksum = source.GetRevisionChecksum (secret);
|
|
return checksum == xChecksum;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check an XTokenResponse instance is Valid with it's revision
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="checksum"></param>
|
|
/// <param name="secret"></param>
|
|
/// <returns></returns>
|
|
public static bool ValidateRevisionChecksum (this XTokenResponse source, string checksum, string secret) {
|
|
//
|
|
var xChecksum = source.GetRevisionChecksum (secret);
|
|
return checksum == xChecksum;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve Exception Details if Fails
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <returns></returns>
|
|
public static Exception GetException (this TokenResponse source) {
|
|
//
|
|
// Validate Args ...
|
|
if (source.IsNull () || !source.IsError) {
|
|
return XException.ActionFailed.ToException ();
|
|
}
|
|
|
|
//
|
|
XError error = source.ErrorDescription.FromJSON<XError> ();
|
|
return error.ToException ();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert XTokenResponse to Header Dictionary
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <returns></returns>
|
|
public static IDictionary<string, string> ToHeaderDictionary (this XTokenResponse source) {
|
|
//
|
|
var result = new Dictionary<string, string> { { XAuthorization.RefreshToken, source.RefreshToken },
|
|
{ XAuthorization.ExpiresAt, source.ExpiresAt.ToString () }
|
|
};
|
|
|
|
//
|
|
return result;
|
|
}
|
|
} |