auth/api_token_queries.ts

API token query functions for token CRUD and validation.

view source

Declarations
#

8 declarations

API_TOKEN_COLUMNS
#

auth/api_token_queries.ts view source

readonly ["id", "account_id", "name", "token_hash", "expires_at", "last_used_at", "last_used_ip", "created_at", "scope"] import {API_TOKEN_COLUMNS} from '@fuzdev/fuz_app/auth/api_token_queries.js';

The full api_token column set, named explicitly so a row read fails loud on schema drift (see ACCOUNT_COLUMNS in auth/account_queries.ts for the outage class; the Rust twin names columns at every token site). Keep in sync with ApiToken and the migration chain's end state.

ApiTokenQueryDeps
#

query_api_token_enforce_limit
#

auth/api_token_queries.ts view source

(deps: QueryDeps, account_id: string, max_tokens: number): Promise<number> import {query_api_token_enforce_limit} from '@fuzdev/fuz_app/auth/api_token_queries.js';

Enforce a per-account token limit by evicting the oldest tokens.

Race safety: this function must run inside a transaction alongside the INSERT that created the new token. The caller (the account_token_create RPC handler) runs under the dispatcher's transaction path because the spec declares side_effects: true, making one creator's INSERT + enforce_limit pair atomic. That does not serialize concurrent creators: under Read Committed, two transactions can't see each other's uncommitted token row, so each evicts against a stale count and both can commit above max_tokens. Closing this needs one serialization point per (account_id, credential_kind) before count/evict/insert — see the matching note on query_session_enforce_limit.

Ordering carries the same id DESC stability tie-breaker, with the same caveat, as query_session_enforce_limit — deterministic survivors under a created_at tie, not a guarantee that the row just inserted survives.

deps

query dependencies (must be transaction-scoped)

account_id

the account to enforce the limit for

type string

max_tokens

maximum number of tokens to keep

type number

returns

Promise<number>

the number of tokens evicted

query_api_token_list_for_account
#

auth/api_token_queries.ts view source

(deps: QueryDeps, account_id: string): Promise<Omit<ApiToken, "token_hash">[]> import {query_api_token_list_for_account} from '@fuzdev/fuz_app/auth/api_token_queries.js';

List all tokens for an account, newest first — API_TOKEN_CLIENT_COLUMNS, so token_hash never rides the listing.

deps

account_id

type string

returns

Promise<Omit<ApiToken, "token_hash">[]>

query_create_api_token
#

auth/api_token_queries.ts view source

(deps: QueryDeps, id: string, account_id: string, name: string, token_hash: string, scope: { version: 1; kind: "full"; grandfathered?: boolean | undefined; } | { version: 1; kind: "methods"; methods: string[]; grandfathered?: boolean | undefined; }, expires_at?: Date | ... 1 more ... | undefined): Promise<...> import {query_create_api_token} from '@fuzdev/fuz_app/auth/api_token_queries.js';

Store a new API token (the hash, not the raw token).

deps

query dependencies

id

the public token id (e.g. tok_abc123)

type string

account_id

the owning account

type string

name

human-readable name

type string

token_hash

blake3 hash of the raw token

type string

scope

type { version: 1; kind: "full"; grandfathered?: boolean | undefined; } | { version: 1; kind: "methods"; methods: string[]; grandfathered?: boolean | undefined; }

expires_at?

optional expiration

type Date | null | undefined
optional

returns

Promise<ApiToken>

the stored token record

query_revoke_all_api_tokens_for_account
#

auth/api_token_queries.ts view source

(deps: QueryDeps, account_id: string): Promise<number> import {query_revoke_all_api_tokens_for_account} from '@fuzdev/fuz_app/auth/api_token_queries.js';

Revoke all tokens for an account.

deps

query dependencies

account_id

the account whose tokens to revoke

type string

returns

Promise<number>

the number of tokens revoked

query_revoke_api_token_for_account
#

auth/api_token_queries.ts view source

(deps: QueryDeps, id: string, account_id: string): Promise<boolean> import {query_revoke_api_token_for_account} from '@fuzdev/fuz_app/auth/api_token_queries.js';

Revoke a token only if it belongs to the specified account.

Prevents cross-account token revocation.

deps

query dependencies

id

the public token id

type string

account_id

the account that must own the token

type string

returns

Promise<boolean>

true if a token was revoked, false if not found or wrong account

query_validate_api_token
#

auth/api_token_queries.ts view source

(deps: ApiTokenQueryDeps, raw_token: string, ip: string | undefined, pending_effects: Promise<void>[] | undefined): Promise<ApiToken | undefined> import {query_validate_api_token} from '@fuzdev/fuz_app/auth/api_token_queries.js';

Validate a raw API token and return the token record.

Hashes the token with blake3, looks up the hash, and checks expiration. Updates last_used_at and last_used_ip on success (fire-and-forget — errors logged, never thrown).

deps

query dependencies with logger

raw_token

the raw API token from the Authorization header

type string

ip

the client IP address (for audit)

type string | undefined

pending_effects

optional array to register the usage-tracking effect for later awaiting

type Promise<void>[] | undefined

returns

Promise<ApiToken | undefined>

the token record if valid, or undefined

Depends on
#

Imported by
#