Files
afilmory/be/apps/oauth-gateway/README.md
Innei 019ee50121 feat: implement OAuth state management for multi-tenancy support
- Introduced encoding and decoding of OAuth state to include tenant metadata, allowing the gateway to route callbacks without hard-coded tenant slugs.
- Updated the AuthController to handle social account linking and sign-in with compatibility for legacy paths.
- Refactored redirect URI construction to simplify tenant slug handling.
- Enhanced documentation to reflect changes in the OAuth flow and state management.

Signed-off-by: Innei <tukon479@gmail.com>
2025-11-30 14:03:05 +08:00

3.2 KiB

OAuth Gateway

Multi-tenant OAuth callback router that lets every identity provider point to a single domain (auth.afilmory.art, for example) while keeping the actual Better Auth handlers inside each tenant subdomain.

How It Works

  1. Better Auth (running inside be/apps/core) wraps the OAuth state with the tenant slug.
  2. Providers always redirect to the fixed URL https://auth.afilmory.art/api/auth/callback/{provider}.
  3. The gateway unwraps state, restores the inner Better Auth state, and issues a 302 redirect to https://<slug>.afilmory.art/api/auth/callback/{provider} (preserving code, state, etc.).

Because the gateway only rewrites the callback target, it does not interact with provider APIs or tokens. This keeps configuration simple (single callback URL in GitHub/Google) while ensuring tenant sessions are still created on the correct host.

Development

pnpm --filter @afilmory/oauth-gateway dev

The service starts on http://0.0.0.0:8790 by default.

Environment Variables

Variable Default Description
AUTH_GATEWAY_HOST 0.0.0.0 Interface to bind.
AUTH_GATEWAY_PORT 8790 Port to listen on.
AUTH_GATEWAY_BASE_DOMAIN afilmory.art Root domain used when constructing tenant hosts.
AUTH_GATEWAY_CALLBACK_BASE_PATH /api/auth/callback Base path that the providers call.
AUTH_GATEWAY_FORCE_HTTPS true Forces redirects to https unless the host looks like localhost.
AUTH_GATEWAY_ALLOW_CUSTOM_HOST false Allow requests to pass an explicit targetHost query parameter.
AUTH_GATEWAY_ROOT_SLUG root Slug treated as the apex (no subdomain).
AUTH_GATEWAY_STATE_SECRET none (required) HMAC secret for decoding wrapped state values (fallbacks to CONFIG_ENCRYPTION_KEY).

Callback Contract

GET /api/auth/callback/:provider

Query parameters:

  • state — wrapped gateway state containing the tenant slug and inner Better Auth state.
  • tenantSlug (legacy) or tenant — optional slug fallback when state is not wrapped.
  • targetHost — explicit host override (opt-in via ALLOW_CUSTOM_HOST).
  • All other query parameters (code, etc.) are forwarded verbatim.

Example redirect produced by the gateway:

https://auth.afilmory.art/api/auth/callback/github?code=...&state=<wrapped>
  ⮕ 302 → https://innei.afilmory.art/api/auth/callback/github?code=...&state=<inner>

This service is intentionally stateless so it can be deployed behind a simple load balancer.