Google is a Heimdall provider behind Bifrost, with no direct upstream. Scopes openid email profile; the callback verifies the id_token (RS256 against Google's keys, iss, aud, exp, iat, nonce) and requires a verified email. The identity key is sub, never the email. PKCE verifier and nonce are derived from the state jti and Heimdall's signing key, so nothing is stored between start and callback. Google never mints an account. mode=link needs a Heimdall access token for the app and attaches to that account only; sign_in signs in an identity already linked; an unknown sub is refused, and a sub held by another account is never moved. The six existing providers still mint on first login. Admitted to the bifrost profile only. The Idunn recipe declares the Google client id and secret file. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
109 lines
5.4 KiB
Markdown
109 lines
5.4 KiB
Markdown
# Heimdall
|
|
|
|
Heimdall is the shared auth authority taking shape for GameCult-hosted
|
|
experiments.
|
|
|
|
The point is painfully simple:
|
|
|
|
- stop rebuilding Discord/Patreon login glue in every app
|
|
- keep provider OAuth, linked identities, grants, entitlements, and session
|
|
issuance in one place
|
|
- let app runtimes verify signed claims locally so "shared auth" does not
|
|
become "everything phones home for every request"
|
|
- keep app-domain data separate so "shared auth" does not become "shared swamp"
|
|
|
|
## Front Door
|
|
|
|
- **Value:** one shared auth authority: provider OAuth (Discord, Patreon, GitHub, Twitch, YouTube, Spotify, Google OIDC for linking), linked identities, grants, entitlements and session issuance, with Ed25519-signed claims that app backends verify locally.
|
|
- **Problem:** every GameCult app was rebuilding Discord/Patreon login glue. Heimdall centralizes identity authority and keeps app-domain data in the apps.
|
|
- **Entry points:**
|
|
- `src/index.ts` and `src/app.ts`: the Fastify service. `src/signing.ts` and `src/claims.ts` issue JWTs and serve `/.well-known/jwks.json`; `src/oauth.ts` and `src/providers.ts` own provider flows.
|
|
- `src/verifier.ts`: the local verifier helper an app backend imports to validate Heimdall JWTs without phoning home.
|
|
- `src/odin-publication.ts` and `plugins/gamecult.heimdall.access`: the redacted Odin/Eve provider advertisement and the `gamecult.heimdall.access` plugin (`docs/eve-access-plugin.md`).
|
|
- `docs/architecture.md`, `docs/service-contract.md`, `docs/verse-service-contract.md`, `docs/app-profiles/`: the contracts.
|
|
- `pnpm install`, `pnpm dev`, `pnpm test` (see Dev Commands).
|
|
- **Live:** a stateful first slice: signing and JWKS, OAuth state for six providers, in-memory and Postgres stores, a real Discord callback path for the Repixelizer flow, sealed provider tokens, the loopback CultNet operation boundary, Idunn health publication, and the deployed Ghostlight Eve host consuming the discovered boundary.
|
|
- **Planned:** the two-account and eight-account Ghostlight human canaries, refresh and revocation flows, admin surfaces, and broader inspection projections beyond the redacted access boundary.
|
|
|
|
## Current Shape
|
|
|
|
This repo now has a stateful first slice instead of a decorative skeleton.
|
|
|
|
Landed right now:
|
|
|
|
- a TypeScript/Fastify service scaffold under `src/`
|
|
- Ed25519 JWT signing plus `/.well-known/jwks.json`
|
|
- file-backed signing-key loading/bootstrap so restart does not vaporize the
|
|
service identity when you configure a real key path
|
|
- signed OAuth start-state scaffolding for Discord, Patreon, GitHub, Twitch,
|
|
YouTube, and Spotify
|
|
- durable auth/control-plane storage with in-memory and Postgres backends
|
|
- account, linked-identity, session, persistent OAuth attempt, grant,
|
|
entitlement snapshot, and audit
|
|
persistence
|
|
- a real Discord OAuth callback path for the first Repixelizer access flow
|
|
- direct backend callback handoff for same-host or Yggdrasil-reachable app
|
|
backends
|
|
- AES-256-GCM sealing for managed provider tokens at rest
|
|
- a one-time browser completion exchange kept as fallback for opener
|
|
`postMessage` handoff
|
|
- app profile surfaces for Repixelizer, StreamPixels, and Spotiverse
|
|
- signed claim issuance for app-local verification experiments
|
|
- a local verifier helper in `src/verifier.ts` so app backends can validate
|
|
Heimdall JWTs without phoning home on every guarded request
|
|
- a read-only Odin/Eve provider advertisement export that names redacted
|
|
CultCache witness document shapes and paths without touching live secrets
|
|
- a daemon-owned CultCache boundary witness at `GC_ACCESS_CULTCACHE_PATH`
|
|
containing the Heimdall provider advertisement, command boundary, transport
|
|
profile, and Idunn daemon-health summary
|
|
- optional Idunn daemon-health publication over `cultnet.transport.rudp.v0`
|
|
when `GC_ACCESS_IDUNN_RUDP_HEALTH` is configured
|
|
- the `gamecult.heimdall.access` Eve plugin with executable `describe`,
|
|
`validate`, and `project` operations plus a browser reference adapter
|
|
- a loopback-only CultNet operation boundary on port `4101` for authenticated
|
|
`heimdall.auth.begin` and `heimdall.auth.complete`; app payloads and completion
|
|
claims are AES-256-GCM wrapped and HMAC-bound to expiry, nonce, operation,
|
|
runtime, and idempotency identity
|
|
- redacted provider, command-boundary, plugin, and transport records published
|
|
through Odin under unique typed catalog keys; secret-bearing auth state never
|
|
enters the catalog
|
|
- the deployed Ghostlight Eve host consumes that discovered boundary and keeps
|
|
routine app-session verification local
|
|
|
|
Remaining work:
|
|
|
|
- the two-account and eight-account Ghostlight human canaries
|
|
- refresh/revocation flows and admin surfaces
|
|
- broader admin and inspection projections beyond the redacted access boundary
|
|
|
|
Canonical docs:
|
|
|
|
- `docs/architecture.md`
|
|
- `docs/implementation-plan.md`
|
|
- `docs/service-contract.md`
|
|
- `docs/verse-service-contract.md`
|
|
- `docs/app-profiles/repixelizer.md`
|
|
- `docs/app-profiles/streampixels.md`
|
|
- `docs/app-profiles/spotiverse.md`
|
|
- `state/map.yaml`
|
|
- `notes/fresh-workspace-handoff.md`
|
|
|
|
## Dev Commands
|
|
|
|
```powershell
|
|
pnpm install
|
|
pnpm dev
|
|
pnpm test
|
|
pnpm build
|
|
pnpm export:provider-advertisement
|
|
```
|
|
|
|
## Working Loop
|
|
|
|
1. Rehydrate from canonical state.
|
|
2. Keep identity authority central and app-domain data local.
|
|
3. Prefer a Yggdrasil-hosted shared service over duplicated per-app provider
|
|
stacks.
|
|
4. Let app backends verify signed claims locally instead of begging Heimdall on
|
|
every guarded route.
|
|
5. Commit finished passes before the notes start breeding.
|