.agents/features/ee-authentication.md
The Enterprise Authentication module extends the Community Edition auth layer with SAML 2.0 SSO, Google/GitHub federated OAuth, one-time-password email flows (verification and password reset), fine-grained RBAC enforcement per project, and a managed-auth JWT exchange for embedded SDK use cases. All SSO paths ultimately delegate to authenticationService.federatedAuthn() which creates or links a user and issues a standard AP JWT.
packages/server/api/src/app/ee/authentication/ — backend EE auth module rootpackages/server/api/src/app/ee/authentication/saml-authn/ — SAML SSO service and controllerpackages/server/api/src/app/ee/authentication/federated-authn/ — Google/GitHub OAuth service and controllerpackages/server/api/src/app/ee/authentication/otp/ — OTP entity, service, and controllerpackages/server/api/src/app/ee/authentication/enterprise-local-authn/ — email verify + password resetpackages/server/api/src/app/ee/authentication/project-role/ — RBAC enforcement servicepackages/server/api/src/app/ee/authentication/ee-authorization.ts — Fastify preHandler hooks for plan/ownership checkspackages/server/api/src/app/ee/managed-authn/ — managed auth JWT exchange controller + servicepackages/shared/src/lib/ee/authn/index.ts — enterprise authn shared exportspackages/shared/src/lib/ee/authn/access-control-list.ts — ACL types for RBACpackages/shared/src/lib/ee/authn/enterprise-local-authn/requests.ts — verify email / reset password DTOspackages/shared/src/lib/ee/otp/otp-model.ts — OTP entity Zod schema and state enumpackages/shared/src/lib/ee/otp/otp-type.ts — OtpType enum (EMAIL_VERIFICATION, PASSWORD_RESET)packages/web/src/features/authentication/components/sign-in-form.tsx — sign-in form (includes federated login buttons)packages/web/src/features/authentication/components/third-party-logins.tsx — Google/GitHub login buttonspackages/web/src/features/authentication/components/verify-email.tsx — OTP email verification UIpackages/web/src/features/authentication/components/reset-password-form.tsx — OTP-based password reset formpackages/web/src/features/authentication/hooks/auth-hooks.ts — auth TanStack Query/mutation hookspackages/web/src/features/authentication/api/managed-auth-api.ts — managed auth API clientpackages/web/src/app/routes/platform/security/sso/index.tsx — SSO settings page (SAML + Google config)packages/web/src/app/routes/platform/security/sso/saml-dialog.tsx — SAML configuration dialogpackages/web/src/app/routes/platform/security/sso/oauth2-dialog.tsx — Google/GitHub OAuth app dialogpackages/web/src/app/routes/platform/security/sso/allowed-domain.tsx — allowed email domain dialogpackages/web/src/app/routes/authenticate/index.tsx — SAML ACS callback landing pagessoEnabled plan flag; managed auth gated by embeddingEnabled./v1/authn/saml/acs) that receives and validates the IdP POST after SAML authentication.POST /v1/authn/federated/login initiates, POST /v1/authn/federated/claim completes.assertPrincipalAccessToProject() and per-flow-operation by assertUserHasPermissionToFlow().{ saml: SAMLAuthnProviderConfig, google: GoogleOAuthConfig }.saml-authn/)Flow:
POST /v1/authn/saml/login → generates SAML login request → returns { redirectUrl } to IdPPOST /v1/authn/saml/acsauthnSsoSamlService.acs() parses assertion, extracts email/firstName/lastNameauthenticationService.federatedAuthn() → creates/links user → returns JWTConfig: SAMLAuthnProviderConfig { entityId, ssoUrl, certificate } — stored in platform.federatedAuthProviders.saml
Gating: platform.plan.ssoEnabled
federated-authn/)Flow:
POST /v1/authn/federated/login → returns Google/GitHub OAuth redirect URLPOST /v1/authn/federated/claim → exchanges code for token → validates → creates/links user → returns JWTConfig: platform.federatedAuthProviders.google { clientId, clientSecret } or system-wide defaults
Custom domain support: Redirect URLs use custom domain if configured
otp/)Types: EMAIL_VERIFICATION, PASSWORD_RESET
Flow:
createAndSend() → generates OTP → checks re-send prevention (10-min window) → sends emailconfirm() → validates: pending state + not expired (10 min) + matches value → sets CONFIRMEDEntity: OTP with identityId, type, value, state (PENDING/CONFIRMED), updated timestamp
enterprise-local-authn/)Extends CE auth with:
verifyEmail({ identityId, otp }) → confirms OTP → sets UserIdentity.verified = true → audit logresetPassword({ identityId, otp, newPassword }) → confirms OTP → updates password hash → audit logproject-role/)assertPrincipalAccessToProject({ principal, permission, projectId }):
getPrincipalRoleOrThrow() → grantAccess({ principalRoleId, routePermission }) → throws PERMISSION_DENIED if no accessprincipal.projectId !== projectId → throwsproject.platformId !== principal.platform.id → throwsassertUserHasPermissionToFlow({ principal, operationType, projectId }):
ee-authorization.ts)platformMustHaveFeatureEnabled(handler) — throws FEATURE_DISABLED (402) if handler returns falseprojectMustBeTeamType — enforces project.type === TEAMplatformMustBeOwnedByCurrentUser — enforces user is platform ownerplatformToEditMustBeOwnedByCurrentUser — enforces ownership for edit operationsmanaged-authn/)Endpoint: POST /v1/managed-authn/external-token (public — JWT provides security)
Flow:
kid (signing key ID)JWT v3 payload: { version: 'v3', externalUserId, externalProjectId, firstName, lastName, role, piecesFilterType, piecesTags, tasks, aiCredits, exp }
The SSO settings page (/platform/security/sso) is wrapped in LockedFeatureGuard keyed on platform.plan.ssoEnabled. It surfaces three provider cards (Allowed Domains, Google, SAML 2.0) and an email auth toggle. saml-dialog captures entityId, ssoUrl, and certificate. oauth2-dialog captures clientId and clientSecret for Google. Email verification and password-reset flows in verify-email and reset-password-form POST OTPs to the enterprise local auth endpoints.