access token generation added

This commit is contained in:
2026-01-30 00:18:25 +01:00
Unverified
parent 236a7a0fe1
commit dc12ee75e5
8 changed files with 96 additions and 16 deletions

View File

@@ -10,11 +10,13 @@ public class AuthPasswordHandler : IRequestHandler<AuthPasswordCommand, AuthPass
{ {
private readonly DatabaseContext _databaseContext; private readonly DatabaseContext _databaseContext;
private readonly IPasswordHasher _passwordHasher; private readonly IPasswordHasher _passwordHasher;
private readonly ITokenGenerator _tokenGenerator;
public AuthPasswordHandler(DatabaseContext databaseContext, IPasswordHasher passwordHasher) public AuthPasswordHandler(DatabaseContext databaseContext, IPasswordHasher passwordHasher, ITokenGenerator tokenGenerator)
{ {
_databaseContext = databaseContext; _databaseContext = databaseContext;
_passwordHasher = passwordHasher; _passwordHasher = passwordHasher;
_tokenGenerator = tokenGenerator;
} }
public async Task<AuthPasswordResult> Handle(AuthPasswordCommand request, CancellationToken cancellationToken) public async Task<AuthPasswordResult> Handle(AuthPasswordCommand request, CancellationToken cancellationToken)
@@ -31,6 +33,8 @@ public class AuthPasswordHandler : IRequestHandler<AuthPasswordCommand, AuthPass
return AuthPasswordResult.Failure(); return AuthPasswordResult.Failure();
} }
string accessToken = _tokenGenerator.GenerateAccessToken(account);
return null; return null;
} }
} }

View File

@@ -1,4 +1,5 @@
using MediatR; using MediatR;
using Microsoft.EntityFrameworkCore.Storage;
using TimetableDesigner.Backend.Events.OutboxPattern; using TimetableDesigner.Backend.Events.OutboxPattern;
using TimetableDesigner.Backend.Services.Authentication.Core.Helpers; using TimetableDesigner.Backend.Services.Authentication.Core.Helpers;
using TimetableDesigner.Backend.Services.Authentication.Database; using TimetableDesigner.Backend.Services.Authentication.Database;
@@ -21,20 +22,25 @@ public class RegisterHandler : IRequestHandler<RegisterCommand, RegisterResult>
public async Task<RegisterResult> Handle(RegisterCommand command, CancellationToken cancellationToken) public async Task<RegisterResult> Handle(RegisterCommand command, CancellationToken cancellationToken)
{ {
PasswordHashData hash = _passwordHasher.CreateHash(command.Password); PasswordHashData hash = _passwordHasher.CreateHash(command.Password);
Account account = new Account Account account = new Account
{ {
Email = command.Email, Email = command.Email,
Password = hash.Hash, Password = hash.Hash,
PasswordSalt = hash.Salt, PasswordSalt = hash.Salt,
}; };
await _databaseContext.Accounts.AddAsync(account, cancellationToken);
await _databaseContext.SaveChangesAsync(cancellationToken);
Event eventData = Event.Create(new RegisterEvent(account.Id, account.Email));
await _databaseContext.Events.AddAsync(eventData, cancellationToken);
await _databaseContext.SaveChangesAsync(cancellationToken);
await using (IDbContextTransaction transaction = await _databaseContext.Database.BeginTransactionAsync(cancellationToken))
{
await _databaseContext.Accounts.AddAsync(account, cancellationToken);
await _databaseContext.SaveChangesAsync(cancellationToken);
Event eventData = Event.Create(new RegisterEvent(account.Id, account.Email));
await _databaseContext.Events.AddAsync(eventData, cancellationToken);
await _databaseContext.SaveChangesAsync(cancellationToken);
await transaction.CommitAsync(cancellationToken);
}
return new RegisterResult(account.Id, account.Email); return new RegisterResult(account.Id, account.Email);
} }

View File

@@ -0,0 +1,10 @@
using TimetableDesigner.Backend.Services.Authentication.Database.Model;
namespace TimetableDesigner.Backend.Services.Authentication.Core.Helpers;
public interface ITokenGenerator
{
string GenerateAccessToken(Account account);
Task<string> GenerateRefreshTokenAsync(Account account);
Task<string> ExtendRefreshTokenAsync();
}

View File

@@ -1,27 +1,68 @@
using Microsoft.Extensions.Configuration; using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using TimetableDesigner.Backend.Services.Authentication.Database;
using TimetableDesigner.Backend.Services.Authentication.Database.Model;
using JwtRegisteredClaimNames = Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames;
namespace TimetableDesigner.Backend.Services.Authentication.Core.Helpers; namespace TimetableDesigner.Backend.Services.Authentication.Core.Helpers;
public class TokenGenerator public class TokenGenerator : ITokenGenerator
{ {
/* private readonly IConfiguration _configuration;
private readonly DatabaseContext _databaseContext;
public TokenGenerator(IConfiguration configuration, DatabaseContext databaseContext) public TokenGenerator(IConfiguration configuration, DatabaseContext databaseContext)
{ {
_configuration = configuration;
_databaseContext = databaseContext;
} }
public string GenerateAccessToken(Account account) public string GenerateAccessToken(Account account)
{ {
IConfigurationSection accessTokenSettings = _configuration.GetSection("Tokens")
.GetSection("AccessToken");
int lifetime = accessTokenSettings.GetSection("Lifetime")
.GetValue<int>("Normal");
DateTimeOffset expirationDate = DateTimeOffset.UtcNow.AddMinutes(lifetime);
string stringKey = accessTokenSettings.GetValue<string>("Key")!;
byte[] encodedKey = Encoding.UTF8.GetBytes(stringKey);
SymmetricSecurityKey key = new SymmetricSecurityKey(encodedKey);
string algorithm = accessTokenSettings.GetValue<string>("Algorithm")!;
SecurityTokenDescriptor descriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(
[
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Sub, account.Id.ToString()),
new Claim(JwtRegisteredClaimNames.Exp, expirationDate.UtcTicks.ToString())
]),
Issuer = accessTokenSettings.GetValue<string>("Issuer"),
Audience = accessTokenSettings.GetValue<string>("Audience"),
SigningCredentials = new SigningCredentials(key, algorithm),
Expires = expirationDate.UtcDateTime,
};
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
handler.InboundClaimTypeMap.Clear();
SecurityToken token = handler.CreateToken(descriptor);
return handler.WriteToken(token);
} }
public async Task<string> GenerateRefreshTokenAsync(Account account) public async Task<string> GenerateRefreshTokenAsync(Account account)
{ {
return null;
} }
public async Task<string> ExtendRefreshTokenAsync() public async Task<string> ExtendRefreshTokenAsync()
{ {
return null;
}*/ }
} }

View File

@@ -14,6 +14,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.1" /> <PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.1" />
<PackageReference Include="MediatR" Version="14.0.0" /> <PackageReference Include="MediatR" Version="14.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.15.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -52,6 +52,7 @@ public static class Program
private static IServiceCollection AddHelpers(this IServiceCollection services) private static IServiceCollection AddHelpers(this IServiceCollection services)
{ {
services.AddSingleton<IPasswordHasher, PasswordHasher>(); services.AddSingleton<IPasswordHasher, PasswordHasher>();
services.AddScoped<ITokenGenerator, TokenGenerator>();
return services; return services;
} }

View File

@@ -19,7 +19,7 @@
</PackageReference> </PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
<PackageReference Include="timetabledesigner.backend.events.extensions.aspnetcore.openapi" Version="1.0.2" /> <PackageReference Include="timetabledesigner.backend.events.extensions.aspnetcore.openapi" Version="1.0.2" />
<PackageReference Include="timetabledesigner.backend.events.outboxpattern" Version="1.1.2" /> <PackageReference Include="timetabledesigner.backend.events.outboxpattern" Version="1.1.3" />
<PackageReference Include="TimetableDesigner.Backend.Events.Providers.RabbitMQ" Version="1.0.5" /> <PackageReference Include="TimetableDesigner.Backend.Events.Providers.RabbitMQ" Version="1.0.5" />
</ItemGroup> </ItemGroup>

View File

@@ -9,5 +9,22 @@
"ConnectionStrings": { "ConnectionStrings": {
"Database": "Host=localhost;Port=5433;Database=ttd_authentication;Username=postgres;Password=l4JxOIuSoyod86N;Include Error Detail=True", "Database": "Host=localhost;Port=5433;Database=ttd_authentication;Username=postgres;Password=l4JxOIuSoyod86N;Include Error Detail=True",
"EventQueue": "Hostname=localhost;Port=5672;Username=user;Password=l4JxOIuSoyod86N;ExchangeName=events;QueuePrefix=authentication" "EventQueue": "Hostname=localhost;Port=5672;Username=user;Password=l4JxOIuSoyod86N;ExchangeName=events;QueuePrefix=authentication"
},
"Tokens": {
"AccessToken": {
"Lifetime": {
"Normal": 5
},
"Issuer": "auth.timetabledesigner.com",
"Audience": "timetabledesigner.com",
"Key": "testkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytestkeytest",
"Algorithm": "HS512"
},
"RefreshToken": {
"Lifetime": {
"Normal": 1440,
"Extended": 10080
}
}
} }
} }