Finished database management and user authentication
This commit is contained in:
56
HopFrame.Security/Authentication/HopFrameAuthentication.cs
Normal file
56
HopFrame.Security/Authentication/HopFrameAuthentication.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using HopFrame.Database;
|
||||
using HopFrame.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
||||
namespace HopFrame.Security.Authentication;
|
||||
|
||||
public class HopFrameAuthentication<TDbContext> : AuthenticationHandler<AuthenticationSchemeOptions> where TDbContext : HopDbContextBase {
|
||||
|
||||
public const string SchemeName = "HopCore.Authentication";
|
||||
public static readonly TimeSpan AccessTokenTime = new(0, 0, 5, 0);
|
||||
public static readonly TimeSpan RefreshTokenTime = new(30, 0, 0, 0);
|
||||
|
||||
private readonly TDbContext _context;
|
||||
|
||||
public HopFrameAuthentication(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger,
|
||||
UrlEncoder encoder, ISystemClock clock, TDbContext context) : base(options, logger, encoder, clock) {
|
||||
_context = context;
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() {
|
||||
var accessToken = Request.Headers["Authorization"].ToString();
|
||||
if (string.IsNullOrEmpty(accessToken)) return AuthenticateResult.Fail("No Access Token provided");
|
||||
|
||||
var tokenEntry = await _context.Tokens.SingleOrDefaultAsync(token => token.Token == accessToken);
|
||||
|
||||
if (tokenEntry is null) return AuthenticateResult.Fail("The provided Access Token does not exist");
|
||||
if (tokenEntry.CreatedAt + AccessTokenTime < DateTime.Now) return AuthenticateResult.Fail("The provided Access Token is expired");
|
||||
|
||||
if (!(await _context.Users.AnyAsync(user => user.Id == tokenEntry.UserId)))
|
||||
return AuthenticateResult.Fail("The provided Access Token does not match any user");
|
||||
|
||||
var claims = new List<Claim> {
|
||||
new(HopFrameClaimTypes.AccessTokenId, accessToken),
|
||||
new(HopFrameClaimTypes.UserId, tokenEntry.UserId.ToString())
|
||||
};
|
||||
|
||||
var permissions = await _context.Permissions
|
||||
.Where(perm => perm.UserId == tokenEntry.UserId)
|
||||
.Select(perm => perm.PermissionText)
|
||||
.ToListAsync();
|
||||
|
||||
claims.AddRange(permissions.Select(perm => new Claim(HopFrameClaimTypes.Permission, perm)));
|
||||
|
||||
var principal = new ClaimsPrincipal();
|
||||
principal.AddIdentity(new ClaimsIdentity(claims, SchemeName));
|
||||
return AuthenticateResult.Success(new AuthenticationTicket(principal, Scheme.Name));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using HopFrame.Database;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace HopFrame.Security.Authentication;
|
||||
|
||||
public static class HopFrameAuthenticationExtensions {
|
||||
|
||||
public static AuthenticationBuilder AddHopFrameAuthentication<TDbContext>(this IServiceCollection service) where TDbContext : HopDbContextBase {
|
||||
return service.AddAuthentication(HopFrameAuthentication<TDbContext>.SchemeName).AddScheme<AuthenticationSchemeOptions, HopFrameAuthentication<TDbContext>>(HopFrameAuthentication<TDbContext>.SchemeName, _ => {});
|
||||
}
|
||||
|
||||
}
|
||||
9
HopFrame.Security/Authorization/AuthorizedAttribute.cs
Normal file
9
HopFrame.Security/Authorization/AuthorizedAttribute.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace HopFrame.Security.Authorization;
|
||||
|
||||
public class AuthorizedAttribute : TypeFilterAttribute {
|
||||
public AuthorizedAttribute(params string[] permission) : base(typeof(AuthorizedFilter)) {
|
||||
Arguments = new object[] { permission };
|
||||
}
|
||||
}
|
||||
24
HopFrame.Security/Authorization/AuthorizedFilter.cs
Normal file
24
HopFrame.Security/Authorization/AuthorizedFilter.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
namespace HopFrame.Security.Authorization;
|
||||
|
||||
public class AuthorizedFilter : IAuthorizationFilter {
|
||||
private readonly string[] _permissions;
|
||||
|
||||
public AuthorizedFilter(params string[] permissions) {
|
||||
_permissions = permissions;
|
||||
}
|
||||
|
||||
public void OnAuthorization(AuthorizationFilterContext context) {
|
||||
if (context.Filters.Any(item => item is IAllowAnonymousFilter)) return;
|
||||
|
||||
if (context.HttpContext.User.Identity?.IsAuthenticated == false) {
|
||||
context.Result = new UnauthorizedResult();
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: Check Permissions
|
||||
}
|
||||
}
|
||||
21
HopFrame.Security/Claims/HopFrameClaimTypes.cs
Normal file
21
HopFrame.Security/Claims/HopFrameClaimTypes.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace HopFrame.Security.Claims;
|
||||
|
||||
public static class HopFrameClaimTypes {
|
||||
public const string AccessTokenId = "HopFrame.AccessTokenId";
|
||||
public const string UserId = "HopFrame.UserId";
|
||||
public const string Permission = "HopFrame.Permission";
|
||||
|
||||
public static string GetAccessTokenId(this ClaimsPrincipal principal) {
|
||||
return principal.FindFirstValue(AccessTokenId);
|
||||
}
|
||||
|
||||
public static string GetUserId(this ClaimsPrincipal principal) {
|
||||
return principal.FindFirstValue(UserId);
|
||||
}
|
||||
|
||||
public static string[] GetPermissions(this ClaimsPrincipal principal) {
|
||||
return principal.FindAll(Permission).Select(claim => claim.Value).ToArray();
|
||||
}
|
||||
}
|
||||
9
HopFrame.Security/Claims/ITokenContextBase.cs
Normal file
9
HopFrame.Security/Claims/ITokenContextBase.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using HopFrame.Database.Models;
|
||||
|
||||
namespace HopFrame.Security.Claims;
|
||||
|
||||
public interface ITokenContextBase {
|
||||
bool IsAuthenticated { get; }
|
||||
User User { get; }
|
||||
Guid AccessToken { get; }
|
||||
}
|
||||
23
HopFrame.Security/Claims/TokenContextImplementor.cs
Normal file
23
HopFrame.Security/Claims/TokenContextImplementor.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using HopFrame.Database;
|
||||
using HopFrame.Database.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace HopFrame.Security.Claims;
|
||||
|
||||
public class TokenContextImplementor : ITokenContextBase {
|
||||
private readonly IHttpContextAccessor _accessor;
|
||||
private readonly HopDbContextBase _context;
|
||||
|
||||
public TokenContextImplementor(IHttpContextAccessor accessor, HopDbContextBase context) {
|
||||
_accessor = accessor;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public bool IsAuthenticated => _accessor.HttpContext?.User.Identity?.IsAuthenticated == true;
|
||||
|
||||
public User User => _context.Users
|
||||
.SingleOrDefault(user => user.Id == _accessor.HttpContext.User.GetUserId())?
|
||||
.ToUserModel(_context);
|
||||
|
||||
public Guid AccessToken => Guid.Parse(_accessor.HttpContext?.User.GetAccessTokenId() ?? string.Empty);
|
||||
}
|
||||
20
HopFrame.Security/HopFrame.Security.csproj
Normal file
20
HopFrame.Security/HopFrame.Security.csproj
Normal file
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>disable</Nullable>
|
||||
<RootNamespace>HopFrame.Security</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\HopFrame.Database\HopFrame.Database.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user