Authentication & Authorization¶
The platform uses Auth0 for identity, JWT access/refresh tokens for session continuity, and a two-layer authorization model: CASL gates the UI on the client, while Cerbos is the authoritative policy engine on the server. This page covers all four.
Authentication (Auth0)¶
Authentication is handled through Auth0 using the Universal Login page. Auth0 owns credentials and identity; the platform stores only minimal profile data.
flowchart TD
User -->|Login Request| Platform
Platform -->|Redirect| Auth0[Auth0 Universal Login]
Auth0 -->|Update Metadata| AuthAPI[auth.grepsr.com]
Auth0 -->|Access + Refresh Token| Platform
Platform -->|Set cookies, provide access| User
- Universal Login. Users are redirected to Auth0's hosted login, which performs authentication and authorization and returns an access token on success.
- Token validation. The Next.js app validates the token before any request reaches the backend gRPC microservices.
- Token lifetime. Access tokens are valid for 30 minutes; the platform silently uses the refresh token to mint new ones so the user stays signed in.
- Hard logout. A user left signed in without activity is hard-logged-out after 3 days.
- Account lockout. After 5 invalid password attempts, Auth0 blocks the account; an admin must unblock it via the Auth0 management portal.
- Social login. Grepsr (internal) users may sign in only via Gmail social login; external users use standard credentials.
Where identity data lives¶
- Auth0 stores credentials (email, password) — the platform never stores passwords.
- The platform's own database stores only essential profile fields (first name, last name, industry) for personalization.
- On each login, Auth0 updates platform-specific metadata via the
auth.grepsr.comAPI.
Session & Middleware¶
Routing-level session enforcement lives in middleware.ts, which runs at the edge on every
navigation (it explicitly skips /api, /sso, and Next.js static assets).
flowchart TD
Req[Incoming navigation] --> Skip{"/api or static?"}
Skip -->|yes| Next[Allow]
Skip -->|no| Cred{Has credentials cookie?}
Cred -->|no| Invite{Invite / reset token in URL?}
Invite -->|valid| Next
Invite -->|invalid| Login["Redirect → /account/login"]
Invite -->|no token| Acct{On /account/*?}
Acct -->|yes| Next
Acct -->|no| Login
Cred -->|yes| Public{On a public route?}
Public -->|yes| Projects["Redirect → /projects"]
Public -->|no| Next
Behaviour summary:
- No credentials, protected route → redirect to
/account/loginwith areturnpath so the user lands back where they started after signing in. - No credentials, invite/reset link → the token is validated server-side
(
getUserDataFromInviteToken/getPasswordResetDetails); an invalid token redirects to login with an error. - Authenticated, on a public route (e.g.
/account/login) → redirect to/projects. - A
returnpath pointing at/downloads/{ident}is honoured directly after authentication.
Token Refresh¶
Token lifecycle is managed by the Axios interceptors in apis/index.ts. Tokens live in cookies and
are attached to every outgoing request.
sequenceDiagram
participant App
participant AX as Axios interceptor
participant API as Backend
App->>AX: request (JWT attached)
AX->>API: send
API-->>AX: 401 Unauthorized
AX->>AX: queue subsequent requests
AX->>API: POST /auth/refresh (refresh token)
alt refresh succeeds
API-->>AX: new access token
AX->>API: retry queued requests
AX-->>App: responses
else refresh fails
AX->>App: clear cookies → redirect /account/login
end
- On a
401, the interceptor queues in-flight requests, refreshes the access token once, then replays the queue — avoiding a refresh stampede. - A failed refresh clears cookies and redirects to login.
- A
loginTypecookie distinguishes a normal session from admin impersonation; an expired impersonation session redirects withimpersonateSessionExpired=true.
Authorization — Two Layers¶
Authorization is enforced twice, by design:
flowchart LR
subgraph Client["Client (UI gating)"]
Casl["CASL Ability
lib/ability.ts"]
Can["<Can> component"]
Casl --> Can
end
subgraph Server["Server (authoritative)"]
Route[app/api/* route]
Cerbos["Cerbos policy engine
utils/cerbos.ts"]
Route --> Cerbos
end
User[(userData)] --> Casl
User --> Route
Cerbos -->|allow/deny| Route
CASL — client-side UI gating¶
- Ability rules are defined in
lib/ability.tsas a role → action → subject matrix. TheCaslProvidertransforms the logged-inuserDatainto a CASLAbility, consumed via theuseCaslhook and the<Can>component (components/atoms/Can). - Roles include
ADMIN,ADMIN:ENVOY,ADMIN:BUILDER,MANAGER,TRUSTED, andVIEWER. - CASL exists for responsiveness — it hides or disables UI the user cannot use. It is not a security boundary; a determined client can bypass it.
// Conditional rendering with CASL
<Can I="create" a="Project">
<CreateProjectButton />
</Can>
Cerbos — server-side policy enforcement¶
- The authoritative check runs in the API route handlers using the Cerbos client
(
utils/cerbos.ts). Cerbos evaluates a principal (the user's role + attributes) against a resource (kind, id, attributes such asorg_id) for a requested action. - The client is environment-aware: it uses the HTTP Cerbos client for staging and the gRPC
client for production, addressed via
CERBOS_HOST/CERBOS_PORT(andCERBOS_URLfor HTTP). - Payloads are assembled by
helpers/cerbosHelper.ts.
Security boundary
Never rely on CASL alone for access control. CASL improves UX; Cerbos (server-side) is the
enforcement point. Any new privileged operation must add a Cerbos check in its API route, not
just a <Can> guard in the UI.
Admin route guard (HOC)¶
Admin-only pages are wrapped with hoc/withAdminAuthentication.tsx, which checks the user's role
and redirects non-admins to /projects. This complements — but does not replace — the Cerbos check
on the underlying API routes.
Key Points¶
- Identity: Auth0 Universal Login; platform stores minimal profile data only.
- Access token lifetime: 30 minutes, refreshed silently via refresh token.
- Hard logout: after 3 days of inactivity.
- Lockout: 5 failed attempts → Auth0 blocks the account (admin must unblock).
- Session gating:
middleware.tsredirects unauthenticated users and bounces authenticated users off public routes. - Authorization: CASL (client UI) + Cerbos (server, authoritative). Always enforce on the server.