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
accountNamestringThe account name.
cancellationTokenCancellationTokenToken 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
linkCodestringThe Discord link code to search for.
cancellationTokenCancellationTokenToken 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
usernamestringThe account name or email to query, depending on
email.emailboolWhen false,
usernameis treated as the account name (3-32 chars). When true, it is treated as an email (max 320 chars).cancellationTokenCancellationTokenToken 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
usernamestringThe account name or email to query, depending on
email.emailboolWhen false,
usernameis treated as the account name (3-32 chars). When true, it is treated as an email (max 320 chars).cancellationTokenCancellationTokenToken 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
accountNamestringThe account name.
ageintThe age value.
cancellationTokenCancellationTokenToken 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
accountNamestringThe account name. Must be 3-32 characters.
saltstringThe salt for SRP password hashing. Must not be null or whitespace.
verifierstringThe verifier for SRP password hashing. Must not be null or whitespace.
emailstringThe account email. Must not be empty and must not exceed 320 characters.
ageintThe account holder age. Must be between 0 and 200.
cancellationTokenCancellationTokenToken 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
accountNamestringThe account name.
cancellationTokenCancellationTokenToken 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
accountNamestringThe account name.
linkCodestringThe link code, or null to clear after verification.
cancellationTokenCancellationTokenToken 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
accountNamestringThe account name.
emailstringThe new email address, or null to clear.
cancellationTokenCancellationTokenToken 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
accountNamestringThe account name. Must be 3-32 characters.
cancellationTokenCancellationTokenToken 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
accountNamestringThe account name.
totpWindowlongThe time-step window of the verified code.
cancellationTokenCancellationTokenToken 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
accountNamestringThe account name.
enabledboolWhether TOTP should be enabled.
cancellationTokenCancellationTokenToken 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
accountNamestringThe account name.
encryptedTotpSecretstringThe Base32-encoded TOTP secret, encrypted at rest by the server.
cancellationTokenCancellationTokenToken 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
accountNamestringThe account name.
totpWindowlongThe time-step window of the verified code.
cancellationTokenCancellationTokenToken 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
accountNamestringThe account name.
cancellationTokenCancellationTokenToken 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
accountNamestringThe account name.
verifyCodeintThe verification code the user provided. Must match the stored verify code.
cancellationTokenCancellationTokenToken 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
accountNamestringThe account name.
verifyCodeintThe randomly generated verification code.
expiresUtcDateTimeUTC instant after which the code is invalid.
cancellationTokenCancellationTokenToken to cancel the operation.
Returns
- Task<DatabaseResult>
DatabaseResult indicating success or failure.