-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
431 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
..._JwtBearer/Infrastructure/OAuth2Scheme/Context/SendingTokenIntrospectionRequestContext.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
using Microsoft.AspNetCore.Authentication; | ||
|
||
namespace ProtectedResourceApp_JwtBearer.Infrastructure | ||
{ | ||
public class SendingTokenIntrospectionRequestContext : BaseContext<OAuth2ServerOptions> | ||
{ | ||
public SendingTokenIntrospectionRequestContext(HttpContext context, | ||
AuthenticationScheme scheme, | ||
OAuth2ServerOptions options) | ||
: base(context, scheme, options) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Get or set token; | ||
/// </summary> | ||
public string Token { get; set; } = default!; | ||
|
||
/// <summary> | ||
/// Get or set token type hint. | ||
/// </summary> | ||
public string TokenTypeHint { get; set; } = default!; | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
ProtectedResourceApp_JwtBearer/Infrastructure/OAuth2Scheme/Context/TokenValidatedContext.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
using Microsoft.AspNetCore.Authentication; | ||
|
||
namespace ProtectedResourceApp_JwtBearer.Infrastructure | ||
{ | ||
/// <summary> | ||
/// Define the context for validated token. | ||
/// </summary> | ||
public class TokenValidatedContext : ResultContext<OAuth2ServerOptions> | ||
{ | ||
public TokenValidatedContext(HttpContext context, | ||
AuthenticationScheme scheme, | ||
OAuth2ServerOptions options) | ||
: base(context, scheme, options) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Get or set the token; | ||
/// </summary> | ||
public string Token { get; set; } = default!; | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
...ResourceApp_JwtBearer/Infrastructure/OAuth2Scheme/OAuth2IntrospectionJwtBearerDefaults.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
namespace ProtectedResourceApp_JwtBearer.Infrastructure | ||
{ | ||
/// <summary> | ||
/// Define default value to use in the <see cref="OAuth2ServerHandler"/> for JWT bearer authentication. | ||
/// </summary> | ||
public static class OAuth2IntrospectionJwtBearerDefaults | ||
{ | ||
/// <summary> | ||
/// The default authentication scheme. | ||
/// </summary> | ||
public const string AuthenticationScheme = "Bearer"; | ||
|
||
/// <summary> | ||
/// HttpClient name, that will be resolved from HttpClientFactory. | ||
/// </summary> | ||
public const string NamedBackChannelHttpClient = "OAuth2BackChannelHttpClient"; | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
ProtectedResourceApp_JwtBearer/Infrastructure/OAuth2Scheme/OAuth2JwtBearerExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using Microsoft.AspNetCore.Authentication; | ||
|
||
namespace ProtectedResourceApp_JwtBearer.Infrastructure.OAuth2Scheme | ||
{ | ||
public static class OAuth2JwtBearerExtensions | ||
{ | ||
public static AuthenticationBuilder AddOAuth2IntrospectionJwtBearer(this AuthenticationBuilder builder, | ||
string authenticationSchem,Action<OAuth2ServerOptions> configureOptions) | ||
{ | ||
builder.Services.AddHttpClient(OAuth2IntrospectionJwtBearerDefaults.NamedBackChannelHttpClient); | ||
|
||
// The configureOptions will registerd by defult internaly if is not null in a method named AddSchemeHelper( ... parameters here ... ); | ||
|
||
return builder.AddScheme< OAuth2ServerOptions, OAuth2ServerHandler>(authenticationSchem, configureOptions); | ||
} | ||
} | ||
} |
139 changes: 139 additions & 0 deletions
139
ProtectedResourceApp_JwtBearer/Infrastructure/OAuth2Scheme/OAuth2ServerHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
using Microsoft.AspNetCore.Authentication; | ||
using Microsoft.Extensions.Options; | ||
using ProtectedResourceApp_JwtBearer.Infrastructure.OAuth2Scheme; | ||
using System.Security.Claims; | ||
using System.Text; | ||
using System.Text.Encodings.Web; | ||
|
||
namespace ProtectedResourceApp_JwtBearer.Infrastructure | ||
{ | ||
// Made in love by Mohammed Ahmed Hussien | ||
public class OAuth2ServerHandler : AuthenticationHandler<OAuth2ServerOptions> | ||
{ | ||
private readonly IHttpClientFactory _httpClientFactory; | ||
public OAuth2ServerHandler(IOptionsMonitor<OAuth2ServerOptions> options, | ||
ILoggerFactory logger, | ||
UrlEncoder encoder, | ||
ISystemClock clock, | ||
IHttpClientFactory httpClientFactory) | ||
: base(options, logger, encoder, clock) | ||
{ | ||
_httpClientFactory = httpClientFactory; | ||
} | ||
|
||
protected new OAuth2TokenIntrospectionEvent Events | ||
{ | ||
get => (OAuth2TokenIntrospectionEvent)base.Events!; | ||
set => base.Events = value; | ||
} | ||
|
||
protected override Task<object> CreateEventsAsync() | ||
{ | ||
return Task.FromResult<object>(new OAuth2TokenIntrospectionEvent()); | ||
} | ||
|
||
|
||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() | ||
{ | ||
string token = string.Empty; | ||
try | ||
{ | ||
string authorization = Request.Headers.Authorization.ToString(); | ||
if (string.IsNullOrWhiteSpace(authorization)) | ||
{ | ||
return AuthenticateResult.NoResult(); | ||
} | ||
|
||
if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) | ||
{ | ||
token = authorization.Substring("Bearer ".Length).Trim(); | ||
} | ||
|
||
if (string.IsNullOrWhiteSpace(token)) | ||
{ | ||
return AuthenticateResult.NoResult(); | ||
} | ||
|
||
var requestSendingContext = new SendingTokenIntrospectionRequestContext(Context, Scheme, Options) | ||
{ | ||
Token = token, | ||
TokenTypeHint = Options.TokenTypeHint | ||
}; | ||
await Events.SendingTokenIntrospectionRequest(requestSendingContext); | ||
|
||
|
||
var client = _httpClientFactory.CreateClient(OAuth2IntrospectionJwtBearerDefaults.NamedBackChannelHttpClient); | ||
var values = new List<KeyValuePair<string, string>> | ||
{ | ||
new KeyValuePair<string, string>("token", token), | ||
new KeyValuePair<string, string>("token_type_hint", Options.TokenTypeHint) | ||
}; | ||
|
||
Uri baseUri = new Uri(Options.Authority); | ||
client.BaseAddress = baseUri; | ||
client.DefaultRequestHeaders.Clear(); | ||
client.DefaultRequestHeaders.ConnectionClose = true; | ||
string credential = String.Format("{0}:{1}", Options.ClientId, Options.ClientSecret); | ||
string parameters = Convert.ToBase64String(Encoding.UTF8.GetBytes(credential)); | ||
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", parameters); | ||
using var req = new HttpRequestMessage(HttpMethod.Post, "/Introspections/TokenIntrospect") { Content = new FormUrlEncodedContent(values) }; | ||
using var res = await client.SendAsync(req); | ||
|
||
if (res.IsSuccessStatusCode == false) | ||
{ | ||
return AuthenticateResult.Fail($"Calling introspection endpoint is faild with this status code: {res.StatusCode}"); | ||
} | ||
|
||
|
||
string responseBody = await res.Content.ReadAsStringAsync(); | ||
TokenIntrospectionResponse? result = System.Text.Json.JsonSerializer.Deserialize<TokenIntrospectionResponse>( | ||
responseBody, new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }); | ||
|
||
|
||
if (result?.Active ?? false) | ||
{ | ||
// Create ticket | ||
var authenticationType = Options.AuthenticationType ?? Scheme.Name; | ||
var claimIdentity = new ClaimsIdentity(result.Claims, authenticationType, "name", "role"); | ||
var claimPrinciple = new ClaimsPrincipal(claimIdentity); | ||
|
||
// TODO: here I need token vaidation context | ||
TokenValidatedContext tokenValidatedContext = new TokenValidatedContext(Context, Scheme, Options) | ||
{ | ||
Principal = claimPrinciple, | ||
Token = token | ||
}; | ||
await Events.TokenValidated(tokenValidatedContext); | ||
|
||
if (tokenValidatedContext.Result != null) | ||
{ | ||
return tokenValidatedContext.Result; | ||
} | ||
|
||
if (Options.SaveToken) | ||
{ | ||
tokenValidatedContext.Properties.StoreTokens(new[] | ||
{ | ||
new AuthenticationToken { Name = "access_token", Value = token } | ||
}); | ||
} | ||
tokenValidatedContext.Success(); | ||
return tokenValidatedContext.Result!; | ||
} | ||
else | ||
{ | ||
return AuthenticateResult.Fail($"The token is not active"); | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
return AuthenticateResult.Fail($"There is an exception {ex}"); | ||
} | ||
|
||
|
||
|
||
|
||
|
||
} | ||
} | ||
} |
58 changes: 58 additions & 0 deletions
58
ProtectedResourceApp_JwtBearer/Infrastructure/OAuth2Scheme/OAuth2ServerOptions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
using Microsoft.AspNetCore.Authentication; | ||
|
||
namespace ProtectedResourceApp_JwtBearer.Infrastructure | ||
{ | ||
public class OAuth2ServerOptions : AuthenticationSchemeOptions | ||
{ | ||
/// <summary> | ||
/// Get or set the OAuthrization URI. | ||
/// </summary> | ||
public string Authority { get; set; } = default!; | ||
|
||
/// <summary> | ||
/// Get or set the client Id. | ||
/// </summary> | ||
public string ClientId { get; set; } = default!; | ||
|
||
/// <summary> | ||
/// Get or set the client secret. | ||
/// </summary> | ||
public string ClientSecret { get; set; } = default!; | ||
|
||
// <summary> | ||
// Defines whether the bearer token should be stored in the | ||
// Microsoft.AspNetCore.Authentication.AuthenticationProperties | ||
// after a successful authorization. | ||
// </summary> | ||
public bool SaveToken { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the challenge to put in the "WWW-Authenticate" header. | ||
/// </summary> | ||
public string Challenge { get; set; } = "Bearer"; | ||
|
||
/// <summary> | ||
/// The Backchannel used to retrieve metadata. | ||
/// </summary> | ||
public HttpClient BackChannel { get; set; } = default!; | ||
|
||
/// <summary> | ||
/// Get or set token type hint of the introspection client. | ||
/// </summary> | ||
public string TokenTypeHint { get; set; } = "access_token"; | ||
|
||
/// <summary> | ||
/// Set the authentication type for the authenticated identity, null by default. | ||
/// </summary> | ||
public string? AuthenticationType { get; set; } | ||
/// <summary> | ||
/// Get or set the discovery endpoint to retrive all informations about OAuth2 server. | ||
/// </summary> | ||
//public string MetadataAddress { get; set; } = default!; | ||
|
||
/// <summary> | ||
/// Get or set the required scheme type for <see cref="MetadataAddress"/> | ||
/// </summary> | ||
// public bool RequireHttpsMetadata { get; set; } | ||
} | ||
} |
Oops, something went wrong.