import type { AccessToken, CreateClientOptions, CreateClientResult, DatabaseDriver, OAuthClient, PersonalAccessTokenResult, RefreshTokenResult, TokenScopes } from '@stacksjs/types'; /** * Read `users.password_changed_at` for a user. * * Binds a token's validity to the account's credential state: a token * issued before the user last changed their password is no longer * trusted, regardless of its own `revoked`/`expires_at` flags. This is * the durable, use-time backstop behind the post-reset revocation sweep * (#1947) — even a freshly minted pair that the sweep never saw is * rejected on first use. * * Returns `null` on ANY error (missing column / missing table) so a * not-yet-migrated database degrades to legacy-allow rather than locking * everyone out. Accepts an optional query runner so the refresh exchange * can read the stamp inside its own transaction. */ export declare function getPasswordChangedAt(userId: unknown, q?: { unsafe: (sql: string, params?: any[]) => any }): Promise; /** * True when a credential issued at `createdAt` predates the user's last * password change (`changedAt`) and must therefore be rejected. * * Legacy-allow semantics: * - `changedAt` null (no stamp / un-migrated) => never reject. * - `createdAt` missing/unparseable => never reject. * * Strict `<` so a token minted in the SAME second as (or after) the * reset — e.g. the victim's immediate post-reset login — is NOT bricked * by CURRENT_TIMESTAMP's one-second granularity. */ export declare function isIssuedBeforePasswordChange(createdAt: unknown, changedAt: Date | null): boolean; /** * Get all access tokens for a user * * @example * import { tokens } from '@stacksjs/auth' * const userTokens = await tokens(user.id) */ export declare function tokens(userId: number): Promise; /** * Get a specific token by its plain text value * Uses hash comparison for security * * @example * import { findToken } from '@stacksjs/auth' * const token = await findToken('abc123...') */ export declare function findToken(plainTextToken: string): Promise; /** * Get the current access token from the request context * * @example * import { currentAccessToken } from '@stacksjs/auth' * const token = await currentAccessToken() */ export declare function currentAccessToken(): Promise; /** * Check if the current token has a given scope/ability * * @example * import { tokenCan } from '@stacksjs/auth' * if (await tokenCan('posts:create')) { * // user can create posts * } */ export declare function tokenCan(scope: string): Promise; /** * Check if the current token does NOT have a given scope/ability * * @example * import { tokenCant } from '@stacksjs/auth' * if (await tokenCant('admin')) { * throw new Error('Admin access required') * } */ export declare function tokenCant(scope: string): Promise; /** * Check if token has ALL of the given scopes * * @example * import { tokenCanAll } from '@stacksjs/auth' * if (await tokenCanAll(['posts:read', 'posts:write'])) { * // user has both scopes * } */ export declare function tokenCanAll(scopes: string[]): Promise; /** * Check if token has ANY of the given scopes * * @example * import { tokenCanAny } from '@stacksjs/auth' * if (await tokenCanAny(['admin', 'moderator'])) { * // user has at least one of the scopes * } */ export declare function tokenCanAny(scopes: string[]): Promise; /** * Get all scopes/abilities for the current token * * @example * import { tokenAbilities } from '@stacksjs/auth' * const abilities = await tokenAbilities() * // ['read', 'write', 'posts:create'] */ export declare function tokenAbilities(): Promise; /** * Create a new personal access token for a user * Tokens are hashed before storage for security * * @param userId - The user ID to create the token for * @param name - A name/description for the token * @param scopes - Array of scopes/abilities for the token * @param options - Additional options * @param options.expiresInMinutes - Token expiry in minutes (default: 60) * @param options.withRefreshToken - Whether to create a refresh token (default: true) * @param options.refreshExpiresInDays - Refresh token expiry in days (default: 30) * * @example * import { createToken } from '@stacksjs/auth' * const result = await createToken(user.id, 'My API Token', ['read', 'write']) * console.log(result.plainTextToken) // Save this - it won't be shown again! * console.log(result.refreshToken) // Use this to get new access tokens */ export declare function createToken(userId: number, name?: string, scopes?: string[], options?: { expiresInMinutes?: number withRefreshToken?: boolean refreshExpiresInDays?: number /** * What the browser called itself, when there is one. * * Stored so a person can recognise their own sessions on a list well enough * to revoke one. Untrusted - it is a string a client chose - which is why * it sits beside the address rather than instead of it, and why nothing * authorises on it. */ userAgent?: string | null /** Where the request came from, for the same list. */ ipAddress?: string | null }): Promise; /** * Exchange a refresh token for a new access token * * @param refreshTokenPlain - The plain text refresh token * @param options - Additional options * @param options.expiresInMinutes - New access token expiry in minutes (default: 60) * @param options.refreshExpiresInDays - New refresh token expiry in days (default: 30) * * @example * import { refreshToken } from '@stacksjs/auth' * const result = await refreshToken('your-refresh-token') * // Use result.plainTextToken as new access token * // Use result.refreshToken as new refresh token (old one is revoked) */ export declare function refreshToken(refreshTokenPlain: string, options?: { expiresInMinutes?: number refreshExpiresInDays?: number }): Promise; /** * Validate a refresh token without exchanging it * * @example * import { validateRefreshToken } from '@stacksjs/auth' * const isValid = await validateRefreshToken('your-refresh-token') */ export declare function validateRefreshToken(refreshTokenPlain: string): Promise; /** * Revoke a specific refresh token * * @example * import { revokeRefreshToken } from '@stacksjs/auth' * await revokeRefreshToken('your-refresh-token') */ export declare function revokeRefreshToken(refreshTokenPlain: string): Promise; /** * Revoke all refresh tokens for a user * * @example * import { revokeAllRefreshTokens } from '@stacksjs/auth' * await revokeAllRefreshTokens(user.id) */ export declare function revokeAllRefreshTokens(userId: number): Promise; /** * Delete expired refresh tokens (cleanup) * * @example * import { deleteExpiredRefreshTokens } from '@stacksjs/auth' * const count = await deleteExpiredRefreshTokens() */ export declare function deleteExpiredRefreshTokens(): Promise; /** * Delete revoked refresh tokens older than specified days * * @example * import { deleteRevokedRefreshTokens } from '@stacksjs/auth' * const count = await deleteRevokedRefreshTokens(7) */ export declare function deleteRevokedRefreshTokens(daysOld?: number): Promise; /** * Revoke a specific access token * * @example * import { revokeToken } from '@stacksjs/auth' * await revokeToken('abc123...') */ export declare function revokeToken(plainTextToken: string): Promise; /** * Revoke a token by its ID * * @example * import { revokeTokenById } from '@stacksjs/auth' * await revokeTokenById(123) */ export declare function revokeTokenById(tokenId: number): Promise; /** * Revoke all tokens for a user * * @example * import { revokeAllTokens } from '@stacksjs/auth' * await revokeAllTokens(user.id) */ export declare function revokeAllTokens(userId: number): Promise; /** * Revoke all tokens except the current one * * @example * import { revokeOtherTokens } from '@stacksjs/auth' * await revokeOtherTokens(user.id) */ export declare function revokeOtherTokens(userId: number): Promise; /** * Delete expired tokens (cleanup) * * @example * import { deleteExpiredTokens } from '@stacksjs/auth' * const count = await deleteExpiredTokens() */ export declare function deleteExpiredTokens(): Promise; /** * Delete revoked tokens older than specified days * * @example * import { deleteRevokedTokens } from '@stacksjs/auth' * const count = await deleteRevokedTokens(30) // older than 30 days */ export declare function deleteRevokedTokens(daysOld?: number): Promise; /** * Get all OAuth clients for a user * * @example * import { clients } from '@stacksjs/auth' * const userClients = await clients(user.id) */ export declare function clients(userId: number): Promise; /** * Get a specific OAuth client by ID * * @example * import { findClient } from '@stacksjs/auth' * const client = await findClient(1) */ export declare function findClient(clientId: number): Promise; /** * Create a new OAuth client * * @example * import { createClient } from '@stacksjs/auth' * const client = await createClient({ * name: 'My App', * redirect: 'https://myapp.com/callback' * }) */ export declare function createClient(options: CreateClientOptions): Promise; /** * Revoke an OAuth client * * @example * import { revokeClient } from '@stacksjs/auth' * await revokeClient(1) */ export declare function revokeClient(clientId: number): Promise; // ============================================================================ // HELPER FUNCTIONS // ============================================================================ export declare function parseScopes(scopes: string | string[] | null | undefined): TokenScopes; /** Current database driver */ declare const dbDriver: DatabaseDriver; /** Cross-database SQL helpers */ declare const sql: ReturnType; /** * Alias for currentAccessToken * * @example * import { token } from '@stacksjs/auth' * const t = await token() */ export declare const token: unknown;