Skip to content

feat(auth): add GET /auth/portal-logout for cross-app logout chain#11

Open
awais786 wants to merge 1 commit into
foss-mainfrom
feat/portal-logout-endpoint
Open

feat(auth): add GET /auth/portal-logout for cross-app logout chain#11
awais786 wants to merge 1 commit into
foss-mainfrom
feat/portal-logout-endpoint

Conversation

@awais786
Copy link
Copy Markdown
Collaborator

Summary

Adds `GET /auth/portal-logout?next=` so the foss-server-bundle portal's "Log out of all apps" button can include Twenty in its cross-origin redirect chain.

Same pattern as Plane twentyhq#35 / Outline twentyhq#24 / Penpot twentyhq#24 / SurfSense twentyhq#24.

Behaviour

`GET /auth/portal-logout?next=<absolute_url>`:

  • Clears the `tokenPair` cookie via `clearTokenPairCookie` (the same util used by `JwtAuthGuard` + `MiddlewareService` on mismatch — single source of truth).
  • Validates `?next=` against `PLATFORM_DOMAIN` (scheme http/https + suffix-match with dot boundary).
  • 302s to `?next=` when allowlisted; otherwise 200 with cookie still cleared.

Files

File Purpose
`controllers/portal-logout.controller.ts` New controller
`controllers/portal-logout.controller.spec.ts` 10 unit cases
`auth.module.ts` Register controller
`twenty-config/config-variables.ts` Add `PLATFORM_DOMAIN`

Net: 4 files, +213 lines.

Tests

10 unit cases:

  1. Cookie cleared regardless of `?next=` validity
  2. 302 when host equals `PLATFORM_DOMAIN`
  3. 302 when host is a subdomain
  4. 200 when host unrelated
  5. Dot-boundary enforcement (`foss.arbisoft.com.evil` rejected)
  6. `javascript:` / `data:` schemes rejected
  7. `?next=` omitted → 200
  8. `PLATFORM_DOMAIN` unset → 200 (all rejected)
  9. Malformed `?next=` rejected
  10. Leading dot in `PLATFORM_DOMAIN` normalised

Force-logout note

GET endpoint is reachable via ``. Impact for Twenty: clears the current browser's `tokenPair` cookie. The SPA's next request bootstraps via `/auth/sso/proxy-login` and gets a fresh tokenPair — so the user is auto-relogged in unless oauth2-proxy was also cleared.

This is lighter than SurfSense's portal-logout (which revokes refresh tokens) — appropriate for Twenty's JWT model where `tokenPair` isn't a long-lived refresh token.

Out of scope

  • Portal-side button wiring — separate PR
  • Devstack env passthrough (`PLATFORM_DOMAIN` for Twenty's compose env) — separate small PR

@awais786 awais786 force-pushed the feat/portal-logout-endpoint branch from 9e928f2 to 9f85990 Compare May 18, 2026 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant