69 lines
2.5 KiB
TypeScript
69 lines
2.5 KiB
TypeScript
/**
|
|
* Better-auth client factory for the auth-only SPA (`apps/ui-server-auth`).
|
|
*
|
|
* Separate from the stage-ui singleton because that client is Bearer-only:
|
|
* it omits cookies and injects the auth-store token on every request, which
|
|
* makes no sense on the page the session cookie was just set on. This client
|
|
* uses better-auth's cookie defaults (`credentials: 'include'`) instead.
|
|
*
|
|
* Transport seam: pass `fetchImpl` to substitute `globalThis.fetch` (wired as
|
|
* `customFetchImpl`; see node_modules/better-auth/dist/client/config.mjs L+
|
|
* — the `restOfFetchOptions` spread happens after the default, so a
|
|
* user-supplied value wins). Test fetches and request-scoped abort signals
|
|
* both bypass memoisation so state cannot leak into the next attempt;
|
|
* ordinary production callers still memoise per `apiServerUrl`.
|
|
*
|
|
* Removal condition: better-auth ships a hosted typed client for OIDC IdP
|
|
* setups where one process is both IdP and resource server. Until then,
|
|
* one factory per credential mode is the cleanest contract.
|
|
*/
|
|
|
|
import { steamClient } from '@proj-airi/stage-ui/libs/steam-auth-client'
|
|
import { createAuthClient } from 'better-auth/vue'
|
|
|
|
export interface AuthClientArgs {
|
|
apiServerUrl: string
|
|
/**
|
|
* Optional fetch override for tests. When provided we *do not* memoise so
|
|
* every test case can install its own mock without bleed-through.
|
|
*/
|
|
fetchImpl?: typeof fetch
|
|
/**
|
|
* Optional signal for one request-scoped client. Supplying it disables
|
|
* memoisation so a later sign-in attempt receives a fresh signal.
|
|
*/
|
|
requestSignal?: AbortSignal
|
|
}
|
|
|
|
type AuthClient = ReturnType<typeof createAuthClient<{
|
|
baseURL: string
|
|
plugins: ReturnType<typeof steamClient>[]
|
|
}>>
|
|
|
|
const clientCache = new Map<string, AuthClient>()
|
|
|
|
/**
|
|
* Cookie-credentialed better-auth client for the auth UI, with the Steam
|
|
* plugin wired in (`linkSteam` / `signIn.steam`). Unlike the Bearer-only
|
|
* stage-ui singleton, this client carries the session cookie.
|
|
*/
|
|
export function getAuthClient(args: AuthClientArgs): AuthClient {
|
|
if (args.fetchImpl || args.requestSignal) {
|
|
return createAuthClient({
|
|
baseURL: args.apiServerUrl,
|
|
plugins: [steamClient()],
|
|
fetchOptions: {
|
|
...(args.fetchImpl ? { customFetchImpl: args.fetchImpl } : {}),
|
|
...(args.requestSignal ? { signal: args.requestSignal } : {}),
|
|
},
|
|
})
|
|
}
|
|
|
|
const client = clientCache.get(args.apiServerUrl) ?? createAuthClient({
|
|
baseURL: args.apiServerUrl,
|
|
plugins: [steamClient()],
|
|
})
|
|
clientCache.set(args.apiServerUrl, client)
|
|
return client
|
|
}
|