Security & Auth
Authentication
PolicyOne uses JWT sessions stored in a __c1_session HTTP-only cookie. Sessions are created after a successful sign-in and verified by the Next.js middleware on every request.
Auth Adapters
The @policyone/auth package provides pluggable adapters. Configure one per tenant in org.auth_provider_configs:
| Adapter | Use case |
|---|---|
LocalAuthAdapter | Username + PBKDF2-hashed password stored in org.users.password_hash |
LdapAuthAdapter | Active Directory / OpenLDAP bind |
SamlAuthAdapter | SAML 2.0 SSO (Okta, Azure AD, etc.) |
OidcAuthAdapter | OpenID Connect (Google Workspace, Auth0, etc.) |
Session Middleware
All routes are protected by apps/policyone/middleware.ts. Public routes (no session required):
/sign-in
/api/auth/
/docs
All other routes redirect to /sign-in?return={original_path} if no valid session cookie is present.
RBAC
Access control uses a standard role-permission model:
org.users
└── security.user_roles (which roles a user holds, scoped to tenant/BU)
└── security.roles
└── security.role_permissions
└── security.permissions (resource + action pairs)
Screen-level access is controlled via security.screen_permissions — each screen key maps to a role with an access_level of can_view or can_edit.
OOTB roles (is_ootb = true) cannot be deleted — they are platform-managed. Carriers can create additional custom roles.
Row-Level Security (RLS)
PostgreSQL RLS is enforced at the database layer. The application sets a app.current_tenant_id session variable before every transaction. RLS policies on all tables reject reads and writes where tenant_id does not match.
This means even a SQL injection vulnerability cannot leak cross-tenant data — the database enforces the boundary independently of the application layer.
PII Handling
| Data | Storage method |
|---|---|
| Passwords | PBKDF2 hash via @policyone/auth |
| SSN / EIN / Driver's License | HMAC-SHA-256 hash (for matching) + masked display string (***-**-1234) |
| Tax ID | tax_id_hash + tax_id_masked on policy.parties |
| TOTP secrets | AES-256-GCM encrypted via packages/ai-core/src/encrypt.ts |
| Session tokens | JWT signed with AUTH_SECRET env var; __c1_session HTTP-only, SameSite=Lax cookie |
Rule: Raw sensitive values (SSN, EIN, license numbers) are never persisted. They exist only in application memory during the request lifecycle, are hashed/encrypted before any write, and are never logged.