Skip to main content

Authentication

The Backoffice uses Google OAuth 2.0 exclusively — there are no passwords. Authentication is handled by NextAuth.js 5 on the frontend, which exchanges Google tokens with the Identity Service to create a session JWT.

Sign-in flow

  1. User navigates to the Backoffice and clicks Sign in with Google.
  2. Browser redirects to Google's OAuth consent screen.
  3. User grants permission. Google redirects back to /api/auth/callback/google.
  4. NextAuth exchanges the authorization code for a Google token.
  5. The frontend calls POST /v1/auth/signin on the Identity Service with { email, displayName, provider: "google", providerId }.
  6. The Identity Service creates or updates the user record and returns a signed JWT.
  7. NextAuth stores the JWT in an encrypted session cookie.

JWT claims

Every API call to the Campaign Service includes the JWT and a set of forwarded headers derived from the session:

HeaderContent
Authorization: Bearer {token}Signed JWT
X-Percus-Forwarded-User-IdUser GUID
X-Percus-Forwarded-User-NameDisplay name
X-Percus-Forwarded-Active-OrgActive organization GUID
X-Percus-Forwarded-Org-RoleOrganization role (e.g. OrganizationAdmin)
X-Percus-Forwarded-System-RoleSystem role (Owner), if assigned

The JWT itself carries these claims:

{
"sub": "user-uuid",
"active_org": "org-uuid",
"org_role": "OrganizationAdmin",
"system_role": "Owner",
"exp": 1234567890
}

system_role is only present for platform-level Owner users.

Switching organizations

A user can belong to multiple organizations. Switching is done via:

POST /v1/auth/switch-org
{ "organizationId": "uuid" }

The Identity Service returns a new JWT with the updated active_org and org_role claims. The frontend re-establishes the session with the new token.

User states

StateMeaning
PendingInvited but has not yet accepted
ActiveCan sign in and use the platform
DeactivatedAccess revoked by an admin; cannot sign in
SuspendedSuspended by the platform; cannot sign in

A Pending user becomes Active when they accept their invitation via POST /v1/users/{userId}/accept-invitation.

Invitation flow

  1. An OrganizationAdmin calls POST /v1/users/invite with the invitee's email and desired role.
  2. The system creates a Pending user and (when email is configured) sends an invitation email.
  3. The invitee accepts via the invitation link → user becomes Active.
  4. Admins can resend invitations with POST /v1/users/{userId}/resend-invitation.