Archived
Private
Public Access
1
0

Initial commit

This commit is contained in:
2022-09-04 12:45:01 +02:00
commit f4a01d6a69
11601 changed files with 4206660 additions and 0 deletions

View File

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

View File

@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Configuration;
using WebDesktopBackend.Options;
namespace WebDesktopBackend.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,82 @@
using Microsoft.AspNetCore.Authentication;
using WebDesktopBackend.Entitys.Tokens;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using WebDesktopBackend.Contract.Persistance;
using WebDesktopBackend.Entitys.Permissions;
using WebDesktopBackend.Security.Authorization;
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
namespace WebDesktopBackend.Security.Authentication
{
public class JwtTokenAuthenticationHandler : AuthenticationHandler<JwtTokenAuthenticationHandlerOptions>
{
private readonly ITokenRepository _tokens;
private readonly IGroupRepository _groups;
public JwtTokenAuthenticationHandler(
IOptionsMonitor<JwtTokenAuthenticationHandlerOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
ITokenRepository tokens,
IGroupRepository groups)
: base(options, logger, encoder, clock)
{
_tokens = tokens;
_groups = groups;
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
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) {
List<Claim> claims = GenerateClaims(accessToken, refreshToken);
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) {
List<Claim> claims = new List<Claim>() {
new (CustomClaimTypes.AccessTokenId, accessToken.Id),
new (CustomClaimTypes.RefreshTokenId, refreshToken.Id),
new (CustomClaimTypes.UserId, refreshToken.UserId),
};
Permission[] permissions = _groups.GetUserPermissions(refreshToken.UserId)
.Where(perm => perm.Type == Permission.Allow).ToArray();
claims.AddRange(permissions.Select(permission => new Claim(CustomClaimTypes.Permission, permission.PermissionName)));
return claims;
}
private AccessToken GetAccessToken() {
string key = Request.Headers["Authorization"];
if (string.IsNullOrEmpty(key)) {
key = Request.Query["token"];
}
AccessToken token = _tokens.GetAccessToken(key);
return token;
}
}
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously

View File

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

View File

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

View File

@@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Mvc;
namespace WebDesktopBackend.Security.Authorization
{
public sealed class AuthorizedAttribute : TypeFilterAttribute
{
public AuthorizedAttribute(params string[] permission) : base(typeof(AuthorizedFilter)) {
Arguments = new object[] { permission };
}
}
}

View File

@@ -0,0 +1,67 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Linq;
namespace WebDesktopBackend.Security.Authorization
{
public class AuthorizedFilter : IAuthorizationFilter
{
private readonly string[] _permissions;
public AuthorizedFilter(params string[] permissions)
{
_permissions = permissions;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
if (EndpointHasAllowAnonymousFilter(context))
{
return;
}
if (!IsAuthenticated(context))
{
context.Result = new UnauthorizedResult();
return;
}
if (!ContainsRequiredRole(context))
{
context.Result = new ForbidResult();
return;
}
}
private static bool EndpointHasAllowAnonymousFilter(AuthorizationFilterContext context)
{
return context.Filters.Any(item => item is IAllowAnonymousFilter);
}
private bool IsAuthenticated(AuthorizationFilterContext context)
{
return context.HttpContext.User.Identity.IsAuthenticated;
}
private bool ContainsRequiredRole(AuthorizationFilterContext context) {
if (_permissions.Length == 0)
return true;
if (context.HttpContext.User.HasClaim(CustomClaimTypes.Permission, "*"))
return true;
foreach (var permission in _permissions) {
string[] splice = permission.Split(".");
string cache = "";
foreach (var s in splice) {
cache += s + ".";
if (context.HttpContext.User.HasClaim(CustomClaimTypes.Permission, cache + "*"))
return true;
}
}
return false;
}
}
}

View File

@@ -0,0 +1,17 @@
using System.Linq;
using System.Security.Claims;
namespace WebDesktopBackend.Security.Authorization
{
public static class ClaimsPrincipalExtensions
{
public static string GetAccessTokenId(this ClaimsPrincipal principal) => principal.FindFirstValue(CustomClaimTypes.AccessTokenId);
public static string GetRefreshTokenId(this ClaimsPrincipal principal) => principal.FindFirstValue(CustomClaimTypes.RefreshTokenId);
public static string GetUserId(this ClaimsPrincipal principal) => principal.FindFirstValue(CustomClaimTypes.UserId);
public static string[] GetPermissions(this ClaimsPrincipal principal) => principal.Claims
.Where(claim => claim.Type.Equals(CustomClaimTypes.Permission))
.Select(claim => claim.Value)
.ToArray();
}
}

View File

@@ -0,0 +1,10 @@
namespace WebDesktopBackend.Security.Authorization
{
public static class CustomClaimTypes
{
public const string AccessTokenId = "WebDesktop.AccessTokenId";
public const string RefreshTokenId = "WebDesktop.RefreshTokenId";
public const string UserId = "WebDesktop.UserId";
public const string Permission = "WebDesktop.Permission";
}
}

View File

@@ -0,0 +1,10 @@
namespace WebDesktopBackend.Security
{
public interface ITokenContext {
bool IsAuthenticated {get;}
string UserId {get;}
string AccessTokenId {get;}
string RefreshTokenId {get;}
string[] Permissions { get; }
}
}

View File

@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Http;
using WebDesktopBackend.Security.Authorization;
namespace WebDesktopBackend.Security
{
internal class TokenContext : ITokenContext
{
private readonly IHttpContextAccessor _accessor;
public TokenContext(IHttpContextAccessor accessor) {
_accessor = accessor;
}
public bool IsAuthenticated => _accessor.HttpContext.User.Identity.IsAuthenticated;
public string UserId => _accessor.HttpContext?.User.GetUserId();
public string AccessTokenId => _accessor.HttpContext?.User.GetAccessTokenId();
public string RefreshTokenId => _accessor.HttpContext?.User.GetRefreshTokenId();
public string[] Permissions => _accessor.HttpContext?.User.GetPermissions();
}
}