accounts
Version:
Tempo Accounts SDK
221 lines • 10.8 kB
TypeScript
import { Hex } from 'ox';
import type { Address, Client, Transport } from 'viem';
import * as z from 'zod/mini';
import { type Handler, from } from '../../Handler.js';
import * as Kv from '../../Kv.js';
import * as Session from './session.js';
/**
* Session payload persisted in the session store and surfaced via
* `getSession`. `address` is the account address that signed the
* authentication challenge; `chainId` is the chain echoed in the message.
*/
export type SessionPayload = {
/** Address of the account. */
address: Address;
/** Chain ID echoed into the challenge message. */
chainId: number;
/**
* Verified email, set when the verify request included a valid `idToken`
* (see `auth({ identity })`). Omitted when no identity token was supplied.
*/
email?: string | undefined;
/** Unix timestamp (seconds) when the session was issued. */
issuedAt: number;
/** Unix timestamp (seconds) when the session expires. */
expiresAt: number;
};
/** Zod schemas for the auth handler's request and response payloads. */
export declare namespace schema {
/** Schemas for `POST {path}/challenge`. */
namespace challenge {
/** Request body schema. */
const parameters: z.ZodMiniObject<{
chainId: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
resources: z.ZodMiniOptional<z.ZodMiniReadonly<z.ZodMiniArray<z.ZodMiniString<string>>>>;
}, z.core.$strip>;
/** Response body schema. */
const returns: z.ZodMiniObject<{
message: z.ZodMiniString<string>;
}, z.core.$strip>;
}
/** Schemas for `POST {path}` (verify). */
namespace verify {
/** Request body schema. */
const parameters: z.ZodMiniObject<{
address: z.ZodMiniTemplateLiteral<`0x${string}`>;
message: z.ZodMiniString<string>;
signature: z.ZodMiniTemplateLiteral<`0x${string}`>;
keyAuthorization: z.ZodMiniOptional<z.ZodMiniTemplateLiteral<`0x${string}`>>;
idToken: z.ZodMiniOptional<z.ZodMiniString<string>>;
returnToken: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
}, z.core.$strip>;
/** Response body schema. */
const returns: z.ZodMiniObject<{
token: z.ZodMiniOptional<z.ZodMiniString<string>>;
}, z.core.$loose>;
}
}
/**
* Server Authentication request handler. Mounts three routes under `path`:
*
* - `POST {path}/challenge` → `{ message }`
* - `POST {path}` → verify and issue a session (cookie via `Set-Cookie`)
* - `POST {path}/logout` → clear the session cookie
*
* The returned handler also exposes `getSession(req)` for resolving the
* current session from a follow-up request's cookie.
*
* The challenge message is wire-formatted as EIP-4361 (SIWE) for ecosystem
* compatibility, but address binding is deferred: the SDK can fold the
* challenge digest into the connect ceremony before the account knows
* which address it will sign with. The wallet supplies the real address at
* verify time and the server uses it as the session subject.
*/
export declare function auth(options?: auth.Options): auth.ReturnType;
export declare namespace auth {
/** Return type of `auth()` — a `Handler` extended with `getSession`. */
type ReturnType = Handler & {
getSession: getSession;
};
/** Resolves the current session from a request's cookie or bearer token. */
type getSession = (req: Session.SessionRequest) => Promise<SessionPayload | undefined>;
/**
* Hook invoked after a SIWE signature is verified but before the
* session token is issued. Returning a `Response` merges its JSON
* body and status onto the verify response. Throwing rejects with
* `401` — the thrown error's `message` is surfaced as the response
* `error` field — and no session is issued.
*/
type onAuthenticate = (params: {
/** Verified address that signed the SIWE message. */
address: Address;
/** Chain ID parsed from the SIWE message. */
chainId: number;
/** Verbatim SIWE message that was signed. */
message: string;
/** Underlying request — useful for headers, IP, etc. */
request: Request;
/** Signature provided by the wallet. */
signature: Hex.Hex;
}) => Response | Promise<Response> | void | Promise<void>;
type Options = from.Options & {
/**
* Whether to issue a session cookie on successful verify. When
* `false`, the verify response always contains `{ token }` in the
* body (the per-request `returnToken` flag is ignored), no
* `Set-Cookie` header is sent, logout does not clear a cookie, and
* `getSession` ignores any incoming cookie — only
* `Authorization: Bearer <token>` is honored. Use this when the SDK
* lives in a non-browser context (CLI, server-to-server) or when
* the host app already manages auth cookies.
* @default true
*/
cookie?: boolean | undefined;
/** Cookie name for the session token. @default "accounts_auth" */
cookieName?: string | undefined;
/** Domain echoed into challenge messages. @default request `Host` header */
domain?: string | undefined;
/** Resolves the Viem client used to verify a SIWE signature for its chain. */
getClient?: ((chainId: number) => Client) | undefined;
/**
* OIDC identity verification (verified email), enabled by default. A verify
* request carrying an `idToken` is checked against the issuer's JWKS, with
* `aud` pinned to this request's resolved origin, `sub` cross-checked
* against the SIWE-verified address, and `nonce` matched to the SIWE nonce.
* On success the verified email is folded onto the session
* (`SessionPayload.email`). The wallet only mints id tokens when the app
* requests identity via `wallet_connect`, so requests without one are
* unaffected. Override `issuer` for self-hosted wallets, or set `required`
* to reject verifies that omit a valid token.
*/
identity?: {
/**
* Issuer (IdP) URL whose JWKS signs the id token (the wallet's OIDC
* mount). Defaults to the Tempo wallet's production OIDC issuer;
* override for self-hosted or non-production wallets.
* @default "https://wallet.tempo.xyz/api/oidc"
*/
issuer?: string | undefined;
/** Reject the verify when no (or an invalid) `idToken` is supplied. @default false */
required?: boolean | undefined;
} | undefined;
/**
* Hook invoked after the SIWE signature is verified but before the
* session token is issued. Use to provision a user record, emit
* analytics, or apply application-level allow/deny rules. Throwing
* from this hook rejects the request with `401` and the thrown
* error's `message` is surfaced as the response `error` field.
*/
onAuthenticate?: onAuthenticate | undefined;
/** Path prefix for the auth endpoints. @default "/" */
path?: string | undefined;
/**
* Pinned canonical public origin (e.g. `'https://app.example.com'`).
* When set, the SIWE `domain` and `uri`, and the cookie `Secure` flag,
* are derived from this URL — request `Host`, request URL, and
* `x-forwarded-*` headers are ignored. This is the recommended setting
* for production deployments behind a CDN or reverse proxy: it
* prevents a spoofed `x-forwarded-host` from shifting the SIWE domain
* and a spoofed `x-forwarded-proto: http` from disabling `Secure`.
*/
origin?: string | undefined;
/**
* Whether to issue a session on successful verify. When `false`,
* verify acts as a stateless signature check — no token is generated,
* no entry is written to the session store, and no cookie is sent.
* The verify response is `{}`. `getSession` always returns
* `undefined` and `/logout` is a no-op (still returns `204`). Use
* this when the host application mints its own session token (e.g.
* a JWT inside `onAuthenticate`).
* @default true
*/
session?: boolean | undefined;
/**
* Human-readable SIWE statement to bind into issued challenges. Static
* strings are used verbatim; callbacks can derive copy from the accepted
* `resources` while keeping the user-facing text under server control.
*/
statement?: string | ((params: {
/** Chain ID requested for the SIWE challenge. */
chainId: number;
/** SIWE resources requested by the caller. */
resources?: readonly string[] | undefined;
/** Underlying request — useful for headers, IP, etc. */
request: Request;
}) => string | Promise<string | undefined> | undefined) | undefined;
/**
* Backing store for both single-use challenges (nonces) and issued
* sessions. Keys are namespaced internally (`challenge:…`, `session:…`).
* @default `Kv.memory()`
*/
store?: Kv.Kv | undefined;
/**
* Viem transport for the default Tempo verification client, built with Tempo signature
* handling. Ignored when `getClient` is provided.
* @default `http()`
*/
transport?: Transport | undefined;
/**
* Honor `x-forwarded-proto` / `x-forwarded-host` to derive the public
* origin. Required when running behind a trusted reverse proxy that
* terminates TLS (OrbStack on `*.tempo.local`, a CDN, etc.). When
* `false`, forwarded headers are ignored to prevent spoofing on
* deployments that expose the origin server directly. Ignored when
* `origin` is set.
*
* Explicitly passing `true` also satisfies the `origin`/`domain`
* requirement. Only do so behind a proxy that overwrites (not appends)
* `x-forwarded-host` and is the only way to reach the server.
* @default `true` on Cloudflare Workers (always edge-fronted), `false` elsewhere.
*/
trustProxy?: boolean | undefined;
/** TTLs in seconds. */
ttl?: {
/** Challenge (nonce) TTL. @default 600 (10m) */
challenge?: number | undefined;
/** Session TTL. @default 86400 (24h) */
session?: number | undefined;
} | undefined;
};
}
//# sourceMappingURL=auth.d.ts.map