Skip to main content

Overview

The AuthService handles user authentication, registration, profile management, and password operations. It uses PBKDF2 with SHA256 for secure password hashing and integrates with JWT token generation.

Interface

public interface IAuthService
Location: ~/workspace/source/AndanDo/Services/Auth/AuthService.cs

Authentication

SignInAsync

Authenticates a user with email and password.
Task<AuthResult> SignInAsync(
    LoginRequest request,
    CancellationToken cancellationToken = default)
request
LoginRequest
required
Login credentials
cancellationToken
CancellationToken
default:"default"
Optional cancellation token
authResult
AuthResult
Authentication result containing user data and JWT token
var loginRequest = new LoginRequest(
    Email: "[email protected]",
    Password: "SecurePassword123!"
);

try
{
    var result = await authService.SignInAsync(loginRequest);
    Console.WriteLine($"Welcome, {result.Nombre}!");
    Console.WriteLine($"Token: {result.Token}");
}
catch (UnauthorizedAccessException ex)
{
    Console.WriteLine($"Login failed: {ex.Message}");
}
Security Checks: This method validates:
  • Email exists in the database
  • Password matches the stored hash
  • User account is active (EstaActivo = true)
  • User account is not blocked (EstaBloqueado = false)
Throws UnauthorizedAccessException if any check fails.
The method automatically updates the user’s UltimoLogin field to the current UTC time upon successful authentication.

Registration

RegisterAsync

Registers a new user account.
Task<RegisterResult> RegisterAsync(
    RegisterRequest request,
    CancellationToken cancellationToken = default)
request
RegisterRequest
required
Registration data
registerResult
RegisterResult
Registration result
var registerRequest = new RegisterRequest(
    Email: "[email protected]",
    Password: "SecurePassword123!",
    Nombre: "John",
    Apellido: "Doe",
    Telefono: "+1-555-0123",
    Pais: "Dominican Republic",
    Ciudad: "Santo Domingo",
    FotoPerfilUrl: null
);

try
{
    var result = await authService.RegisterAsync(registerRequest);
    Console.WriteLine($"User registered with ID: {result.UserId}");
    Console.WriteLine($"Message: {result.Message}");
}
catch (InvalidOperationException ex)
{
    Console.WriteLine($"Registration failed: {ex.Message}");
}
The user is automatically assigned to role ID 1 (default user role) after successful registration.
Validation: The method will throw InvalidOperationException if:
  • Email is already registered (result code 1)
  • Specified role doesn’t exist or is inactive (result code 2)
  • Any other database constraint is violated

Password Management

HashPassword

Hashes a password using PBKDF2 with SHA256.
string HashPassword(string password)
password
string
required
Plain text password to hash
hash
string
Base64-encoded hash in format: {salt}:{hash}
Security Parameters:
  • Algorithm: PBKDF2 with SHA256
  • Iterations: 100,000
  • Salt size: 16 bytes
  • Key size: 32 bytes

VerifyPassword

Verifies a password against a stored hash.
bool VerifyPassword(string password, string passwordHash)
password
string
required
Plain text password to verify
passwordHash
string
required
Stored password hash
isValid
bool
Returns true if password matches, false otherwise
Uses constant-time comparison to prevent timing attacks.

ChangePasswordAsync

Changes a user’s password after verifying the current password.
Task ChangePasswordAsync(
    ChangePasswordRequest request,
    CancellationToken cancellationToken = default)
request
ChangePasswordRequest
required
Password change data
var changeRequest = new ChangePasswordRequest(
    UsuarioId: 123,
    PasswordActual: "OldPassword123",
    PasswordNueva: "NewSecurePassword456!"
);

try
{
    await authService.ChangePasswordAsync(changeRequest);
    Console.WriteLine("Password changed successfully");
}
catch (InvalidOperationException ex)
{
    Console.WriteLine($"Failed: {ex.Message}");
}
Validation: The method validates:
  • User exists
  • User account is active
  • User account is not blocked
  • Current password is correct
Throws InvalidOperationException if any check fails.

Profile Management

GetUserProfileAsync

Retrieves complete user profile information.
Task<UserProfileDto?> GetUserProfileAsync(
    int userId,
    CancellationToken cancellationToken = default)
userId
int
required
User ID to retrieve
profile
UserProfileDto?
User profile data or null if not found
var profile = await authService.GetUserProfileAsync(userId: 123);

if (profile != null)
{
    Console.WriteLine($"Name: {profile.Nombre} {profile.Apellido}");
    Console.WriteLine($"Email: {profile.Email}");
    Console.WriteLine($"Country: {profile.Pais}");
    Console.WriteLine($"Last login: {profile.UltimoLogin}");
}

UpdateUserProfileAsync

Updates user profile information.
Task UpdateUserProfileAsync(
    UpdateUserProfileRequest request,
    CancellationToken cancellationToken = default)
request
UpdateUserProfileRequest
required
Profile update data
var updateRequest = new UpdateUserProfileRequest(
    UsuarioId: 123,
    Nombre: "John",
    Apellido: "Smith",
    Telefono: "+1-555-9999",
    Pais: "United States",
    Ciudad: "New York",
    FotoPerfilUrl: "https://example.com/photos/new-profile.jpg"
);

await authService.UpdateUserProfileAsync(updateRequest);
The FechaActualizacion field is automatically set to the current UTC time.
Throws InvalidOperationException if no rows are affected (user not found or database error).

Achievements

GetHostAchievementsAsync

Retrieves host achievements for a user.
Task<IReadOnlyList<HostAchievementDto>> GetHostAchievementsAsync(
    int userId,
    int take = 3,
    CancellationToken cancellationToken = default)
userId
int
required
User ID
take
int
default:"3"
Maximum number of achievements to return
achievements
IReadOnlyList<HostAchievementDto>
List of achievements, ordered by unlock date (most recent first)
var achievements = await authService.GetHostAchievementsAsync(
    userId: 123,
    take: 5
);

foreach (var achievement in achievements)
{
    Console.WriteLine($"{achievement.Titulo} - {achievement.Descripcion}");
    if (achievement.DesbloqueadoEn.HasValue)
    {
        Console.WriteLine($"Unlocked: {achievement.DesbloqueadoEn.Value:g}");
    }
}

Role Management

IsUserInRoleAsync

Checks if a user has a specific role.
Task<bool> IsUserInRoleAsync(
    int userId,
    int roleId,
    CancellationToken cancellationToken = default)
userId
int
required
User ID to check
roleId
int
required
Role ID to check
hasRole
bool
Returns true if user has the role, false otherwise
// Check if user is an admin (role ID 2)
bool isAdmin = await authService.IsUserInRoleAsync(
    userId: 123,
    roleId: 2
);

if (isAdmin)
{
    Console.WriteLine("User has admin privileges");
}
Returns false if userId is less than or equal to 0 (invalid user ID).

Security Best Practices

Password Requirements: While the service doesn’t enforce password complexity, it’s recommended to validate passwords before calling RegisterAsync or ChangePasswordAsync:
  • Minimum 8 characters
  • Mix of uppercase and lowercase
  • Include numbers and special characters
Token Handling: The JWT token returned by SignInAsync should be:
  • Stored securely on the client side
  • Sent in the Authorization header for subsequent requests
  • Never logged or exposed in URLs
Account Status: Always check EstaActivo and EstaBloqueado fields before allowing operations. Inactive or blocked users should not be able to perform actions even if they have a valid token.

Common Patterns

User Registration Flow

// 1. Register user
var registerRequest = new RegisterRequest(...);
var result = await authService.RegisterAsync(registerRequest);

// 2. Automatically log them in
var loginRequest = new LoginRequest(
    Email: registerRequest.Email,
    Password: registerRequest.Password
);
var authResult = await authService.SignInAsync(loginRequest);

// 3. User is now authenticated with a token
return authResult.Token;

Profile Update Flow

// 1. Get current profile
var profile = await authService.GetUserProfileAsync(userId);

// 2. Modify fields
var updateRequest = new UpdateUserProfileRequest(
    UsuarioId: userId,
    Nombre: profile.Nombre,
    Apellido: profile.Apellido,
    Telefono: newPhoneNumber, // Updated field
    Pais: profile.Pais,
    Ciudad: profile.Ciudad,
    FotoPerfilUrl: profile.FotoPerfilUrl
);

// 3. Save changes
await authService.UpdateUserProfileAsync(updateRequest);

Build docs developers (and LLMs) love