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:

AdapterUse case
LocalAuthAdapterUsername + PBKDF2-hashed password stored in org.users.password_hash
LdapAuthAdapterActive Directory / OpenLDAP bind
SamlAuthAdapterSAML 2.0 SSO (Okta, Azure AD, etc.)
OidcAuthAdapterOpenID 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

DataStorage method
PasswordsPBKDF2 hash via @policyone/auth
SSN / EIN / Driver's LicenseHMAC-SHA-256 hash (for matching) + masked display string (***-**-1234)
Tax IDtax_id_hash + tax_id_masked on policy.parties
TOTP secretsAES-256-GCM encrypted via packages/ai-core/src/encrypt.ts
Session tokensJWT 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.