mirror of
https://github.com/Afilmory/afilmory
synced 2026-02-01 22:48:17 +00:00
- 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>
4.0 KiB
4.0 KiB
Tenant & OAuth Flow
This document describes how tenant resolution, Better Auth instances, and dashboard redirects tie together across the platform.
Request Bootstrap
RequestContextMiddlewareruns first on every request.- Calls
TenantContextResolver.resolve()to populateHttpContext.tenant. - Calls
AuthProvider.getAuth()so downstream handlers reuse the tenant-aware Better Auth instance.
- Calls
TenantContextResolverinspectsx-forwarded-host,origin, andhostheaders.- Extracts a slug via
tenant-host.utils.ts. - Loads the tenant aggregate; when a subdomain hits
/author/api/authfor the first time, it auto-provisions a real tenant record withstatus = "pending"so auth flows have a fully-qualified tenant id. - Always stores the original
requestedSlugso downstream services know which workspace was requested.
- Extracts a slug via
Auth Provider
AuthProvidercaches Better Auth instances byprotocol://host::slug::settings-hash.- The slug priority is:
HttpContext.tenant.requestedSlugHttpContext.tenant.slug- Derived from the host (when the context slug is still the placeholder).
- Redirect URIs are fixed to
<OAuthGateway>/api/auth/callback/:provider(Google-friendly). - Tenant routing is encoded into the OAuth
statevalue (HMAC wrapped) so the gateway can forward callbacks to the right tenant without dynamic redirect URIs. - Because the requested slug participates in the cache key, the same Better Auth instance handles both the
/auth/socialrequest and the gateway callback, preserving OAuth state.
System Settings & Gateway
- System settings only manage:
- Registration flags
- Base domain
- OAuth gateway URL
- Provider credentials
- No per-provider redirect URIs are stored; every provider points to the centralized gateway.
/auth/social/providersreflects the enabled providers for the UI.
Session Payload
GET /auth/session returns:
{
user: BetterAuthUser,
session: BetterAuthSession,
tenant: {
id: string,
slug: string | null, // Effective slug (requested slug if present, otherwise actual)
isPlaceholder: boolean
} | null
}
- When a tenant is still provisioning (
status = "pending"),tenant.slugstill holds the requested subdomain,isPlaceholderistrue, and the dashboard stays on the onboarding surface. - Consumers simply check
tenant.isPlaceholderto know whether they are in onboarding.
Dashboard Behavior
- Welcome flow (
/platform/welcome):- Locks the slug input to
window.location.hostnameviagetTenantSlugFromHost. - Shows
TenantMissingorTenantRestrictedpages, but relies on backend redirects for actual auth.
- Locks the slug input to
- Hooks (
usePageRedirect):- If
tenantis null orisPlaceholder, stay on welcome routes. - If
tenant.slugexists and differs from the current host, sign out placeholder cookies andwindow.location.replace(buildTenantUrl(tenant.slug)). - Superadmin routes are gated separately.
- If
OAuth Happy Path
- User opens
https://slug.example.com(maybe not provisioned yet). - Resolver sets
requestedSlug = "slug", but tenant aggregate may still be the placeholder. - User clicks “Sign in with GitHub” →
/auth/socialusesrequestedSlugand redirects via the OAuth gateway. - Gateway forwards the callback to
https://slug.example.com/api/auth/callback/github. - Resolver again sets
requestedSlug = "slug"; Better Auth instance cache hits, sostatematches. /auth/sessionreturns{ tenant: { slug: "slug", isPlaceholder: true } }while the workspace is pending → dashboard stays on welcome, no cross-subdomain jump.- Once the onboarding API marks the tenant
active, future sessions haveisPlaceholder: false, andusePageRedirectensures we land on the actual workspace subdomain.
Key Guarantees
- Only a single
tenant.slugcrosses the API boundary; there are no ambiguous fields. - Placeholder detection is a boolean (
isPlaceholder). - Better Auth instances survive OAuth handshakes regardless of tenant provisioning state.