Initial Commit ...
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
using IdentityModel;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace xIdentityService.Extensions {
|
||||
public static class AuthorizationPolicyBuilderExtensions {
|
||||
/// <summary>
|
||||
/// Adds a policy to check for required scopes.
|
||||
/// </summary>
|
||||
/// <param name="builder"></param>
|
||||
/// <param name="scope">List of any required scopes. The token must contain at least one of the listed scopes.</param>
|
||||
/// <returns></returns>
|
||||
public static AuthorizationPolicyBuilder RequireScope (this AuthorizationPolicyBuilder builder, params string[] scope) {
|
||||
return builder.RequireClaim (JwtClaimTypes.Scope, scope);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper for creating scope-related policies
|
||||
/// </summary>
|
||||
public static class ScopePolicy {
|
||||
/// <summary>
|
||||
/// Creates a policy to check for required scopes.
|
||||
/// </summary>
|
||||
/// <param name="scopes">List of any required scopes. The token must contain at least one of the listed scopes.</param>
|
||||
/// <returns></returns>
|
||||
public static AuthorizationPolicy Create (params string[] scopes) {
|
||||
return new AuthorizationPolicyBuilder ()
|
||||
.RequireScope (scopes)
|
||||
.Build ();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using xCommons.Constants;
|
||||
using xCommons.Extensions;
|
||||
using xIdentityModels.Models;
|
||||
using xIdentityService.Constants;
|
||||
|
||||
namespace xIdentityService.Extensions {
|
||||
public static class HttpContextExtensions {
|
||||
/// <summary>
|
||||
/// Retrieve Authentication Service ...
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public static IAuthenticationService GetAuthenticationService (this HttpContext context) {
|
||||
//
|
||||
var result = context.RequestServices
|
||||
.GetRequiredService<IAuthenticationService> ();
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Specific Token from HttpContext ...
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="tokenName"></param>
|
||||
/// <param name="scheme"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> GetXTokenAsync (
|
||||
this HttpContext context,
|
||||
string tokenName,
|
||||
XAuthenticationScheme scheme = XAuthenticationScheme.XToken
|
||||
) {
|
||||
//
|
||||
// Validate Args ...
|
||||
if (tokenName.IsNullOrEmpty ()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
var authService = context.GetAuthenticationService ();
|
||||
if (authService.IsNull ()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
var authResult = await authService
|
||||
.AuthenticateAsync (
|
||||
context,
|
||||
scheme
|
||||
.GetStringValue ()
|
||||
);
|
||||
|
||||
//
|
||||
var result = authResult?.Properties?
|
||||
.GetTokenValue (tokenName);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Access Token
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> GetAccessToken (this HttpContext context) {
|
||||
//
|
||||
var accessToken = context.Request.Headers[XAuthorization.Header]
|
||||
.ToString ();
|
||||
if (accessToken.IsNullOrEmpty ()) {
|
||||
accessToken = await context
|
||||
.GetTokenAsync (XAuthorization.AccessToken);
|
||||
}
|
||||
|
||||
//
|
||||
if (accessToken
|
||||
.ToNormalString ()
|
||||
.Contains (XAuthorization.TokenIdentifier
|
||||
.ToNormalString ())) {
|
||||
accessToken = accessToken
|
||||
.Remove (0, XAuthorization.TokenIdentifier.Length);
|
||||
}
|
||||
|
||||
//
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Refresh Token
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> GetRefreshToken (this HttpContext context) {
|
||||
//
|
||||
var refreshToken = context.Request.Headers[XAuthorization.RefreshToken]
|
||||
.ToString ();
|
||||
if (refreshToken.IsNullOrEmpty ()) {
|
||||
refreshToken = await context
|
||||
.GetTokenAsync (XAuthorization.RefreshToken);
|
||||
}
|
||||
|
||||
//
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve Access Token Expiration Date
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static async Task<long> GetTokenExpiresAt (this HttpContext context) {
|
||||
//
|
||||
var expiresAtStr = context.Request.Headers[XAuthorization.ExpiresAt]
|
||||
.ToString ();
|
||||
if (expiresAtStr.IsNullOrEmpty ()) {
|
||||
expiresAtStr = await context
|
||||
.GetTokenAsync (XAuthorization.ExpiresAt);
|
||||
}
|
||||
|
||||
//
|
||||
var expiresAt = expiresAtStr.ConvertTo<long> ();
|
||||
|
||||
//
|
||||
return expiresAt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve All Required Tokens
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static async Task<XTokenResponse> RetrieveTokensAsXTokenResponse (HttpContext context) {
|
||||
//
|
||||
var accessToken = await GetAccessToken (context);
|
||||
var refreshToken = await GetRefreshToken (context);
|
||||
var expiresAt = await GetTokenExpiresAt (context);
|
||||
|
||||
//
|
||||
return new XTokenResponse {
|
||||
AccessToken = accessToken,
|
||||
RefreshToken = refreshToken,
|
||||
ExpiresAt = expiresAt
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using xCommons.Extensions;
|
||||
using xIdentityModels.Constants;
|
||||
using xIdentityModels.Models;
|
||||
|
||||
namespace xIdentityService.Extensions {
|
||||
public static class XUserClaimsInfoDtoExtensions {
|
||||
public static string GetUserSelectByParam (
|
||||
this XUserClaimsInfoDto source,
|
||||
XUserSelectBy userSelectBy = XUserSelectBy.Username
|
||||
) {
|
||||
//
|
||||
var result = "";
|
||||
|
||||
//
|
||||
switch (userSelectBy) {
|
||||
//
|
||||
case XUserSelectBy.ID:
|
||||
result = source.UserId;
|
||||
break;
|
||||
|
||||
//
|
||||
case XUserSelectBy.Username:
|
||||
result = source.UserName;
|
||||
break;
|
||||
|
||||
//
|
||||
case XUserSelectBy.Email:
|
||||
result = source.Email;
|
||||
break;
|
||||
|
||||
//
|
||||
case XUserSelectBy.MobileNumber:
|
||||
result = source.PhoneNumber;
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
if (result.IsNullOrEmpty ()) {
|
||||
//
|
||||
if (!source.UserId.IsNullOrEmpty ()) {
|
||||
result = source.UserId;
|
||||
} else if (!source.UserName.IsNullOrEmpty ()) {
|
||||
result = source.UserName;
|
||||
} else if (!source.Email.IsNullOrEmpty ()) {
|
||||
result = source.Email;
|
||||
} else if (!source.PhoneNumber.IsNullOrEmpty ()) {
|
||||
result = source.PhoneNumber;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user