import { SmrtClassOptions } from '@happyvertical/smrt-core'; import { OidcProfileOwnerAuthorizer, OidcProfileResolver } from '../collections/UserCollection.js'; import { OidcLoginResult, OidcProviderResolutionOptions, OidcTransaction } from '../services/OidcLoginService.js'; import { TerminalAuthError, TerminalAuthRateLimitError, TerminalAuthService, TerminalAuthServiceOptions } from '../services/TerminalAuthService.js'; export { type AppResultContract, type AppResultMetadata, type CommandRequirements, canonicalizeDiscoveryArtifact, createDiscoveryConformanceArtifact, type DeclaredActionField, type DiscoveryArtifactIntegrity, DiscoveryArtifactValidationError, type DiscoveryConformanceArtifact, type DiscoveryPayload, deriveCommandRequirements, type JsonValue, SMRT_APP_RESULT_CONTRACT, SMRT_APP_RESULT_SCHEMA, SMRT_APP_RESULT_VERSION, SMRT_DISCOVERY_CONFORMANCE_ARTIFACT_SCHEMA, SMRT_DISCOVERY_CONFORMANCE_SCHEMA, SMRT_DISCOVERY_CONFORMANCE_VERSION, SMRT_MCP_RESULT_METADATA_KEY, validateDiscoveryConformanceArtifact, } from '../app-contract.js'; export type { NormalizedOidcClaims, OidcProfileOwnerAuthorization, OidcProfileOwnerAuthorizer, OidcProfileOwnerAuthorizerContext, OidcProfileResolver, OidcProfileResolverContext, } from '../collections/UserCollection.js'; export { MobileAuthError, type MobileAuthErrorCode, MobileAuthService, type MobileAuthServiceOptions, type MobileBootstrapContext, type MobileLoginContext, type MobileLogoutResult, type MobileRequestMeta, type MobileResolvedUser, type MobileTenantContext, readMobileBearerToken, validateMobileRedirectUri, } from '../services/MobileAuthService.js'; export { type CreateMobileAuthHandlersOptions, createMobileAuthHandlers, type MobileAuthHandlers, type MobileRequestEvent, type MobileRequestHandler, resolveMobileUploadDedupKey, } from './mobile-handlers.js'; export { type CliResource, type CommandDefinition, type CommandKind, type CommandPolicyContext, type CommandScope, type CreateResourceListHandlerOptions, createResourceListHandler, InvalidBearerError, type ResolvedSession, type ResourceListResponseBody, } from './resource-list-handler.js'; export { defaultSessionLocals, type SessionLocals } from './types.js'; /** * Options for session handler */ export interface SessionHandlerOptions extends SmrtClassOptions { /** Cookie name (default: 'sid') */ cookieName?: string; /** Session TTL in seconds (default: 7 days) */ ttl?: number; /** Paths to skip session loading (e.g., '/api/health') */ skipPaths?: string[]; /** Whether to auto-extend sessions on each request (default: false) */ autoExtend?: boolean; /** * Cookie domain (default: undefined, uses request domain). The session * handler only reads the cookie; this is consumed by `createSessionCookie` * / `destroySessionCookie` when the same options object is shared with them. */ cookieDomain?: string; /** Cookie path (default: '/') */ cookiePath?: string; /** Whether cookies are secure (default: true in production) */ cookieSecure?: boolean; /** SameSite cookie attribute (default: 'lax') */ cookieSameSite?: 'strict' | 'lax' | 'none'; /** Whether to enter smrt-tenancy request context when tenant data exists */ enterTenantContext?: boolean; /** Whether to enforce Postgres RLS via request-scoped transactions */ postgresRls?: boolean; } /** * SvelteKit Handle type (minimal definition to avoid requiring @sveltejs/kit as dependency) */ type HandleInput = { event: { cookies: { get: (name: string) => string | undefined; set: (name: string, value: string, options?: Record) => void; delete: (name: string, options?: Record) => void; }; locals: Record; url: { pathname: string; protocol?: string; }; request: { headers: Headers; }; }; resolve: (event: unknown) => Promise; }; type Handle = (input: HandleInput) => Promise; type SvelteKitRequestEvent = { cookies: HandleInput['event']['cookies']; getClientAddress?: () => string; locals?: Record; params?: Record; request: Request; url: URL; }; type OidcProviderResolver = string | ((event: SvelteKitRequestEvent) => string | undefined); type OidcStringResolver = T | ((result: OidcLoginResult, event: SvelteKitRequestEvent) => T | Promise); export interface OidcSvelteKitOptions extends SmrtClassOptions, OidcProviderResolutionOptions { /** Optional fetch override for tests or custom runtimes. */ fetch?: typeof fetch; /** JWT clock tolerance passed to jose. */ clockTolerance?: number | string; /** Provider name, or a resolver. Defaults to event.params.provider. */ provider?: OidcProviderResolver; /** Callback path used when provider.redirectUri is omitted. */ callbackPath?: string | ((providerName: string) => string); /** Query parameter used to preserve post-login redirects. */ returnToParam?: string; /** Prefix for the temporary OIDC transaction cookie. */ transactionCookiePrefix?: string; /** Temporary transaction cookie TTL in seconds. Default: 10 minutes. */ transactionTtl?: number; /** Cookie path for the temporary OIDC transaction. */ transactionCookiePath?: string; /** Secure flag for the temporary OIDC transaction cookie. */ transactionCookieSecure?: boolean; /** SameSite value for the temporary OIDC transaction cookie. */ transactionCookieSameSite?: 'strict' | 'lax' | 'none'; /** HMAC secret for transaction cookie integrity. Defaults to clientSecret. */ transactionCookieSecret?: string; /** Session cookie name. Defaults to sid. */ sessionCookieName?: string; /** Session cookie path. Defaults to /. */ sessionCookiePath?: string; /** Secure flag for the session cookie. Defaults to true on HTTPS. */ sessionCookieSecure?: boolean; /** SameSite value for the session cookie. */ sessionCookieSameSite?: 'strict' | 'lax' | 'none'; /** Session TTL in seconds. Defaults to the package session default. */ sessionTtl?: number; /** Optional tenant to bind to the session. */ tenantId?: OidcStringResolver; /** * Resolve or reject the canonical Profile after validated claims and before * User/session creation. The resolver runs inside the provisioning * transaction and may be retried after a concurrent unique-key race. */ resolveProfile?: OidcProfileResolver; /** * Explicitly authorize a first issuer/subject binding to a pre-provisioned * canonical Profile and its existing owner. SMRT revalidates both records * in the provisioning transaction before identity or session creation. */ authorizeProfileOwner?: OidcProfileOwnerAuthorizer; /** Redirect target after successful callback. */ successRedirect?: OidcStringResolver; /** Redirect target after failed callback. If omitted, failures return 401. */ failureRedirect?: string | ((error: unknown, event: SvelteKitRequestEvent) => string); } export interface BeginOidcLoginResult { providerName: string; transaction: OidcTransaction; url: URL; } export interface CompleteOidcLoginResult extends OidcLoginResult { providerName: string; returnTo?: string; sessionId: string; } /** * Creates a SvelteKit handle hook for session management. * * This hook: * 1. Reads the session cookie * 2. Loads session context (user + permissions) if valid * 3. Populates event.locals with user, permissions, tenantId, sessionId * 4. Optionally extends session on each request * * @example * ```typescript * // hooks.server.ts * import { createSessionHandler } from '@happyvertical/smrt-users/sveltekit'; * * const sessionHandler = createSessionHandler({ * db: { type: 'sqlite', url: 'app.db' }, * cookieName: 'sid', * ttl: 7 * 24 * 60 * 60, // 7 days * skipPaths: ['/api/health', '/api/public'], * }); * * export const handle = sessionHandler; * // Or with sequence: * // export const handle = sequence(sessionHandler, otherHandler); * ``` */ export declare function createSessionHandler(options: SessionHandlerOptions): Handle; /** * Options for creating a session cookie */ export interface CreateSessionCookieOptions { /** Session TTL in seconds (default: 7 days) */ ttl?: number; /** User agent string */ userAgent?: string; /** Client IP address */ ipAddress?: string; /** Custom session data */ data?: Record; } /** * Helper to create a session and set the cookie after login. * * @example * ```typescript * // +page.server.ts * import { createSessionCookie } from '@happyvertical/smrt-users/sveltekit'; * import { redirect } from '@sveltejs/kit'; * * export const actions = { * login: async (event) => { * // Validate credentials... * const user = await validateLogin(email, password); * * await createSessionCookie(event, user.id, tenantId, { * db: { type: 'sqlite', url: 'app.db' }, * ipAddress: event.getClientAddress(), * userAgent: event.request.headers.get('user-agent') ?? '', * }); * * throw redirect(303, '/dashboard'); * } * }; * ``` */ export declare function createSessionCookie(event: HandleInput['event'], userId: string, tenantId: string | undefined, options: SmrtClassOptions & CreateSessionCookieOptions & { cookieName?: string; cookiePath?: string; cookieDomain?: string; cookieSecure?: boolean; cookieSameSite?: 'strict' | 'lax' | 'none'; }): Promise; /** * Helper to destroy a session and delete the cookie on logout. * * @example * ```typescript * // +page.server.ts * import { destroySessionCookie } from '@happyvertical/smrt-users/sveltekit'; * import { redirect } from '@sveltejs/kit'; * * export const actions = { * logout: async (event) => { * await destroySessionCookie(event, { * db: { type: 'sqlite', url: 'app.db' } * }); * throw redirect(303, '/'); * } * }; * ``` */ export declare function destroySessionCookie(event: HandleInput['event'], options: SmrtClassOptions & { cookieName?: string; cookiePath?: string; cookieDomain?: string; ttl?: number; }): Promise; /** * Helper to switch tenant context for the current session. * * Returns `false` without switching when there is no session, or — fail-closed * (#1400) — when the session's user is not an active member of `tenantId`. The * target tenant id is therefore safe to take straight from untrusted form data, * but callers MUST honour the boolean result rather than assuming success. * * Session-id ROTATION (#1354 follow-up): a successful switch into a non-null * tenant mints a fresh session and revokes the old one. This helper transparently * re-sets the session COOKIE to the new id (same flags), so the old cookie value * stops working and the browser carries the rotated id forward. A `null` clear * leaves the id (and cookie) unchanged. * * @example * ```typescript * // +page.server.ts * import { switchSessionTenant } from '@happyvertical/smrt-users/sveltekit'; * import { fail } from '@sveltejs/kit'; * * export const actions = { * switchTenant: async (event) => { * const data = await event.request.formData(); * const tenantId = data.get('tenantId') as string; * * const switched = await switchSessionTenant(event, tenantId, { * db: { type: 'sqlite', url: 'app.db' } * }); * if (!switched) { * return fail(403, { error: 'Not a member of that tenant.' }); * } * * return { success: true }; * } * }; * ``` */ export declare function switchSessionTenant(event: HandleInput['event'], tenantId: string | null, options: SmrtClassOptions & { cookieName?: string; cookiePath?: string; cookieDomain?: string; cookieSecure?: boolean; cookieSameSite?: 'strict' | 'lax' | 'none'; ttl?: number; }): Promise; /** * Start an OIDC login from a SvelteKit route. * * Sets a short-lived, HTTP-only transaction cookie containing state, nonce, * and PKCE verifier, then returns the provider authorization URL. */ export declare function beginOidcLogin(event: SvelteKitRequestEvent, options: OidcSvelteKitOptions): Promise; /** * Complete an OIDC callback, create or update the SMRT user/profile, and set * the session cookie. */ export declare function completeOidcLogin(event: SvelteKitRequestEvent, options: OidcSvelteKitOptions): Promise; /** * Create a SvelteKit GET handler that redirects to an OIDC provider. * * @example * ```typescript * // src/routes/auth/[provider]/login/+server.ts * import { createOidcLoginHandler } from '@happyvertical/smrt-users/sveltekit'; * * export const GET = createOidcLoginHandler({ * db: { type: 'postgres', url: process.env.DATABASE_URL! }, * }); * ``` */ export declare function createOidcLoginHandler(options: OidcSvelteKitOptions): (event: SvelteKitRequestEvent) => Promise; /** * Create a SvelteKit GET handler for the provider callback. */ export declare function createOidcCallbackHandler(options: OidcSvelteKitOptions): (event: SvelteKitRequestEvent) => Promise; /** * Pull `Bearer ` out of an `Authorization` header. Returns `null` if * the header is missing or malformed. */ export declare function parseBearerToken(authorization: string | null): string | null; /** Options for the terminal-auth start handler. */ export interface CreateTerminalAuthStartHandlerOptions extends TerminalAuthServiceOptions { /** * Override the verification origin returned to the CLI (e.g. when the * public origin differs from the request origin behind a proxy). Defaults * to `event.url.origin`. */ verificationOrigin?: string | ((event: SvelteKitRequestEvent) => string); } /** * Create a SvelteKit POST handler that starts a new terminal-auth request. * Mount under `/api/cli/auth/start/+server.ts`: * * ```ts * export const POST = createTerminalAuthStartHandler({ * db: { type: 'postgres', url: process.env.DATABASE_URL! }, * userCodePrefix: 'WG', * }); * ``` */ export declare function createTerminalAuthStartHandler(options: CreateTerminalAuthStartHandlerOptions): (event: SvelteKitRequestEvent) => Promise; /** * Create a SvelteKit POST handler that exchanges a polling device code for a * bearer token once the request has been approved. Mount under * `/api/cli/auth/token/+server.ts`. */ export declare function createTerminalAuthTokenHandler(options: TerminalAuthServiceOptions): (event: SvelteKitRequestEvent) => Promise; /** * Create a SvelteKit DELETE handler that revokes the bearer token in the * request's `Authorization` header. Always returns `{ authenticated: false }` * — does not leak whether the token was actually live, by design. */ export declare function createBearerSessionDeleteHandler(options: TerminalAuthServiceOptions): (event: SvelteKitRequestEvent) => Promise; /** * Look up the session associated with a bearer token. Use from * `hooks.server.ts` to resolve `Authorization: Bearer ` headers * alongside cookie-based sessions. */ export declare function loadBearerSessionContext(token: string, options: TerminalAuthServiceOptions): Promise; /** Shape passed back to `+page.server.ts` `load`. */ export interface TerminalLoginPageData { userCode: string; requestStatus: string | null; } /** Shape returned by the approve action on success. */ export interface TerminalLoginApproveSuccess { approved: true; requestStatus: string; userCode: string; } /** Shape returned by the approve action on failure (HTTP 4xx). */ export interface TerminalLoginApproveFailure { status: number; error: string; userCode: string; } /** * Page-server helper for the terminal-login approval page. Returns * `{ load, approve }` you can spread into a `+page.server.ts` module. * * `approve` is the action implementation, not a wrapped object — wire it up * as you like, e.g. `export const actions = { approve: handler.approve }`. * * @example * ```ts * // src/routes/terminal-login/+page.server.ts * import { mountTerminalLoginPage } from '@happyvertical/smrt-users/sveltekit'; * * const handlers = mountTerminalLoginPage({ * db: { type: 'postgres', url: process.env.DATABASE_URL! }, * userCodePrefix: 'WG', * requireUser: (event) => Boolean(event.locals.user), * resolveUser: (event) => event.locals.user, * resolveTenantId: (event) => event.locals.tenantId, * }); * * export const load = handlers.load; * export const actions = { approve: handlers.approve }; * ``` */ export interface MountTerminalLoginPageOptions extends TerminalAuthServiceOptions { /** Resolve the authenticated user from `event.locals`. */ resolveUser: (event: SvelteKitRequestEvent) => { id?: string | null; email?: string | null; } | null | undefined; /** Resolve the tenant id from `event.locals`. */ resolveTenantId: (event: SvelteKitRequestEvent) => string | null | undefined; /** Query-string parameter holding the user code on the page URL. */ codeQueryParam?: string; } export interface MountedTerminalLoginPage { load: (event: SvelteKitRequestEvent) => Promise; approve: (event: SvelteKitRequestEvent) => Promise; } export declare function mountTerminalLoginPage(options: MountTerminalLoginPageOptions): MountedTerminalLoginPage; export { TerminalAuthError, TerminalAuthRateLimitError, TerminalAuthService, type TerminalAuthServiceOptions, }; //# sourceMappingURL=index.d.ts.map