Appearance
Staging OAuth
OAuth providers register redirect URIs per app. To test changes that touch the auth flow, you typically want a separate OAuth client configured against a staging redirect URI rather than https://hivecast.ai/....
Why a separate client
Sharing the production OAuth client with staging is wrong because:
- Test users land in prod state. A successful staging sign-in creates a real principal record in production
system.auth. Cleanup is manual and error-prone. - Bad code paths can mint prod sessions. A bug in staging that signs sessions with the prod issuer string would issue valid prod cookies.
- No log isolation. Staging traffic mixes with production audit logs.
The fix is to register an additional OAuth client per provider scoped to the staging environment, with its own redirect URI and its own issuer string.
Per-provider configuration
Google
In Google Cloud Console (OAuth 2.0 Client IDs):
- Create a new "Web application" OAuth client.
- Authorized redirect URI:
https://staging.hivecast.ai/api/auth/callback/google. - Authorized JavaScript origin:
https://staging.hivecast.ai. - Copy
clientId+clientSecretto the staging environment's config.
GitHub (target state)
In GitHub Developer Settings → OAuth Apps:
- Create a new OAuth App.
- Homepage URL:
https://staging.hivecast.ai. - Authorization callback URL:
https://staging.hivecast.ai/api/auth/callback/github. - Copy
clientId+clientSecret.
Anthropic (provider OAuth)
For local-loopback OAuth flows, the redirect is always http://localhost:<port>/callback. Anthropic's app registration accepts this; no separate staging client is needed because the OAuth happens device-side, not cloud-side.
Per-environment session secret + issuer
HostSessionService is constructed with two security-relevant inputs:
secret(auth.sessionSecretconfig) — the HMAC key. Distinct per environment.issuer(auth.issuerconfig) — the JWTissclaim. Distinct per environment.
The combination prevents tokens from one environment from being accepted by another:
| Environment | Issuer | Secret rotated separately |
|---|---|---|
| Production | hivecast.ai | yes |
| Staging | staging.hivecast.ai | yes |
| Local dev | localhost-dev (or per-developer) | yes |
If staging's secret leaks, production sessions are not affected.
Distinct cookie domains
The mx_session cookie sets Path=/ without a Domain attribute, so each origin has its own cookie. Staging and prod share neither cookies nor sessions even if a developer is signed in to both in the same browser.
For browsers that share cookies across subdomains (if a Domain were ever set), a wildcard would be a security regression. The current default is correct.
Test principal isolation
Per CLAUDE.md Rule 8 (commit everything, but secrets are excluded from git):
- Do NOT commit staging OAuth client secrets to the repo.
- Use environment-specific secret managers (or
.env.stagingfiles excluded by.gitignore). - Rotate immediately if a secret is committed accidentally.
Production safety: deny-listing test domains
Staging clients in OIDC providers should be configured with hd: 'staging.hivecast.ai' or equivalent restriction where possible. This prevents a misconfigured staging client from being used to sign in real production users.
Test user accounts
For end-to-end test runs:
- Create a dedicated test Google account (
test-staging@<your domain>). - Sign in via the staging OAuth client.
- Verify that:
- the resulting
mx_sessioncookie'sissis the staging issuer - the principal record lands in the staging
system.authstate file, not production - the staging-only Devices page is reachable
- the resulting
See also
- HiveCast account login — how the cookie/JWT mints.
- Local testing — running auth without any cloud at all.
WORKSTREAMS/matrix-web/USER-FLOWS-AND-AUTH-BOUNDARIES.md— what belongs cloud-side vs Device-side.
Source:
projects/matrix-3/packages/system-auth/src/host-auth.ts(HostSessionServiceconstructor accepts secret + issuer);projects/matrix-3/packages/system-auth/src/index.ts(auth config wiring at lines 82-108).