Table of Contents

Interface IAccountService

Namespace
FishMMO.Database.Npgsql.Services.Interfaces
Assembly
FishMMO-DB.dll

Service interface for account operations following ISP principle. All operations are async and return DatabaseResult for consistent error handling. Implements execution strategies for automatic retry on transient database failures.

public interface IAccountService : IExistsByKeyAction<string>
Inherited Members

Remarks

This service provides account management operations including:

  • Account creation with atomic UPSERT to prevent race conditions
  • Login authentication with SRP (Secure Remote Password) protocol support
  • Last login timestamp tracking
  • Account existence checks

Methods that perform database write operations use execution strategies to automatically retry on transient failures (up to 3 attempts by default). This includes connection timeouts, deadlocks, and network interruptions.

All methods follow consistent validation and error handling patterns:

  • Username validation (3-32 characters, non-empty)
  • Null safety with nullable reference types
  • DatabaseResult pattern for safe, typed error handling
  • Custom exceptions with sanitized messages for security

Methods

ClearTotpAsync(string, CancellationToken)

Clears all TOTP fields when a user disables 2FA. Atomically resets totp_secret, totp_enabled, totp_verified_at, and last_totp_window. Recovery codes should be invalidated separately via ITwoFactorRecoveryCodeService.

Task<DatabaseResult> ClearTotpAsync(string accountName, CancellationToken cancellationToken = default)

Parameters

accountName string

The account name.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

DatabaseResult indicating success or failure.

FetchByDiscordLinkCodeAsync(string, CancellationToken)

Fetches an account by its Discord link code for verification. Used by the Discord bot to confirm in-game verification.

Task<DatabaseResult<AccountData?>> FetchByDiscordLinkCodeAsync(string linkCode, CancellationToken cancellationToken = default)

Parameters

linkCode string

The Discord link code to search for.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult<AccountData?>>

DatabaseResult containing the AccountData if found, or null.

FetchForLoginAsync(string, bool, CancellationToken)

Retrieves account authentication data for login.

Task<DatabaseResult<AccountData>> FetchForLoginAsync(string username, bool email = false, CancellationToken cancellationToken = default)

Parameters

username string

The account name or email to query, depending on email.

email bool

When false, username is treated as the account name (3-32 chars). When true, it is treated as an email (max 320 chars).

cancellationToken CancellationToken

Token to cancel the asynchronous operation.

Returns

Task<DatabaseResult<AccountData>>

DatabaseResult containing AccountData with authentication credentials on success, or error information on failure.

Remarks

This method uses LINQ query which automatically benefits from EF Core's configured retry policy for transient failures.

Success: Returns AccountData with salt, verifier, access level, and timestamps.

  • (Banned, null): Account exists but is banned Success: Returns AccountData with salt, verifier, access level, and timestamps. Failure cases:
  • VALIDATION_ERROR: Username or email validation failed
  • DB_NOT_FOUND: Account does not exist
  • ACCOUNT_BANNED: Account exists but is banned
  • DB_CONNECTION_FAILED: Database connection error (transient)
  • DB_TIMEOUT: Query timeout (transient)

Security Note: Does not distinguish between non-existent accounts and banned accounts in error messages to prevent username enumeration attacks.

The returned AccountData DTO is a defensive copy and can be safely used after the database context is disposed.

FetchLastLoginAsync(string, bool, CancellationToken)

Gets the last login time for an account.

Task<DatabaseResult<DateTime>> FetchLastLoginAsync(string username, bool email = false, CancellationToken cancellationToken = default)

Parameters

username string

The account name or email to query, depending on email.

email bool

When false, username is treated as the account name (3-32 chars). When true, it is treated as an email (max 320 chars).

cancellationToken CancellationToken

Token to cancel the asynchronous operation.

Returns

Task<DatabaseResult<DateTime>>

DatabaseResult containing the last login timestamp on success, or error information on failure.

Remarks

This method uses LINQ query which automatically benefits from EF Core's configured retry policy for transient failures.

Success: Returns the last login timestamp. Failure cases:

  • VALIDATION_ERROR: Username or email validation failed
  • DB_NOT_FOUND: Account does not exist
  • DB_CONNECTION_FAILED: Database connection error (transient)
  • DB_TIMEOUT: Query timeout (transient)

PersistAgeAsync(string, int, CancellationToken)

Updates the age for an account.

Task<DatabaseResult> PersistAgeAsync(string accountName, int age, CancellationToken cancellationToken = default)

Parameters

accountName string

The account name.

age int

The age value.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

DatabaseResult indicating success or failure.

PersistAsync(string, string, string, string, int, CancellationToken)

Creates a new account with the specified credentials.

Task<DatabaseResult> PersistAsync(string accountName, string salt, string verifier, string email, int age, CancellationToken cancellationToken = default)

Parameters

accountName string

The account name. Must be 3-32 characters.

salt string

The salt for SRP password hashing. Must not be null or whitespace.

verifier string

The verifier for SRP password hashing. Must not be null or whitespace.

email string

The account email. Must not be empty and must not exceed 320 characters.

age int

The account holder age. Must be between 0 and 200.

cancellationToken CancellationToken

Token to cancel the asynchronous operation.

Returns

Task<DatabaseResult>

DatabaseResult indicating success or failure with error details.

Remarks

Success: Account created with Player access level and current timestamp. Failure cases:

  • VALIDATION_ERROR: Invalid username, salt, verifier, email, or age
  • UNIQUE_VIOLATION: Account name already exists (non-transient)
  • DATABASE_ERROR: Unexpected database error

PersistAutoVerifiedAsync(string, CancellationToken)

Marks an account verified without requiring a verification code, clearing any pending code in the process.

This is the server-initiated counterpart to PersistVerifiedAsync(string, int, CancellationToken) and exists for the development-only AutoVerifyAccounts path, where no code is ever generated or emailed. It performs no code check, so it must never be reachable from a client-supplied value — client-driven verification goes through PersistVerifiedAsync(string, int, CancellationToken), which validates the code atomically.

Task<DatabaseResult> PersistAutoVerifiedAsync(string accountName, CancellationToken cancellationToken = default)

Parameters

accountName string

The account name.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

DatabaseResult indicating success or failure.

PersistDiscordLinkCodeAsync(string, string?, CancellationToken)

Sets the temporary Discord link verification code for an account. The Discord bot generates this code; the user verifies in-game with /verify.

Task<DatabaseResult> PersistDiscordLinkCodeAsync(string accountName, string? linkCode, CancellationToken cancellationToken = default)

Parameters

accountName string

The account name.

linkCode string

The link code, or null to clear after verification.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

DatabaseResult indicating success or failure.

PersistEmailAsync(string, string?, CancellationToken)

Updates the email address for an account.

Task<DatabaseResult> PersistEmailAsync(string accountName, string? email, CancellationToken cancellationToken = default)

Parameters

accountName string

The account name.

email string

The new email address, or null to clear.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

DatabaseResult indicating success or failure.

PersistLastLoginAsync(string, CancellationToken)

Updates the last login timestamp for an account atomically. Uses execution strategy for automatic retry on transient failures.

Task<DatabaseResult> PersistLastLoginAsync(string accountName, CancellationToken cancellationToken = default)

Parameters

accountName string

The account name. Must be 3-32 characters.

cancellationToken CancellationToken

Token to cancel the asynchronous operation.

Returns

Task<DatabaseResult>

DatabaseResult indicating success or failure with error details.

Remarks

Uses atomic UPDATE without loading entity to prevent race conditions. Wrapped in execution strategy for automatic retry on transient failures.

Success: Last login timestamp updated to current database server time. Failure cases:

  • VALIDATION_ERROR: Invalid username (length or format)
  • DB_NOT_FOUND: Account does not exist
  • DB_CONNECTION_FAILED: Database connection error (transient)
  • DB_TIMEOUT: Operation timeout (transient)
  • DB_QUERY_FAILED: Unexpected database error

PersistLastTotpWindowAsync(string, long, CancellationToken)

Updates the last TOTP time-step window used for an account, preventing replay attacks. Only updates if the new window is greater than the stored value.

Task<DatabaseResult> PersistLastTotpWindowAsync(string accountName, long totpWindow, CancellationToken cancellationToken = default)

Parameters

accountName string

The account name.

totpWindow long

The time-step window of the verified code.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

DatabaseResult indicating success or failure.

PersistTotpEnabledAsync(string, bool, CancellationToken)

Enables or disables TOTP two-factor authentication for an account.

Task<DatabaseResult> PersistTotpEnabledAsync(string accountName, bool enabled, CancellationToken cancellationToken = default)

Parameters

accountName string

The account name.

enabled bool

Whether TOTP should be enabled.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

DatabaseResult indicating success or failure.

PersistTotpSecretAsync(string, string, CancellationToken)

Stores the encrypted TOTP secret and enables TOTP for an account. Called during 2FA enrollment after the server generates the secret.

Task<DatabaseResult> PersistTotpSecretAsync(string accountName, string encryptedTotpSecret, CancellationToken cancellationToken = default)

Parameters

accountName string

The account name.

encryptedTotpSecret string

The Base32-encoded TOTP secret, encrypted at rest by the server.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

DatabaseResult indicating success or failure.

PersistTotpVerifiedAtAsync(string, long, CancellationToken)

Records the first successful TOTP verification timestamp, confirming 2FA setup. Atomically sets totp_verified_at and updates last_totp_window.

Task<DatabaseResult> PersistTotpVerifiedAtAsync(string accountName, long totpWindow, CancellationToken cancellationToken = default)

Parameters

accountName string

The account name.

totpWindow long

The time-step window of the verified code.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

DatabaseResult indicating success or failure.

PersistVerificationEmailSentAsync(string, CancellationToken)

Records that the verification email has been successfully sent via SMTP. Once set, login is blocked for unverified accounts until the user provides the correct verify code.

Task<DatabaseResult> PersistVerificationEmailSentAsync(string accountName, CancellationToken cancellationToken = default)

Parameters

accountName string

The account name.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

DatabaseResult indicating success or failure.

PersistVerifiedAsync(string, int, CancellationToken)

Sets the verified status for an account. Called when the user provides the correct verify code from the email verification link.

Task<DatabaseResult> PersistVerifiedAsync(string accountName, int verifyCode, CancellationToken cancellationToken = default)

Parameters

accountName string

The account name.

verifyCode int

The verification code the user provided. Must match the stored verify code.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

DatabaseResult indicating success or failure.

PersistVerifyCodeAsync(string, int, DateTime, CancellationToken)

Sets the verification code for an account, along with the UTC expiry timestamp after which the code is no longer redeemable.

Task<DatabaseResult> PersistVerifyCodeAsync(string accountName, int verifyCode, DateTime expiresUtc, CancellationToken cancellationToken = default)

Parameters

accountName string

The account name.

verifyCode int

The randomly generated verification code.

expiresUtc DateTime

UTC instant after which the code is invalid.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

DatabaseResult indicating success or failure.