Skip to main content

API Authentication

PensionPortal.ai uses session-based authentication powered by Auth.js v5 (NextAuth). There are no standalone API keys for external integrations in the current release — all API access requires a valid authenticated session.
A bearer-token API key system is on the roadmap for Phase 2. For now, all programmatic access must go through the session cookie flow described below.

Session Flow

1. Sign In

POST /api/auth/signin
Content-Type: application/x-www-form-urlencoded

csrfToken=<token>&email=user@example.com&password=...&callbackUrl=/dashboard
On success, Auth.js sets a __Secure-next-auth.session-token cookie (or next-auth.session-token in development). Include this cookie in all subsequent requests.

2. Make Authenticated Requests

Pass the session cookie automatically (browser) or explicitly (server-to-server):
curl -X GET https://your-deployment.vercel.app/api/schemes \
  -H "Cookie: next-auth.session-token=<token>"

3. Sign Out

POST /api/auth/signout

Role-Based Access Control (RBAC)

Every user is assigned exactly one role. Roles determine which API endpoints are accessible and which data is visible.
RoleDescriptionKey Permissions
SuperAdminPlatform administratorFull access across all tenants
BrokerAdminBroker firm administratorFull access within their tenant; can approve schemes
BrokerUserBroker team memberRead/write within tenant; cannot approve
EmployerUserEmployer representativeAccess to their own employer’s data only
MemberScheme memberRead-only access to own member record
Role enforcement happens in API route handlers via requireRole() from src/lib/actor-context.ts. Requests with insufficient role return 403 Forbidden.

Tenant Isolation

Users are scoped to a single tenant (broker firm). The tenant context is derived from the session — you cannot query another tenant’s data by passing a different brokerId in the request body. Server-side enforcement is absolute.
// 403 response when accessing another tenant's resource:
{
  "error": "Forbidden — resource does not belong to your organisation"
}

Unauthenticated Requests

Requests to protected endpoints without a valid session receive:
// 401 Unauthorized
{
  "error": "Unauthenticated"
}

Password Reset

POST /api/auth/forgot-password
Content-Type: application/json

{ "email": "user@example.com" }
If the email exists, a reset link is sent via Resend. The link calls:
POST /api/auth/reset-password
Content-Type: application/json

{ "token": "<reset-token>", "password": "<new-password>" }

Security Notes

  • HTTPS only in production — session cookies are Secure and HttpOnly
  • CSRF protection — all mutating requests require a valid CSRF token (handled automatically by Auth.js)
  • Session expiry — sessions expire after 30 days of inactivity
  • No credential logging — authentication errors return generic messages; detailed errors are logged server-side only

Development Credentials

For local development and demo environments only:
EmailPasswordRole
admin@iorp-ii-app.ieadmin123SuperAdmin
broker@iorp-ii-app.iebroker123BrokerAdmin
employer@iorp-ii-app.ieemployer123EmployerUser
Never use development credentials in production. Rotate AUTH_SECRET before deploying to Vercel.