Skip to content

✨ Add GET /api/auth/portal-logout for cross-app logout chain#24

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

✨ Add GET /api/auth/portal-logout for cross-app logout chain#24
awais786 wants to merge 1 commit into
foss-mainfrom
feat/portal-logout-endpoint

Conversation

@awais786
Copy link
Copy Markdown

Summary

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

Same pattern as Pressingly/plane#35 and Pressingly/outline#24. Each app in the chain clears its own native session while the browser is on that app's origin (cross-origin Set-Cookie is impossible from the portal).

What it does

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

  • Calls `session/delete-fn` — the same primitive the existing `auth/logout` RPC uses → invalidates the server-side session row + clears `auth-token` cookie.
  • Validates `?next=` against `PLATFORM_DOMAIN` (suffix-match with dot boundary; `http`/`https` scheme only).
  • 302s to `?next=` when allowlisted; otherwise 200 with cookies still cleared.

Files

File Purpose
`backend/src/app/http/portal_logout.clj` New: handler + `allowed-next?` predicate + integrant key
`backend/src/app/main.clj` Wire the new integrant key
`backend/src/app/http.clj` Include `::portal-logout/routes` in the main router
`backend/src/app/config.clj` Add `:platform-domain` to config schema
`backend/test/backend_tests/http_portal_logout_test.clj` New: 8 cases on the `allowed-next?` predicate

Net: 5 files, +176 / -11.

Tests

8 cases covering the pure-function `allowed-next?` predicate:

  1. Host equals `PLATFORM_DOMAIN`
  2. Host is a subdomain
  3. Rejects unrelated host
  4. Dot-boundary enforcement (`foss.arbisoft.com.evil` rejected)
  5. Rejects `javascript:` / `data:` schemes
  6. Rejects everything when `PLATFORM_DOMAIN` unset
  7. Rejects malformed URLs
  8. Normalises leading dot in `PLATFORM_DOMAIN`

The handler itself is exercised via integration in the FOSS bundle (with a real session it 302s + clears the cookie; with rejected `?next=` it 200s + clears the cookie). Test docstring notes this.

Config

Setting Source Purpose
`PLATFORM_DOMAIN` env, set by `foss-server-bundle/platform.sh` `?next=` allowlist (suffix-match + dot boundary)

Out of scope

  • Portal-side button wiring — separate PR in `foss-server-bundle-devstack/landing/index.html`.
  • Devstack env passthrough (`PLATFORM_DOMAIN: ${PLATFORM_DOMAIN}` for Penpot's container in compose) — separate small PR.
  • Analogous endpoints on Twenty / SurfSense — separate PRs.

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