Skip to content

Authentication

VentureKit provides authentication through Amazon Cognito with role-based access control (RBAC) and OAuth scope enforcement.

Authentication in VentureKit works at three levels:

  1. Security config — define scopes and app clients
  2. Infrastructure intents — provision a Cognito User Pool
  3. Handler scopes — enforce authentication per endpoint

Scopes are defined once in config/security.ts and shared across all environments:

import type { SecurityConfig } from '@venturekit/core';
export const security: SecurityConfig = {
scopes: [
{ name: 'users.read', description: 'Read user data' },
{ name: 'users.write', description: 'Create and update users' },
{ name: 'admin.users', description: 'Admin user management' },
],
appClients: [
{
name: 'web-app',
allowedScopes: ['users.read', 'users.write'],
supportsRefreshTokens: true,
},
{
name: 'admin-dashboard',
allowedScopes: ['users.read', 'users.write', 'admin.users'],
supportsRefreshTokens: true,
generateSecret: true,
},
],
mfa: 'optional',
};

Use an auth intent to create a Cognito User Pool:

export default defineVenture({
base, security,
envs: { dev, prod },
routesDir: 'src/routes',
infrastructure: {
auth: [{
id: 'main',
signInWith: ['email'],
allowSignUp: true,
mfa: 'optional',
passwordStrength: 'strong',
}],
},
});

Add scopes to your handler to require authentication:

import { handler } from '@venturekit/runtime';
// Public — no auth required
export const main = handler(async (_body, ctx, logger) => {
return { status: 'ok' };
});
// Authenticated — requires users.read scope
export const main = handler(async (_body, ctx, logger) => {
return { userId: ctx.user?.id, email: ctx.user?.email };
}, { scopes: ['users.read'] });
// Admin only — requires admin.users scope
export const main = handler(async (_body, ctx, logger) => {
return { users: [] };
}, { scopes: ['admin.users'] });

Define roles that map to sets of scopes:

import type { RolesConfig } from '@venturekit/auth';
const rolesConfig: RolesConfig = {
roles: [
{ name: 'viewer', description: 'Read only', scopes: ['users.read'] },
{ name: 'member', description: 'Standard', scopes: ['users.read', 'users.write'] },
{ name: 'admin', description: 'Full access', scopes: ['users.read', 'users.write', 'admin.users'], isSystem: true },
],
defaultRole: 'viewer',
superAdminRole: 'admin',
};
import { hasScope, getScopesForRoles, hasAllScopes } from '@venturekit/auth';
getScopesForRoles(['member'], rolesConfig);
// → ['users.read', 'users.write']
hasScope(['member'], 'admin.users', rolesConfig);
// → false
hasAllScopes(['admin'], ['users.read', 'admin.users'], rolesConfig);
// → true
import { decodeToken, extractUserFromToken, isTokenExpired, getTokenExpiry } from '@venturekit/auth';
const claims = decodeToken(jwt); // Decode WITHOUT verification
const user = extractUserFromToken(jwt); // Extract user from ID token
const expired = isTokenExpired(jwt); // Check exp claim
const expiry = getTokenExpiry(jwt); // Get expiry as Date

In authenticated handlers, ctx.user is populated automatically:

export const main = handler(async (_body, ctx, logger) => {
const user = ctx.user!;
logger.info('Request from user', {
userId: user.id,
email: user.email,
scopes: user.scopes,
});
return { profile: { id: user.id, email: user.email } };
}, { scopes: ['users.read'] });