Files
Twungeon/docs/twitch-setup.md

8.3 KiB

Twitch setup

This guide connects the Twungeon proof of concept to a Twitch development channel. Never commit real values: copy .env.example to an ignored .env or provide the variables through your process manager.

Prerequisites

  • Node.js 22 or newer and npm.
  • A Twitch developer application and a development channel.
  • A Twitch Extension with identity sharing enabled. Twungeon requires the numeric user_id; anonymous or opaque-only viewers fail closed.
  • A confidential Twitch developer application whose OAuth callback is the public Twungeon /oauth/callback URL.
  • The Extension shared secret, copied exactly as the base64 value supplied by the Extension Manager.

The installed Twurple packages are locked in package-lock.json. Re-check their release documentation and Twitch's current scope requirements before rotating tokens or upgrading packages.

Required environment

Provide the following values first. Keep TWITCH_ENABLED=false until OAuth, Extension, and Channel Points setup are complete; then switch it to true for live operation.

Variable Purpose
TWITCH_CLIENT_ID Confidential OAuth application client ID
TWITCH_CLIENT_SECRET Server-only application secret
TWITCH_BROADCASTER_ID Numeric channel owner ID
TWITCH_CHANNEL_LOGIN Channel login joined by Twurple chat
TWITCH_EXTENSION_SECRET Base64 Extension shared secret
TWITCH_OAUTH_REDIRECT_URI Exact HTTPS OAuth callback registered with Twitch
TWITCH_TOKEN_FILE Restricted file used for access and refresh tokens
CHANNEL_POINTS_RESURRECTION_REWARD_ID Stable ID of the resurrection custom reward
PUBLIC_BASE_URL Public HTTPS backend origin

The user token currently needs chat:read, moderator:read:followers, and channel:read:redemptions. The follower and Channel Points subscriptions use the broadcaster token, so its subject must match TWITCH_BROADCASTER_ID. Twungeon subscribes only to the configured custom reward and uses Twitch's stable redemption ID as the deduplication key. The broadcaster owns the reward cost in Twitch; the backend does not duplicate it.

Broadcaster OAuth authorization

  1. Register a Confidential Twitch application with an exact HTTPS redirect such as https://twungeon.example/oauth/callback.
  2. Configure TWITCH_CLIENT_ID, TWITCH_CLIENT_SECRET, TWITCH_CHANNEL_LOGIN, TWITCH_OAUTH_REDIRECT_URI, and TWITCH_TOKEN_FILE while leaving TWITCH_ENABLED=false.
  3. Restart Twungeon and confirm /oauth/status reports configured: true and authorized: false.
  4. Open /oauth/login in a browser and authorize using the configured broadcaster account. Twungeon requests only chat:read, moderator:read:followers, and channel:read:redemptions.
  5. Confirm the callback reports success and /oauth/status reports authorized: true. The callback validates the client ID, broadcaster login, and required scopes before storing the token.
  6. Record the numeric broadcaster ID shown by the callback as TWITCH_BROADCASTER_ID.

The access and refresh tokens are stored atomically at TWITCH_TOKEN_FILE with owner-only permissions. Twurple refreshes the access token when necessary and Twungeon replaces the stored token without printing either token. The token directory must be writable only by the Twungeon service account. Never place the token file inside the repository or a web-served directory.

Extension configuration

Twungeon's production viewer is a Video Overlay Extension. Twitch's current Extension Manager labels this asset type Video - Fullscreen and its path field Video - Fullscreen View Path. This is a manual dashboard setting; the repository has no manifest that can change an Extension version in Twitch.

  1. Create a Twitch Extension separately from the confidential OAuth application, or create a new test version of the existing Twungeon Extension.
  2. On Asset Hosting, set Testing Base URI to the public HTTPS Twungeon origin with a trailing /.
  3. Under Type of Extension, enable Video - Fullscreen (the video-overlay placement) and set Video - Fullscreen View Path to extension. Do not use Video - Component for the production viewer. If a component entry is retained temporarily for development, it may point at the same shared page, but the overlay placement is the supported viewer experience.
  4. On Capabilities, enable Request Identity Link. The viewer must click Share Twitch identity in the overlay; the Extension Helper then invokes requestIdShare() from that user gesture and supplies a new JWT through onAuthorized.
  5. Put the version in Local Test or Hosted Test and activate it in the channel's video-overlay slot. Changing an already released version may require a new Extension version and Twitch review; the repository cannot submit or activate that version automatically.
  6. Test the overlay while live in normal, theater, fullscreen, and a narrow player. The video player and stream supply the game frame; /extension should show only the upper-left controls on a transparent canvas. The same viewer path handles mobile: Twitch context selects the mobile layout when available and responsive CSS is the fallback. Do not configure a second Extension, stream, or backend instance for mobile viewers.
  7. A JWT without user_id, with the wrong channel_id, an expired signature, or an external role is rejected.
  8. Keep the Extension secret only in the backend environment. It must never be included in the UI bundle or URL.
  9. Start with npm run build && npm start. Confirm /health returns ready: true and twitchMode: "configured" after chat connects.

The Extension JWT is exchanged for a random 15-minute Twungeon session. A refresh obtains a new Twitch JWT, re-verifies it, and restores the existing character; it does not create a player.

Channel Points reward

  1. In the broadcaster dashboard, create one custom reward for resurrection.
  2. Choose its title and cost in Twitch. Disable viewer text input unless the channel has a separate moderation reason to keep it.
  3. Enable Skip Reward Requests Queue because Twungeon consumes the redemption immediately and uses read-only EventSub access rather than managing fulfillment status.
  4. Retrieve the reward's stable ID through the Twitch API or Twitch CLI using the broadcaster token, and set it as CHANNEL_POINTS_RESURRECTION_REWARD_ID.
  5. Do not reuse that reward for another action. Redemptions of every other reward are ignored by the domain.

Bits, Cheers, and Bits-in-Extensions products are outside the MVP and have no gameplay handler.

Event flow

  1. A follower types !spawn in chat.
  2. Twurple supplies the stable chatter ID; the API checks that ID against the broadcaster's followers.
  3. The Extension sends its current Twitch JWT to POST /api/extension/session.
  4. The backend verifies signature, expiry, channel, role, and numeric user ID.
  5. Controls enable only when the verified ID equals the chat-spawn owner ID.
  6. A redemption of the configured Channel Points reward by the dead player invokes resurrection exactly once.

Troubleshooting and rotation

  • Chat disconnected: check token validity, chat:read, channel login, and outbound WebSocket access. Existing game state continues; new Twitch inputs fail closed until reconnect.
  • Follower rejected: ensure the token belongs to the broadcaster and has moderator:read:followers; confirm the numeric broadcaster ID.
  • Extension returns UNAUTHENTICATED: check the base64 secret, JWT expiry, allowed channel, and identity-sharing capability. Never log the JWT.
  • IDENTITY_NOT_BOUND: compare the chat and verified Extension numeric IDs; display names are deliberately ignored for ownership.
  • Reward does not resurrect: confirm the configured reward ID, the channel:read:redemptions scope, the dead character owner ID, and that the redemption was not already processed.
  • Public iframe fails: confirm TLS, allowed origins, Extension asset paths, and that WebSocket traffic reaches /ws.

To rotate credentials, stop new Twitch intake, replace the server environment, restart, verify /health, test one follower check and Extension login, then revoke the old token/secret. A full process restart creates a new in-memory run, as required by the MVP persistence boundary.