Skip to content

@venturekit/auth API

FunctionDescription
createCognitoConfig(security)Create Cognito config from SecurityConfig
buildUserPoolConfig(config)Build User Pool infrastructure config
FunctionSignatureDescription
hasScope(roles: string[], scope: string, config: RolesConfig) => booleanCheck if roles grant a scope
hasAnyScope(roles: string[], scopes: string[], config: RolesConfig) => booleanCheck if roles grant any of the scopes
hasAllScopes(roles: string[], scopes: string[], config: RolesConfig) => booleanCheck if roles grant all scopes
getScopesForRoles(roles: string[], config: RolesConfig) => string[]Get all scopes granted by roles
validateRolesConfig(config: RolesConfig) => ValidationResultValidate a roles configuration

DB-backed role → scopes (vk_role_scopes)

Section titled “DB-backed role → scopes (vk_role_scopes)”

A mutable role → scope matrix backed by the vk_role_scopes table. The table structure ships with this package’s migrations (vk_auth_003_role_scopes.sql); the rows are your app’s authorization policy — seed them in an app data migration and change them at runtime (e.g. from an admin UI) without redeploying. Every helper takes a caller-supplied Querier (same shape as @venturekit/data’s query), so the package keeps no hard data-layer dependency.

Baseline authorization for any app: single-tenant apps resolve a user’s global role through the resolver and grant the result; multi-tenant apps pass resolver.lookup as scopesByRole to @venturekit-pro/tenancy’s createTenantUserScopesMiddleware.

FunctionSignatureDescription
listRoleScopes(q)(Querier) => Promise<Record<string, string[]>>The full mapping, { role: [scope, …] } (scopes sorted).
getRoleScopes(q, role)(Querier, string) => Promise<string[]>Scopes granted by one role ([] when unknown).
setRoleScopes(q, role, scopes)(Querier, string, readonly string[]) => Promise<void>Replace a role’s scope set atomically. An empty array removes the role.
grantScopeToRole(q, role, scope)(Querier, string, string) => Promise<void>Add one grant (idempotent).
revokeScopeFromRole(q, role, scope)(Querier, string, string) => Promise<void>Remove one grant (idempotent).
createRoleScopesResolver(options)(RoleScopesResolverOptions) => RoleScopesResolverCached hot-path resolver (below).

createRoleScopesResolver reads the whole table once and caches it per process with a TTL (ttlMs, default 30_000):

interface RoleScopesResolverOptions { querier: Querier; ttlMs?: number }
interface RoleScopesResolver {
lookup: (role: string) => Promise<readonly string[]> | readonly string[]; // pass as scopesByRole
invalidate(): void; // drop the cache — the next lookup reloads from the DB
}

Consistency: the mutating instance calls invalidate() and sees the change immediately; other warm instances converge within one TTL. Availability: if a reload fails but a previous snapshot exists, the stale snapshot is served; a cold-start failure with no snapshot throws (failing loudly rather than silently granting nothing to everyone).

Federated sign-in (@venturekit/auth/server)

Section titled “Federated sign-in (@venturekit/auth/server)”

These helpers implement the server-side OAuth Authorization Code flow. VentureKit does not enable the Cognito Hosted UI; the SPA runs the redirect dance, the API exchanges the code with the IdP back-channel (so the client_secret never leaves the server).

FunctionSignatureDescription
generateOAuthState()() => stringMint a random URL-safe state token. The /start route returns it AND pins it to the browser via an HttpOnly cookie.
verifyOAuthState(fromQuery, fromCookie)(string?, string?) => booleanConstant-time compare for the /complete CSRF check.
buildAuthorizeUrl(input, env?)(BuildAuthorizeUrlInput) => Promise<string>Build the IdP authorize URL the SPA navigates to. Loads client_id from Secrets Manager.
exchangeAuthorizationCode(input, env?)(ExchangeAuthorizationCodeInput) => Promise<FederatedProfile>Back-channel POST to the IdP token endpoint, then resolve the verified profile (Google id_token claims / Facebook /me Graph).
signInAsFederatedUser(input, config?)(SignInAsFederatedUserInput) => Promise<SignInResult>Idempotently create / refresh the Cognito user for a verified FederatedProfile and mint session tokens via ADMIN_USER_PASSWORD_AUTH.
loadFederatedProviderCredentials(provider, env?)(FederatedProvider) => Promise<FederatedProviderCredentials>Read the OAuth client id/secret from the Secrets Manager placeholder. Cached per Lambda container.

Verification codes (@venturekit/auth/server)

Section titled “Verification codes (@venturekit/auth/server)”

OTP toolkit for the “we’ll text you a 6-digit code” gate. Pluggable storage via VerificationCodeStore; channel is opaque ('email' / 'whatsapp' / anything else the application defines).

FunctionSignatureDescription
generateVerificationCode(length?)(number?) => stringcrypto.randomInt-backed digits. Default 6, range 4–10.
hashVerificationCode(code)(string) => stringSHA-256 hex digest stored on disk.
requestVerificationCode(input)(RequestVerificationCodeInput) => Promise<{ code, expiresAt }>Mint + persist + return plaintext for delivery. Overwrites any previous code for the same (channel, identifier).
verifyVerificationCode(input)(VerifyVerificationCodeInput) => Promise<void>Constant-time check; deletes on success, increments attempts on mismatch, wipes after maxAttempts. Throws verification_failed (HTTP 401).
createPostgresVerificationCodeStore(q?)(Querier?) => VerificationCodeStoreProduction store over vk_verification_codes (table ships with this package’s migrations). Pass a transaction’s query to enlist writes in an open transaction; omit for the global pool. Import from @venturekit/auth/server/store/postgres.
createInMemoryVerificationCodeStore()() => VerificationCodeStoreTests / vk dev only.

The placeholder secret VentureKit provisions for each declared AuthIntent.federated provider is named venturekit/<project>/<stage>/auth/<intent.id>/<provider> and holds {"clientId":"PLACEHOLDER","clientSecret":"PLACEHOLDER"}. Populate it after the first deploy:

Terminal window
aws secretsmanager put-secret-value \
--secret-id <arn-from-cfn-output> \
--secret-string '{"clientId":"…","clientSecret":"…"}'
FunctionSignatureDescription
decodeTokenUnsafe(jwt: string) => Record<string, unknown> | nullDecode JWT without verifying the signature. Use only when the token has already been verified upstream (e.g. API Gateway Cognito Authorizer).
decodeToken(jwt: string) => Record<string, unknown> | nullDeprecated alias of decodeTokenUnsafe. Prefer the explicit name.
verifyAndDecode(jwt: string, opts: VerifyOptions) => Promise<Record<string, unknown> | null>Verify the JWT signature against the Cognito JWKS and decode the claims. Use when the token has not been gated upstream.
extractUserFromToken(jwt: string) => User | nullExtract user from ID token
isTokenExpired(jwt: string) => booleanCheck if token is expired
getTokenExpiry(jwt: string) => Date | nullGet token expiry as Date

Exported from ./types/index.js — includes User, Session, Role, Permission, RolesConfig, and related types.

TypeDescription
UserPoolOutputsCognito User Pool deployment outputs
UserPoolInfraConfigCognito infrastructure configuration

Exported from ./roles/index.js (see the DB-backed role → scopes section above): Querier, RoleScopeRow, RoleScopesLookup, RoleScopesResolver, RoleScopesResolverOptions.

ConstantDescription
DEFAULT_COGNITO_CONFIGDefault Cognito configuration