Declare Joystick in the Idunn recipe and document the locked refresh
Recipe optional_environment gains the Joystick client id and secret file; service-contract documents the row lock, rotation fail-closed and the 410/502 answers; the StreamPixels profile names Heimdall as sole holder of provider refresh tokens (F5). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
43b7072dfb
commit
3eaa9ef265
3 changed files with 30 additions and 6 deletions
|
|
@ -157,6 +157,8 @@ optional_environment = [
|
||||||
"GC_ACCESS_PROVIDER_DISCORD_CLIENT_ID",
|
"GC_ACCESS_PROVIDER_DISCORD_CLIENT_ID",
|
||||||
"GC_ACCESS_PROVIDER_DISCORD_CLIENT_SECRET_FILE",
|
"GC_ACCESS_PROVIDER_DISCORD_CLIENT_SECRET_FILE",
|
||||||
"GC_ACCESS_PROVIDER_GITHUB_CLIENT_SECRET_FILE",
|
"GC_ACCESS_PROVIDER_GITHUB_CLIENT_SECRET_FILE",
|
||||||
|
"GC_ACCESS_PROVIDER_JOYSTICK_CLIENT_ID",
|
||||||
|
"GC_ACCESS_PROVIDER_JOYSTICK_CLIENT_SECRET_FILE",
|
||||||
"GC_ACCESS_PROVIDER_PATREON_CLIENT_ID",
|
"GC_ACCESS_PROVIDER_PATREON_CLIENT_ID",
|
||||||
"GC_ACCESS_PROVIDER_PATREON_CLIENT_SECRET_FILE",
|
"GC_ACCESS_PROVIDER_PATREON_CLIENT_SECRET_FILE",
|
||||||
"GC_ACCESS_PROVIDER_SPOTIFY_CLIENT_SECRET_FILE",
|
"GC_ACCESS_PROVIDER_SPOTIFY_CLIENT_SECRET_FILE",
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,11 @@ Corroborated by `gamecult-ops/nginx/streampixels.gamecult.org.conf:14`
|
||||||
The current StreamPixels architecture already makes useful splits:
|
The current StreamPixels architecture already makes useful splits:
|
||||||
|
|
||||||
- the web app owns the remembered viewer session
|
- the web app owns the remembered viewer session
|
||||||
- the service owns provider OAuth exchange and identity resolution
|
- Heimdall owns provider OAuth exchange, identity resolution and refresh-token
|
||||||
|
custody for Twitch, YouTube and Joystick; StreamPixels never calls a provider
|
||||||
|
token endpoint and holds no provider refresh token. It asks Heimdall for a
|
||||||
|
short-lived access token (`managed-credentials/resolve`) and treats `410
|
||||||
|
managed_credential_revoked` as "the creator must reconnect"
|
||||||
- the overlay remains auth-free
|
- the overlay remains auth-free
|
||||||
- creator memberships and operator roles are separate from general viewer state
|
- creator memberships and operator roles are separate from general viewer state
|
||||||
|
|
||||||
|
|
@ -38,7 +42,7 @@ In the service-first Heimdall version, the most likely reusable slice is:
|
||||||
|
|
||||||
- provider OAuth authority
|
- provider OAuth authority
|
||||||
- identity-linking authority
|
- identity-linking authority
|
||||||
- managed Twitch/YouTube credential custody for creator connections
|
- managed Twitch/YouTube/Joystick credential custody for creator connections (Joystick is a managed connection only, not a viewer sign-in provider)
|
||||||
- entitlement refresh
|
- entitlement refresh
|
||||||
- shared claim semantics
|
- shared claim semantics
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,7 @@ Supported providers in the skeleton:
|
||||||
- `twitch`
|
- `twitch`
|
||||||
- `youtube`
|
- `youtube`
|
||||||
- `spotify`
|
- `spotify`
|
||||||
|
- `joystick`
|
||||||
|
|
||||||
Request body:
|
Request body:
|
||||||
|
|
||||||
|
|
@ -430,9 +431,11 @@ Important behavior:
|
||||||
|
|
||||||
- the refresh token is signed by Heimdall and scoped to one app session
|
- the refresh token is signed by Heimdall and scoped to one app session
|
||||||
- the access token remains short-lived; app backends still verify it locally
|
- the access token remains short-lived; app backends still verify it locally
|
||||||
- if a stored provider access token is expired or near expiry, Heimdall uses
|
- if a stored provider access token is expired or near expiry, Heimdall takes
|
||||||
the stored provider refresh token and persists any rotation before
|
a row lock on the linked identity, re-reads it, uses the stored provider
|
||||||
evaluating entitlements
|
refresh token and persists any rotation before evaluating entitlements; an
|
||||||
|
identity whose grant the provider has revoked (no stored access token)
|
||||||
|
contributes no entitlement
|
||||||
- any app authenticated via `x-heimdall-app-secret` (`resolveAppCaller`) may
|
- any app authenticated via `x-heimdall-app-secret` (`resolveAppCaller`) may
|
||||||
send `entitlementPolicies`; Heimdall matches each stored identity to the
|
send `entitlementPolicies`; Heimdall matches each stored identity to the
|
||||||
policy for that provider. Sending policy without authenticating is
|
policy for that provider. Sending policy without authenticating is
|
||||||
|
|
@ -484,7 +487,22 @@ Important behavior:
|
||||||
- it returns access tokens only; refresh tokens remain in Heimdall custody
|
- it returns access tokens only; refresh tokens remain in Heimdall custody
|
||||||
- when a stored managed credential is expired or within the refresh window,
|
- when a stored managed credential is expired or within the refresh window,
|
||||||
Heimdall refreshes it before returning the access token and persists rotated
|
Heimdall refreshes it before returning the access token and persists rotated
|
||||||
access/refresh tokens
|
access/refresh tokens. The refresh runs under a row lock on the linked
|
||||||
|
identity (`SELECT ... FOR UPDATE`), so a given refresh token is presented to
|
||||||
|
the provider once across concurrent requests and across two Heimdall
|
||||||
|
processes sharing one database. The provider call is bounded at 10 seconds
|
||||||
|
- a provider whose refresh tokens rotate (Joystick) fails closed: a refresh
|
||||||
|
response without a new refresh token revokes the credential instead of
|
||||||
|
keeping the old token
|
||||||
|
- `410 {"error":"managed_credential_revoked","provider":...}`: the provider
|
||||||
|
rejected the grant (`invalid_grant`), or the identity holds no access token.
|
||||||
|
The stored tokens are cleared and an audit event `managed_credential_revoked`
|
||||||
|
is written; the owner must reconnect. `404 managed_credential_not_found`
|
||||||
|
means no linked identity exists
|
||||||
|
- `502 {"error":"managed_credential_refresh_failed","provider":...}`: the
|
||||||
|
refresh failed for any reason that is not a grant verdict (invalid client,
|
||||||
|
provider 5xx, timeout, malformed body). Stored tokens are untouched and a
|
||||||
|
later request may succeed
|
||||||
- app-local binding, diagnostics, subscription setup, and runtime behavior stay
|
- app-local binding, diagnostics, subscription setup, and runtime behavior stay
|
||||||
app-owned
|
app-owned
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue