Skip to content

Feature/self hosted cloud backend#112

Merged
krzychdre merged 10 commits into
mainfrom
feature/self-hosted-cloud-backend
Jun 21, 2026
Merged

Feature/self hosted cloud backend#112
krzychdre merged 10 commits into
mainfrom
feature/self-hosted-cloud-backend

Conversation

@krzychdre

Copy link
Copy Markdown
Owner

No description provided.

krzychdre added 10 commits May 6, 2026 10:44
- Auto-detect Clerk base URL from cloudApiUrl when not explicitly  set, fixing HTTP 400 errors in self-hosted deployments
- Replace HTTP 307 redirect with HTML response for auth callback  to avoid browser blocking of custom-protocol URIs
- Add alembic migrations for timezone-aware DateTime columns
- Update all SQLAlchemy models to use DateTime(timezone=True)
- Update README with auth flow docs and troubleshooting
POST /v1/client/sign_ins returned the ticket's session id in the body
but issued an Authorization token bound to a brand-new, unrelated
session (create_session_and_token created session_B while the body
reported session_A). The extension then called
POST /v1/client/sessions/{session_A}/tokens with token_B; the server
resolved token_B to session_B, failed the session_B != session_A guard,
and returned 404 — breaking the entire sign-in flow after Authentik
login. Recorded in spotted-errors/self-hosted-cloud.md.

Split session creation from token issuance in auth_service:
- create_session(user_id) -> Session
- create_client_token(session_id) -> (ClientToken, raw_token)
- create_session_and_token kept as a composition of the two

sign_ins now mints the client token against the ticket's existing
session, so the returned session id and the token's session match.
The OAuth callback (browser.py) now only creates the session — the
client token it used to create was hashed into the DB and never
returned to anyone, so it was unrecoverable dead weight.

Also:
- database.py: only pass pool_size/max_overflow for postgresql URLs;
  SQLite's StaticPool rejects them, which blocked test/dev setups.
- validate_ticket: coerce naive datetimes to UTC before comparing, so
  the expiry check works on SQLite (naive) as well as Postgres (aware).

Test infrastructure:
- conftest.py: set required env vars before src imports and override
  get_db with a per-test in-memory SQLite engine so TestClient and
  seeding share state.
- test_sign_in_flow.py: new end-to-end regression covering
  ticket -> sign_ins -> sessions/{id}/tokens (the 404 case), /v1/me
  after sign-in, the cross-session token guard, and ticket single-use.
- test_auth.py / test_browser_auth.py: use the shared client fixture
  and the renamed create_session.
@krzychdre krzychdre merged commit 2912420 into main Jun 21, 2026
4 of 7 checks passed
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