Archived
Private
Public Access
1
0

Initial commit

This commit is contained in:
2022-09-04 12:03:44 +02:00
commit 15f48d259f
91 changed files with 22716 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
namespace Backend.Security.Authentication {
public static class JwtTokenAuthentication {
public const string Scheme = "JwtTokenAuthentication";
}
}

View File

@@ -0,0 +1,15 @@
using Backend.Options;
using Microsoft.AspNetCore.Authentication;
namespace Backend.Security.Authentication {
public static class JwtTokenAuthenticationExtensions {
public static AuthenticationBuilder AddJwtTokenAuthentication(this AuthenticationBuilder builder,
IConfiguration configuration) {
builder.Services.AddOptionsFromConfiguration<JwtTokenAuthenticationOptions>(configuration);
return builder.AddScheme<JwtTokenAuthenticationHandlerOptions, JwtTokenAuthenticationHandler>(
JwtTokenAuthentication.Scheme,
_ => { });
}
}
}

View File

@@ -0,0 +1,88 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
using System.Text.Encodings.Web;
using System.Security.Claims;
using Backend.Entitys;
using Backend.Repositorys;
using Backend.Security.Authorization;
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
namespace Backend.Security.Authentication {
public class JwtTokenAuthenticationHandler : AuthenticationHandler<JwtTokenAuthenticationHandlerOptions> {
private readonly TokenRepository _tokens;
private readonly GroupRepository _groups;
private readonly JwtTokenAuthenticationOptions _options;
public JwtTokenAuthenticationHandler(
IOptionsMonitor<JwtTokenAuthenticationHandlerOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IOptions<JwtTokenAuthenticationOptions> tokenOptions,
TokenRepository tokens,
GroupRepository groups)
: base(options, logger, encoder, clock) {
_options = tokenOptions.Value;
_tokens = tokens;
_groups = groups;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() {
if (Request.Headers["Authorization"].Equals(_options.DebugAccessToken))
return AuthenticateResult.Success(GetAuthenticationTicket(null, null, "*"));
var accessToken = GetAccessToken();
if (accessToken == null) return AuthenticateResult.Fail("Access Token invalid");
var refreshToken = _tokens.GetRefreshToken(accessToken.RefreshTokenId);
if (refreshToken == null) return AuthenticateResult.Fail("Refresh Token invalid");
if (!_tokens.ValidateRefreshToken(refreshToken.Id)) return AuthenticateResult.Fail("Refresh Token invalid");
bool valid = _tokens.ValidateAccessToken(accessToken.Id);
return valid
? AuthenticateResult.Success(GetAuthenticationTicket(accessToken, refreshToken))
: AuthenticateResult.Fail("Access Token invalid");
}
private AuthenticationTicket GetAuthenticationTicket(AccessToken accessToken, RefreshToken refreshToken, params string[] customPerms) {
List<Claim> claims = GenerateClaims(accessToken, refreshToken, customPerms);
ClaimsPrincipal principal = new ClaimsPrincipal();
principal.AddIdentity(new ClaimsIdentity(claims, JwtTokenAuthentication.Scheme));
AuthenticationTicket ticket = new AuthenticationTicket(principal, Scheme.Name);
return ticket;
}
private List<Claim> GenerateClaims(AccessToken accessToken, RefreshToken refreshToken, params string[] customPerms) {
List<Claim> claims = new List<Claim>();
if (accessToken is not null && refreshToken is not null) {
claims.AddRange(new List<Claim> {
new(CustomClaimTypes.AccessTokenId, accessToken.Id.ToString()),
new(CustomClaimTypes.RefreshTokenId, refreshToken.Id.ToString()),
new(CustomClaimTypes.UserId, refreshToken.UserId.ToString()),
});
string[] permissions = _groups.GetUserPermissions(refreshToken.UserId).Select(perm => perm.PermissionKey).ToArray();
claims.AddRange(permissions
.Select(permission => new Claim(CustomClaimTypes.Permission, permission)));
}
claims.AddRange(customPerms.Select(perm => new Claim(CustomClaimTypes.Permission, perm)));
return claims;
}
private AccessToken GetAccessToken() {
string key = Request.Headers["Authorization"];
if (string.IsNullOrEmpty(key)) {
key = Request.Query["token"];
}
if (string.IsNullOrEmpty(key))
return null;
AccessToken token = _tokens.GetAccessToken(Guid.Parse(key));
return token;
}
}
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously

View File

@@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Authentication;
namespace Backend.Security.Authentication {
public class JwtTokenAuthenticationHandlerOptions : AuthenticationSchemeOptions {
// Options for the authentication handler.
// Currently: None
}
}

View File

@@ -0,0 +1,11 @@
using Backend.Options;
namespace Backend.Security.Authentication {
public class JwtTokenAuthenticationOptions : OptionsFromConfiguration {
public override string Position => "Authentication";
public string RefreshTokenExpirationTimeInHours { get; set; }
public string AccessTokenExpirationTimeInMinutes { get; set; }
public string DebugAccessToken { get; set; }
}
}

View File

@@ -0,0 +1,5 @@
namespace Backend.Options {
public abstract class OptionsFromConfiguration {
public abstract string Position { get; }
}
}

View File

@@ -0,0 +1,17 @@
namespace Backend.Options {
public static class OptionsFromConfigurationExtensions {
public static T AddOptionsFromConfiguration<T>(this IServiceCollection services, IConfiguration configuration)
where T : OptionsFromConfiguration {
T optionsInstance = (T)Activator.CreateInstance(typeof(T));
if (optionsInstance == null) return null;
string position = optionsInstance.Position;
services.Configure((Action<T>)(options => {
IConfigurationSection section = configuration.GetSection(position);
if (section != null) {
section.Bind(options);
}
}));
return optionsInstance;
}
}
}