diff --git a/web/backend/Features/Features.Auth/Commands/ConfirmUser/ConfirmUserCommand.cs b/web/backend/Features/Features.Auth/Commands/ConfirmUser/ConfirmUserCommand.cs
index bb80868..0a3c3aa 100644
--- a/web/backend/Features/Features.Auth/Commands/ConfirmUser/ConfirmUserCommand.cs
+++ b/web/backend/Features/Features.Auth/Commands/ConfirmUser/ConfirmUserCommand.cs
@@ -7,4 +7,4 @@ namespace Features.Auth.Commands.ConfirmUser;
/// Validates a confirmation token and confirms the corresponding user account.
///
/// The confirmation token issued to the user, typically delivered via email.
-public record ConfirmUserCommand(string Token) : IRequest;
\ No newline at end of file
+public record ConfirmUserCommand(string Token) : IRequest;
diff --git a/web/backend/Features/Features.Auth/Commands/ConfirmUser/ConfirmUserHandler.cs b/web/backend/Features/Features.Auth/Commands/ConfirmUser/ConfirmUserHandler.cs
index 979698d..230d799 100644
--- a/web/backend/Features/Features.Auth/Commands/ConfirmUser/ConfirmUserHandler.cs
+++ b/web/backend/Features/Features.Auth/Commands/ConfirmUser/ConfirmUserHandler.cs
@@ -13,22 +13,26 @@ namespace Features.Auth.Commands.ConfirmUser;
///
/// Repository used to look up and confirm user accounts.
/// Service used to validate the confirmation token.
-public class ConfirmUserHandler(
- IAuthRepository authRepository,
- ITokenService tokenService
-) : IRequestHandler
+public class ConfirmUserHandler(IAuthRepository authRepository, ITokenService tokenService)
+ : IRequestHandler
{
///
/// Thrown when the confirmation token is invalid or expired, or when the associated user account cannot be found.
///
- public async Task Handle(ConfirmUserCommand request, CancellationToken cancellationToken)
+ public async Task Handle(
+ ConfirmUserCommand request,
+ CancellationToken cancellationToken
+ )
{
- ValidatedToken validatedToken = await tokenService.ValidateConfirmationTokenAsync(request.Token);
+ ValidatedToken validatedToken = await tokenService.ValidateConfirmationTokenAsync(
+ request.Token
+ );
UserAccount? user = await authRepository.ConfirmUserAccountAsync(validatedToken.UserId);
- if (user == null) throw new UnauthorizedException("User account not found");
+ if (user == null)
+ throw new UnauthorizedException("User account not found");
return new ConfirmationPayload(user.UserAccountId, DateTime.UtcNow);
}
-}
\ No newline at end of file
+}
diff --git a/web/backend/Features/Features.Auth/Commands/RefreshToken/RefreshTokenCommand.cs b/web/backend/Features/Features.Auth/Commands/RefreshToken/RefreshTokenCommand.cs
index ed8709e..d065235 100644
--- a/web/backend/Features/Features.Auth/Commands/RefreshToken/RefreshTokenCommand.cs
+++ b/web/backend/Features/Features.Auth/Commands/RefreshToken/RefreshTokenCommand.cs
@@ -7,4 +7,4 @@ namespace Features.Auth.Commands.RefreshToken;
/// Exchanges a valid refresh token for a new access/refresh token pair. Bound directly from the
/// request body of POST /api/auth/refresh.
///
-public record RefreshTokenCommand(string RefreshToken) : IRequest;
\ No newline at end of file
+public record RefreshTokenCommand(string RefreshToken) : IRequest;
diff --git a/web/backend/Features/Features.Auth/Commands/RefreshToken/RefreshTokenHandler.cs b/web/backend/Features/Features.Auth/Commands/RefreshToken/RefreshTokenHandler.cs
index 977e739..91c9d49 100644
--- a/web/backend/Features/Features.Auth/Commands/RefreshToken/RefreshTokenHandler.cs
+++ b/web/backend/Features/Features.Auth/Commands/RefreshToken/RefreshTokenHandler.cs
@@ -12,10 +12,17 @@ namespace Features.Auth.Commands.RefreshToken;
public class RefreshTokenHandler(ITokenService tokenService)
: IRequestHandler
{
- public async Task Handle(RefreshTokenCommand request, CancellationToken cancellationToken)
+ public async Task Handle(
+ RefreshTokenCommand request,
+ CancellationToken cancellationToken
+ )
{
RefreshTokenResult result = await tokenService.RefreshTokenAsync(request.RefreshToken);
- return new LoginPayload(result.UserAccount.UserAccountId, result.UserAccount.Username, result.RefreshToken,
- result.AccessToken);
+ return new LoginPayload(
+ result.UserAccount.UserAccountId,
+ result.UserAccount.Username,
+ result.RefreshToken,
+ result.AccessToken
+ );
}
-}
\ No newline at end of file
+}
diff --git a/web/backend/Features/Features.Auth/Commands/RefreshToken/RefreshTokenValidator.cs b/web/backend/Features/Features.Auth/Commands/RefreshToken/RefreshTokenValidator.cs
index 24070f5..b6f2a2e 100644
--- a/web/backend/Features/Features.Auth/Commands/RefreshToken/RefreshTokenValidator.cs
+++ b/web/backend/Features/Features.Auth/Commands/RefreshToken/RefreshTokenValidator.cs
@@ -12,8 +12,6 @@ public class RefreshTokenValidator : AbstractValidator
///
public RefreshTokenValidator()
{
- RuleFor(x => x.RefreshToken)
- .NotEmpty()
- .WithMessage("Refresh token is required");
+ RuleFor(x => x.RefreshToken).NotEmpty().WithMessage("Refresh token is required");
}
-}
\ No newline at end of file
+}
diff --git a/web/backend/Features/Features.Auth/Commands/RegisterUser/RegisterUserCommand.cs b/web/backend/Features/Features.Auth/Commands/RegisterUser/RegisterUserCommand.cs
index ab2965a..c6d7d05 100644
--- a/web/backend/Features/Features.Auth/Commands/RegisterUser/RegisterUserCommand.cs
+++ b/web/backend/Features/Features.Auth/Commands/RegisterUser/RegisterUserCommand.cs
@@ -25,4 +25,4 @@ public record RegisterUserCommand(
string Email,
DateTime DateOfBirth,
string Password
-) : IRequest;
\ No newline at end of file
+) : IRequest;
diff --git a/web/backend/Features/Features.Auth/Commands/RegisterUser/RegisterUserHandler.cs b/web/backend/Features/Features.Auth/Commands/RegisterUser/RegisterUserHandler.cs
index 39915bd..0adb299 100644
--- a/web/backend/Features/Features.Auth/Commands/RegisterUser/RegisterUserHandler.cs
+++ b/web/backend/Features/Features.Auth/Commands/RegisterUser/RegisterUserHandler.cs
@@ -28,7 +28,10 @@ public class RegisterUserHandler(
///
/// Thrown when an existing account already has the same username or email address.
///
- public async Task Handle(RegisterUserCommand request, CancellationToken cancellationToken)
+ public async Task Handle(
+ RegisterUserCommand request,
+ CancellationToken cancellationToken
+ )
{
await ValidateUserDoesNotExist(request.Username, request.Email);
@@ -48,14 +51,23 @@ public class RegisterUserHandler(
string confirmationToken = tokenService.GenerateConfirmationToken(createdUser);
if (string.IsNullOrEmpty(accessToken) || string.IsNullOrEmpty(refreshToken))
- return new RegistrationPayload(createdUser.UserAccountId, createdUser.Username, string.Empty, string.Empty,
- false);
+ return new RegistrationPayload(
+ createdUser.UserAccountId,
+ createdUser.Username,
+ string.Empty,
+ string.Empty,
+ false
+ );
bool emailSent = false;
try
{
await mediator.Send(
- new SendRegistrationEmailCommand(createdUser.FirstName, createdUser.Email, confirmationToken),
+ new SendRegistrationEmailCommand(
+ createdUser.FirstName,
+ createdUser.Email,
+ confirmationToken
+ ),
cancellationToken
);
emailSent = true;
@@ -67,8 +79,13 @@ public class RegisterUserHandler(
// ignored
}
- return new RegistrationPayload(createdUser.UserAccountId, createdUser.Username, refreshToken, accessToken,
- emailSent);
+ return new RegistrationPayload(
+ createdUser.UserAccountId,
+ createdUser.Username,
+ refreshToken,
+ accessToken,
+ emailSent
+ );
}
///
@@ -82,4 +99,4 @@ public class RegisterUserHandler(
if (existingUsername != null || existingEmail != null)
throw new ConflictException("Username or email already exists");
}
-}
\ No newline at end of file
+}
diff --git a/web/backend/Features/Features.Auth/Commands/RegisterUser/RegisterUserValidator.cs b/web/backend/Features/Features.Auth/Commands/RegisterUser/RegisterUserValidator.cs
index bca2aaa..04125df 100644
--- a/web/backend/Features/Features.Auth/Commands/RegisterUser/RegisterUserValidator.cs
+++ b/web/backend/Features/Features.Auth/Commands/RegisterUser/RegisterUserValidator.cs
@@ -61,8 +61,6 @@ public class RegisterUserValidator : AbstractValidator
.Matches("[0-9]")
.WithMessage("Password must contain at least one number")
.Matches("[^a-zA-Z0-9]")
- .WithMessage(
- "Password must contain at least one special character"
- );
+ .WithMessage("Password must contain at least one special character");
}
-}
\ No newline at end of file
+}
diff --git a/web/backend/Features/Features.Auth/Commands/ResendConfirmationEmail/ResendConfirmationEmailCommand.cs b/web/backend/Features/Features.Auth/Commands/ResendConfirmationEmail/ResendConfirmationEmailCommand.cs
index 66c4fbf..5b276fc 100644
--- a/web/backend/Features/Features.Auth/Commands/ResendConfirmationEmail/ResendConfirmationEmailCommand.cs
+++ b/web/backend/Features/Features.Auth/Commands/ResendConfirmationEmail/ResendConfirmationEmailCommand.cs
@@ -6,4 +6,4 @@ namespace Features.Auth.Commands.ResendConfirmationEmail;
/// Resends the account confirmation email to a user, generating a fresh confirmation token.
///
/// The unique identifier of the user requesting the resend.
-public record ResendConfirmationEmailCommand(Guid UserId) : IRequest;
\ No newline at end of file
+public record ResendConfirmationEmailCommand(Guid UserId) : IRequest;
diff --git a/web/backend/Features/Features.Auth/Commands/ResendConfirmationEmail/ResendConfirmationEmailHandler.cs b/web/backend/Features/Features.Auth/Commands/ResendConfirmationEmail/ResendConfirmationEmailHandler.cs
index 5049260..33eb3f9 100644
--- a/web/backend/Features/Features.Auth/Commands/ResendConfirmationEmail/ResendConfirmationEmailHandler.cs
+++ b/web/backend/Features/Features.Auth/Commands/ResendConfirmationEmail/ResendConfirmationEmailHandler.cs
@@ -23,12 +23,17 @@ public class ResendConfirmationEmailHandler(
IMediator mediator
) : IRequestHandler
{
- public async Task Handle(ResendConfirmationEmailCommand request, CancellationToken cancellationToken)
+ public async Task Handle(
+ ResendConfirmationEmailCommand request,
+ CancellationToken cancellationToken
+ )
{
UserAccount? user = await authRepository.GetUserByIdAsync(request.UserId);
- if (user == null) return; // Silent return to prevent user enumeration
+ if (user == null)
+ return; // Silent return to prevent user enumeration
- if (await authRepository.IsUserVerifiedAsync(request.UserId)) return; // Already confirmed, no-op
+ if (await authRepository.IsUserVerifiedAsync(request.UserId))
+ return; // Already confirmed, no-op
string confirmationToken = tokenService.GenerateConfirmationToken(user);
await mediator.Send(
@@ -36,4 +41,4 @@ public class ResendConfirmationEmailHandler(
cancellationToken
);
}
-}
\ No newline at end of file
+}
diff --git a/web/backend/Features/Features.Auth/Controllers/AuthController.cs b/web/backend/Features/Features.Auth/Controllers/AuthController.cs
index 12fe44a..308c36d 100644
--- a/web/backend/Features/Features.Auth/Controllers/AuthController.cs
+++ b/web/backend/Features/Features.Auth/Controllers/AuthController.cs
@@ -40,11 +40,14 @@ public class AuthController(IMediator mediator) : ControllerBase
)
{
RegistrationPayload payload = await mediator.Send(command);
- return Created("/", new ResponseBody
- {
- Message = "User registered successfully.",
- Payload = payload
- });
+ return Created(
+ "/",
+ new ResponseBody
+ {
+ Message = "User registered successfully.",
+ Payload = payload,
+ }
+ );
}
///
@@ -61,11 +64,13 @@ public class AuthController(IMediator mediator) : ControllerBase
public async Task>> Login([FromBody] LoginQuery query)
{
LoginPayload payload = await mediator.Send(query);
- return Ok(new ResponseBody
- {
- Message = "Logged in successfully.",
- Payload = payload
- });
+ return Ok(
+ new ResponseBody
+ {
+ Message = "Logged in successfully.",
+ Payload = payload,
+ }
+ );
}
///
@@ -78,14 +83,18 @@ public class AuthController(IMediator mediator) : ControllerBase
/// The confirmation token supplied via the confirmation email link.
/// A 200 OK result wrapping a of .
[HttpPost("confirm")]
- public async Task>> Confirm([FromQuery] string token)
+ public async Task>> Confirm(
+ [FromQuery] string token
+ )
{
ConfirmationPayload payload = await mediator.Send(new ConfirmUserCommand(token));
- return Ok(new ResponseBody
- {
- Message = "User with ID " + payload.UserAccountId + " is confirmed.",
- Payload = payload
- });
+ return Ok(
+ new ResponseBody
+ {
+ Message = "User with ID " + payload.UserAccountId + " is confirmed.",
+ Payload = payload,
+ }
+ );
}
///
@@ -119,10 +128,12 @@ public class AuthController(IMediator mediator) : ControllerBase
)
{
LoginPayload payload = await mediator.Send(command);
- return Ok(new ResponseBody
- {
- Message = "Token refreshed successfully.",
- Payload = payload
- });
+ return Ok(
+ new ResponseBody
+ {
+ Message = "Token refreshed successfully.",
+ Payload = payload,
+ }
+ );
}
-}
\ No newline at end of file
+}
diff --git a/web/backend/Features/Features.Auth/DependencyInjection/FeaturesAuthServiceCollectionExtensions.cs b/web/backend/Features/Features.Auth/DependencyInjection/FeaturesAuthServiceCollectionExtensions.cs
index 16c844e..7e47573 100644
--- a/web/backend/Features/Features.Auth/DependencyInjection/FeaturesAuthServiceCollectionExtensions.cs
+++ b/web/backend/Features/Features.Auth/DependencyInjection/FeaturesAuthServiceCollectionExtensions.cs
@@ -17,4 +17,4 @@ public static class FeaturesAuthServiceCollectionExtensions
services.AddScoped();
return services;
}
-}
\ No newline at end of file
+}
diff --git a/web/backend/Features/Features.Auth/Dtos/AuthPayloads.cs b/web/backend/Features/Features.Auth/Dtos/AuthPayloads.cs
index 0b1bab4..64e5a85 100644
--- a/web/backend/Features/Features.Auth/Dtos/AuthPayloads.cs
+++ b/web/backend/Features/Features.Auth/Dtos/AuthPayloads.cs
@@ -35,4 +35,4 @@ public record RegistrationPayload(
///
/// The unique identifier of the user account that was confirmed.
/// The date and time at which the account was confirmed.
-public record ConfirmationPayload(Guid UserAccountId, DateTime ConfirmedDate);
\ No newline at end of file
+public record ConfirmationPayload(Guid UserAccountId, DateTime ConfirmedDate);
diff --git a/web/backend/Features/Features.Auth/Features.Auth.csproj b/web/backend/Features/Features.Auth/Features.Auth.csproj
index 7adcdc9..3710b91 100644
--- a/web/backend/Features/Features.Auth/Features.Auth.csproj
+++ b/web/backend/Features/Features.Auth/Features.Auth.csproj
@@ -7,16 +7,16 @@
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/web/backend/Features/Features.Auth/Queries/Login/LoginHandler.cs b/web/backend/Features/Features.Auth/Queries/Login/LoginHandler.cs
index 6306d97..d3ee64d 100644
--- a/web/backend/Features/Features.Auth/Queries/Login/LoginHandler.cs
+++ b/web/backend/Features/Features.Auth/Queries/Login/LoginHandler.cs
@@ -46,4 +46,4 @@ public class LoginHandler(
return new LoginPayload(user.UserAccountId, user.Username, refreshToken, accessToken);
}
-}
\ No newline at end of file
+}
diff --git a/web/backend/Features/Features.Auth/Queries/Login/LoginQuery.cs b/web/backend/Features/Features.Auth/Queries/Login/LoginQuery.cs
index 3ae72cb..081e6aa 100644
--- a/web/backend/Features/Features.Auth/Queries/Login/LoginQuery.cs
+++ b/web/backend/Features/Features.Auth/Queries/Login/LoginQuery.cs
@@ -7,4 +7,4 @@ namespace Features.Auth.Queries.Login;
/// Authenticates a user using their username and password and issues new tokens. Bound directly
/// from the request body of POST /api/auth/login.
///
-public record LoginQuery(string Username, string Password) : IRequest;
\ No newline at end of file
+public record LoginQuery(string Username, string Password) : IRequest;
diff --git a/web/backend/Features/Features.Auth/Queries/Login/LoginValidator.cs b/web/backend/Features/Features.Auth/Queries/Login/LoginValidator.cs
index 6b60c07..8458414 100644
--- a/web/backend/Features/Features.Auth/Queries/Login/LoginValidator.cs
+++ b/web/backend/Features/Features.Auth/Queries/Login/LoginValidator.cs
@@ -17,4 +17,4 @@ public class LoginValidator : AbstractValidator
RuleFor(x => x.Password).NotEmpty().WithMessage("Password is required");
}
-}
\ No newline at end of file
+}
diff --git a/web/backend/Features/Features.Auth/Repository/AuthRepository.cs b/web/backend/Features/Features.Auth/Repository/AuthRepository.cs
index 7f4eb6a..03fe138 100644
--- a/web/backend/Features/Features.Auth/Repository/AuthRepository.cs
+++ b/web/backend/Features/Features.Auth/Repository/AuthRepository.cs
@@ -81,8 +81,8 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
}
}
- return await GetUserByIdAsync(userAccountId) ??
- throw new Exception("Failed to retrieve newly registered user.");
+ return await GetUserByIdAsync(userAccountId)
+ ?? throw new Exception("Failed to retrieve newly registered user.");
}
///
@@ -92,9 +92,7 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
/// Email address to search for
/// UserAccount if found, null otherwise
/// Thrown when the database command fails.
- public async Task GetUserByEmailAsync(
- string email
- )
+ public async Task GetUserByEmailAsync(string email)
{
await using DbConnection connection = await CreateConnection();
await using DbCommand command = connection.CreateCommand();
@@ -114,9 +112,7 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
/// Username to search for
/// UserAccount if found, null otherwise
/// Thrown when the database command fails.
- public async Task GetUserByUsernameAsync(
- string username
- )
+ public async Task GetUserByUsernameAsync(string username)
{
await using DbConnection connection = await CreateConnection();
await using DbCommand command = connection.CreateCommand();
@@ -136,9 +132,7 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
/// ID of the user account
/// Active UserCredential if found, null otherwise
/// Thrown when the database command fails.
- public async Task GetActiveCredentialByUserAccountIdAsync(
- Guid userAccountId
- )
+ public async Task GetActiveCredentialByUserAccountIdAsync(Guid userAccountId)
{
await using DbConnection connection = await CreateConnection();
await using DbCommand command = connection.CreateCommand();
@@ -158,10 +152,7 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
/// ID of the user account
/// New hashed password
/// Thrown when the database command fails.
- public async Task RotateCredentialAsync(
- Guid userAccountId,
- string newPasswordHash
- )
+ public async Task RotateCredentialAsync(Guid userAccountId, string newPasswordHash)
{
await using DbConnection connection = await CreateConnection();
await using DbCommand command = connection.CreateCommand();
@@ -180,9 +171,7 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
/// ID of the user account
/// UserAccount if found, null otherwise
/// Thrown when the database command fails.
- public async Task GetUserByIdAsync(
- Guid userAccountId
- )
+ public async Task GetUserByIdAsync(Guid userAccountId)
{
await using DbConnection connection = await CreateConnection();
await using DbCommand command = connection.CreateCommand();
@@ -207,15 +196,15 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
/// Thrown when the database command fails for a reason other than
/// a duplicate verification record.
///
- public async Task ConfirmUserAccountAsync(
- Guid userAccountId
- )
+ public async Task ConfirmUserAccountAsync(Guid userAccountId)
{
UserAccount? user = await GetUserByIdAsync(userAccountId);
- if (user == null) return null;
+ if (user == null)
+ return null;
// Idempotency: if already verified, treat as successful confirmation.
- if (await IsUserVerifiedAsync(userAccountId)) return user;
+ if (await IsUserVerifiedAsync(userAccountId))
+ return user;
await using DbConnection connection = await CreateConnection();
await using DbCommand command = connection.CreateCommand();
@@ -270,15 +259,12 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
return ex.Number == 2601 || ex.Number == 2627;
}
-
///
/// Maps a data reader row to a UserAccount entity.
///
/// The data reader positioned on the row to map.
/// The mapped instance.
- protected override UserAccount MapToEntity(
- DbDataReader reader
- )
+ protected override UserAccount MapToEntity(DbDataReader reader)
{
return new UserAccount
{
@@ -292,9 +278,7 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
? null
: reader.GetDateTime(reader.GetOrdinal("UpdatedAt")),
DateOfBirth = reader.GetDateTime(reader.GetOrdinal("DateOfBirth")),
- Timer = reader.IsDBNull(reader.GetOrdinal("Timer"))
- ? null
- : (byte[])reader["Timer"]
+ Timer = reader.IsDBNull(reader.GetOrdinal("Timer")) ? null : (byte[])reader["Timer"],
};
}
@@ -308,12 +292,10 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
{
UserCredential entity = new()
{
- UserCredentialId = reader.GetGuid(
- reader.GetOrdinal("UserCredentialId")
- ),
+ UserCredentialId = reader.GetGuid(reader.GetOrdinal("UserCredentialId")),
UserAccountId = reader.GetGuid(reader.GetOrdinal("UserAccountId")),
Hash = reader.GetString(reader.GetOrdinal("Hash")),
- CreatedAt = reader.GetDateTime(reader.GetOrdinal("CreatedAt"))
+ CreatedAt = reader.GetDateTime(reader.GetOrdinal("CreatedAt")),
};
// Optional columns
@@ -327,7 +309,8 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
"Timer",
StringComparison.OrdinalIgnoreCase
)
- ) ?? false;
+ )
+ ?? false;
if (hasTimer)
entity.Timer = reader.IsDBNull(reader.GetOrdinal("Timer"))
@@ -344,15 +327,11 @@ public class AuthRepository(ISqlConnectionFactory connectionFactory)
/// The command to add the parameter to.
/// The parameter name (including any prefix, e.g. "@Username").
/// The parameter value, or null to bind .
- private static void AddParameter(
- DbCommand command,
- string name,
- object? value
- )
+ private static void AddParameter(DbCommand command, string name, object? value)
{
DbParameter p = command.CreateParameter();
p.ParameterName = name;
p.Value = value ?? DBNull.Value;
command.Parameters.Add(p);
}
-}
\ No newline at end of file
+}
diff --git a/web/backend/Features/Features.Auth/Repository/IAuthRepository.cs b/web/backend/Features/Features.Auth/Repository/IAuthRepository.cs
index 5c791b7..d405891 100644
--- a/web/backend/Features/Features.Auth/Repository/IAuthRepository.cs
+++ b/web/backend/Features/Features.Auth/Repository/IAuthRepository.cs
@@ -49,9 +49,7 @@ public interface IAuthRepository
///
/// ID of the user account
/// Active UserCredential if found, null otherwise
- Task GetActiveCredentialByUserAccountIdAsync(
- Guid userAccountId
- );
+ Task GetActiveCredentialByUserAccountIdAsync(Guid userAccountId);
///
/// Rotates a user's credential by invalidating all existing credentials and creating a new one.
@@ -81,4 +79,4 @@ public interface IAuthRepository
/// ID of the user account
/// True if the user has a verification record, false otherwise
Task IsUserVerifiedAsync(Guid userAccountId);
-}
\ No newline at end of file
+}
diff --git a/web/backend/Features/Features.Auth/Services/ITokenService.cs b/web/backend/Features/Features.Auth/Services/ITokenService.cs
index 09b34ab..4285367 100644
--- a/web/backend/Features/Features.Auth/Services/ITokenService.cs
+++ b/web/backend/Features/Features.Auth/Services/ITokenService.cs
@@ -15,7 +15,7 @@ public enum TokenType
RefreshToken,
/// A short-lived token used to confirm a user's email/account.
- ConfirmationToken
+ ConfirmationToken,
}
///
@@ -32,11 +32,7 @@ public record ValidatedToken(Guid UserId, string Username, ClaimsPrincipal Princ
/// The user account associated with the refreshed session.
/// The newly issued refresh token.
/// The newly issued access token.
-public record RefreshTokenResult(
- UserAccount UserAccount,
- string RefreshToken,
- string AccessToken
-);
+public record RefreshTokenResult(UserAccount UserAccount, string RefreshToken, string AccessToken);
///
/// Defines the expiration windows, in hours, for each type of token issued by .
@@ -85,7 +81,8 @@ public interface ITokenService
/// The enum type identifying which kind of token to generate. Must be .
/// The user account to generate the token for.
/// The signed token string corresponding to the requested token type.
- string GenerateToken(UserAccount user) where T : struct, Enum;
+ string GenerateToken(UserAccount user)
+ where T : struct, Enum;
///
/// Validates an access token.
@@ -114,4 +111,4 @@ public interface ITokenService
/// The refresh token string to validate and exchange.
/// A containing the user and newly issued tokens.
Task RefreshTokenAsync(string refreshTokenString);
-}
\ No newline at end of file
+}
diff --git a/web/backend/Features/Features.Auth/Services/TokenService.cs b/web/backend/Features/Features.Auth/Services/TokenService.cs
index e9b4a34..1bbf13f 100644
--- a/web/backend/Features/Features.Auth/Services/TokenService.cs
+++ b/web/backend/Features/Features.Auth/Services/TokenService.cs
@@ -29,25 +29,28 @@ public class TokenService : ITokenService
/// Thrown when any of the ACCESS_TOKEN_SECRET, REFRESH_TOKEN_SECRET, or
/// CONFIRMATION_TOKEN_SECRET environment variables are not set.
///
- public TokenService(
- ITokenInfrastructure tokenInfrastructure,
- IAuthRepository authRepository
- )
+ public TokenService(ITokenInfrastructure tokenInfrastructure, IAuthRepository authRepository)
{
_tokenInfrastructure = tokenInfrastructure;
_authRepository = authRepository;
- _accessTokenSecret = Environment.GetEnvironmentVariable("ACCESS_TOKEN_SECRET")
- ?? throw new InvalidOperationException(
- "ACCESS_TOKEN_SECRET environment variable is not set");
+ _accessTokenSecret =
+ Environment.GetEnvironmentVariable("ACCESS_TOKEN_SECRET")
+ ?? throw new InvalidOperationException(
+ "ACCESS_TOKEN_SECRET environment variable is not set"
+ );
- _refreshTokenSecret = Environment.GetEnvironmentVariable("REFRESH_TOKEN_SECRET")
- ?? throw new InvalidOperationException(
- "REFRESH_TOKEN_SECRET environment variable is not set");
+ _refreshTokenSecret =
+ Environment.GetEnvironmentVariable("REFRESH_TOKEN_SECRET")
+ ?? throw new InvalidOperationException(
+ "REFRESH_TOKEN_SECRET environment variable is not set"
+ );
- _confirmationTokenSecret = Environment.GetEnvironmentVariable("CONFIRMATION_TOKEN_SECRET")
- ?? throw new InvalidOperationException(
- "CONFIRMATION_TOKEN_SECRET environment variable is not set");
+ _confirmationTokenSecret =
+ Environment.GetEnvironmentVariable("CONFIRMATION_TOKEN_SECRET")
+ ?? throw new InvalidOperationException(
+ "CONFIRMATION_TOKEN_SECRET environment variable is not set"
+ );
}
///
@@ -59,7 +62,12 @@ public class TokenService : ITokenService
public string GenerateAccessToken(UserAccount user)
{
DateTime expiresAt = DateTime.UtcNow.AddHours(TokenServiceExpirationHours.AccessTokenHours);
- return _tokenInfrastructure.GenerateJwt(user.UserAccountId, user.Username, expiresAt, _accessTokenSecret);
+ return _tokenInfrastructure.GenerateJwt(
+ user.UserAccountId,
+ user.Username,
+ expiresAt,
+ _accessTokenSecret
+ );
}
///
@@ -70,8 +78,15 @@ public class TokenService : ITokenService
/// The signed refresh token string.
public string GenerateRefreshToken(UserAccount user)
{
- DateTime expiresAt = DateTime.UtcNow.AddHours(TokenServiceExpirationHours.RefreshTokenHours);
- return _tokenInfrastructure.GenerateJwt(user.UserAccountId, user.Username, expiresAt, _refreshTokenSecret);
+ DateTime expiresAt = DateTime.UtcNow.AddHours(
+ TokenServiceExpirationHours.RefreshTokenHours
+ );
+ return _tokenInfrastructure.GenerateJwt(
+ user.UserAccountId,
+ user.Username,
+ expiresAt,
+ _refreshTokenSecret
+ );
}
///
@@ -82,8 +97,15 @@ public class TokenService : ITokenService
/// The signed confirmation token string.
public string GenerateConfirmationToken(UserAccount user)
{
- DateTime expiresAt = DateTime.UtcNow.AddHours(TokenServiceExpirationHours.ConfirmationTokenHours);
- return _tokenInfrastructure.GenerateJwt(user.UserAccountId, user.Username, expiresAt, _confirmationTokenSecret);
+ DateTime expiresAt = DateTime.UtcNow.AddHours(
+ TokenServiceExpirationHours.ConfirmationTokenHours
+ );
+ return _tokenInfrastructure.GenerateJwt(
+ user.UserAccountId,
+ user.Username,
+ expiresAt,
+ _confirmationTokenSecret
+ );
}
///
@@ -97,7 +119,8 @@ public class TokenService : ITokenService
///
/// Thrown when is not or does not resolve to a known token type.
///
- public string GenerateToken(UserAccount user) where T : struct, Enum
+ public string GenerateToken(UserAccount user)
+ where T : struct, Enum
{
if (typeof(T) != typeof(TokenType))
throw new InvalidOperationException("Invalid token type");
@@ -112,7 +135,7 @@ public class TokenService : ITokenService
TokenType.AccessToken => GenerateAccessToken(user),
TokenType.RefreshToken => GenerateRefreshToken(user),
TokenType.ConfirmationToken => GenerateConfirmationToken(user),
- _ => throw new InvalidOperationException("Invalid token type")
+ _ => throw new InvalidOperationException("Invalid token type"),
};
}
@@ -189,7 +212,11 @@ public class TokenService : ITokenService
/// Thrown when required claims are missing, the user ID claim is not a valid ,
/// or the underlying token validation fails for any other reason (e.g. expired or invalid signature).
///
- private async Task ValidateTokenInternalAsync(string token, string secret, string tokenType)
+ private async Task ValidateTokenInternalAsync(
+ string token,
+ string secret,
+ string tokenType
+ )
{
try
{
@@ -199,7 +226,9 @@ public class TokenService : ITokenService
string? usernameClaim = principal.FindFirst(JwtRegisteredClaimNames.UniqueName)?.Value;
if (string.IsNullOrEmpty(userIdClaim) || string.IsNullOrEmpty(usernameClaim))
- throw new UnauthorizedException($"Invalid {tokenType} token: missing required claims");
+ throw new UnauthorizedException(
+ $"Invalid {tokenType} token: missing required claims"
+ );
if (!Guid.TryParse(userIdClaim, out Guid userId))
throw new UnauthorizedException($"Invalid {tokenType} token: malformed user ID");
@@ -215,4 +244,4 @@ public class TokenService : ITokenService
throw new UnauthorizedException($"Failed to validate {tokenType} token: {e.Message}");
}
}
-}
\ No newline at end of file
+}