The words Under construction in black text on a yellow background with diagonal black stipes surrounding it
I'm in the process of moving my site. It's still a work in progress. Please excuse the mess and broken links.

Twitch Auth Setup

TODO: Pull subtitle into page object
Note

If you already have your app's Client ID and Client Secret you don't need to register the app, etc... You can just fill them in below, choose your scopes, then hit the button.

I struggle with auth every time I make a Twitch bot. I set this up to make it easier. It uses a Client Id and Client Secret to get a set of scopes via the "Authorization Code Grant Flow"

The Steps (From Scratch)

Launch Page:

Notes

Debugging Stuff

I'm moving stuff around right now. All this below is helping me figure out where to put stuff

        -- title

Twitch Auth Setup 

-- note
-- title: Note

If you already have your app's Client ID
and Client Secret you don't need to 
register the app, etc... You can just
fill them in below, choose your scopes,
then hit the button.


-- p

I struggle with auth every time I make 
a Twitch bot. I set this up to make it 
easier. It uses a Client Id and Client
Secret to get a set of scopes via the
"Authorization Code Grant Flow"

-- h3

The Steps (From Scratch)

-- list

- <<link|Register a new app|https://dev.twitch.tv/docs/authentication/register-app/>>
for your bot

- Open the app management page from the <<link|Developer Console|https://dev.twitch.tv/console/apps>>
if you're not already there

- Add an "OAuth Redirect URLs" entry with:

https://www.alanwsmith.com/twitch-auth/page-2.html

-- list 

- Make sure to click the save button at the bottom of the
page after adding the URL

- Copy the app's Client ID and paste it here:

-- html

<label for="clientId">Client Id: </label>
<input name="clientId" id="clientId" type="password" size="40" />

-- list

- Generate a Client Secret for the app, put it in your
password manager, then paste it here:

-- html

<label for="clientSecret">Client Secret: </label>
<input name="clientSecret" id="clientSecret" type="password" size="40" />

-- list

- Choose the scopes you want

(IRC Chatbots need channel:moderate, chat:edit, 
chat:read, whispers:edit, and whispers:read. 
All of those are selected here by default)

(NOTE: If you selected everything it probably won't
work.)

-- html

<p><input type="checkbox" id="analytics_read_extensions" data-scope="analytics%3Aread%3Aextensions" class="scope" /> <label for="analytics_read_extensions">analytics:read:extensions</label></p>

<p><input type="checkbox" id="analytics_read_games" data-scope="analytics%3Aread%3Agames" class="scope" /> <label for="analytics_read_games">analytics:read:games</label></p>

<p><input type="checkbox" id="bits_read" data-scope="bits%3Aread" class="scope" /> <label for="bits_read">bits:read</label></p>

<p><input type="checkbox" id="channel_edit_commercial" data-scope="channel%3Aedit%3Acommercial" class="scope" /> <label for="channel_edit_commercial">channel:edit:commercial</label></p>

<p><input type="checkbox" id="channel_manage_broadcast" data-scope="channel%3Amanage%3Abroadcast" class="scope" /> <label for="channel_manage_broadcast">channel:manage:broadcast</label></p>

<p><input type="checkbox" id="channel_manage_extensions" data-scope="channel%3Amanage%3Aextensions" class="scope" /> <label for="channel_manage_extensions">channel:manage:extensions</label></p>

<p><input type="checkbox" id="channel_manage_guest_star" data-scope="channel%3Amanage%3Aguest_star" class="scope" /> <label for="channel_manage_guest_star">channel:manage:guest_star</label></p>

<p><input type="checkbox" id="channel_manage_moderators" data-scope="channel%3Amanage%3Amoderators" class="scope" /> <label for="channel_manage_moderators">channel:manage:moderators</label></p>

<p><input type="checkbox" id="channel_manage_polls" data-scope="channel%3Amanage%3Apolls" class="scope" /> <label for="channel_manage_polls">channel:manage:polls</label></p>

<p><input type="checkbox" id="channel_manage_predictions" data-scope="channel%3Amanage%3Apredictions" class="scope" /> <label for="channel_manage_predictions">channel:manage:predictions</label></p>

<p><input type="checkbox" id="channel_manage_raids" data-scope="channel%3Amanage%3Araids" class="scope" /> <label for="channel_manage_raids">channel:manage:raids</label></p>

<p><input type="checkbox" id="channel_manage_redemptions" data-scope="channel%3Amanage%3Aredemptions" class="scope" /> <label for="channel_manage_redemptions">channel:manage:redemptions</label></p>

<p><input type="checkbox" id="channel_manage_schedule" data-scope="channel%3Amanage%3Aschedule" class="scope" /> <label for="channel_manage_schedule">channel:manage:schedule</label></p>

<p><input type="checkbox" id="channel_manage_videos" data-scope="channel%3Amanage%3Avideos" class="scope" /> <label for="channel_manage_videos">channel:manage:videos</label></p>

<p><input type="checkbox" id="channel_manage_vips" data-scope="channel%3Amanage%3Avips" class="scope" /> <label for="channel_manage_vips">channel:manage:vips</label></p>

<p><input type="checkbox" id="channel_moderate" data-scope="channel%3Amoderate" class="scope" checked /> <label for="channel_moderate">channel:moderate</label></p>

<p><input type="checkbox" id="channel_read_charity" data-scope="channel%3Aread%3Acharity" class="scope" /> <label for="channel_read_charity">channel:read:charity</label></p>

<p><input type="checkbox" id="channel_read_editors" data-scope="channel%3Aread%3Aeditors" class="scope" /> <label for="channel_read_editors">channel:read:editors</label></p>

<p><input type="checkbox" id="channel_read_goals" data-scope="channel%3Aread%3Agoals" class="scope" /> <label for="channel_read_goals">channel:read:goals</label></p>

<p><input type="checkbox" id="channel_read_guest_star" data-scope="channel%3Aread%3Aguest_star" class="scope" /> <label for="channel_read_guest_star">channel:read:guest_star</label></p>

<p><input type="checkbox" id="channel_read_hype_train" data-scope="channel%3Aread%3Ahype_train" class="scope" /> <label for="channel_read_hype_train">channel:read:hype_train</label></p>

<p><input type="checkbox" id="channel_read_polls" data-scope="channel%3Aread%3Apolls" class="scope" /> <label for="channel_read_polls">channel:read:polls</label></p>

<p><input type="checkbox" id="channel_read_predictions" data-scope="channel%3Aread%3Apredictions" class="scope" /> <label for="channel_read_predictions">channel:read:predictions</label></p>

<p><input type="checkbox" id="channel_read_redemptions" data-scope="channel%3Aread%3Aredemptions" class="scope" /> <label for="channel_read_redemptions">channel:read:redemptions</label></p>

<p><input type="checkbox" id="channel_read_stream_key" data-scope="channel%3Aread%3Astream_key" class="scope" /> <label for="channel_read_stream_key">channel:read:stream_key</label></p>

<p><input type="checkbox" id="channel_read_subscriptions" data-scope="channel%3Aread%3Asubscriptions" class="scope" /> <label for="channel_read_subscriptions">channel:read:subscriptions</label></p>

<p><input type="checkbox" id="channel_read_vips" data-scope="channel%3Aread%3Avips" class="scope" /> <label for="channel_read_vips">channel:read:vips</label></p>

<p><input type="checkbox" id="chat_edit" data-scope="chat%3Aedit" class="scope" checked /> <label for="chat_edit">chat:edit</label></p>

<p><input type="checkbox" id="chat_read" data-scope="chat%3Aread" class="scope" checked /> <label for="chat_read">chat:read</label></p>

<p><input type="checkbox" id="clips_edit" data-scope="clips%3Aedit" class="scope" /> <label for="clips_edit">clips:edit</label></p>

<p><input type="checkbox" id="moderation_read" data-scope="moderation%3Aread" class="scope" /> <label for="moderation_read">moderation:read</label></p>

<p><input type="checkbox" id="moderator_manage_announcements" data-scope="moderator%3Amanage%3Aannouncements" class="scope" /> <label for="moderator_manage_announcements">moderator:manage:announcements</label></p>

<p><input type="checkbox" id="moderator_manage_automod" data-scope="moderator%3Amanage%3Aautomod" class="scope" /> <label for="moderator_manage_automod">moderator:manage:automod</label></p>

<p><input type="checkbox" id="moderator_manage_automod_settings" data-scope="moderator%3Amanage%3Aautomod_settings" class="scope" /> <label for="moderator_manage_automod_settings">moderator:manage:automod_settings</label></p>

<p><input type="checkbox" id="moderator_manage_banned_users" data-scope="moderator%3Amanage%3Abanned_users" class="scope" /> <label for="moderator_manage_banned_users">moderator:manage:banned_users</label></p>

<p><input type="checkbox" id="moderator_manage_blocked_terms" data-scope="moderator%3Amanage%3Ablocked_terms" class="scope" /> <label for="moderator_manage_blocked_terms">moderator:manage:blocked_terms</label></p>

<p><input type="checkbox" id="moderator_manage_chat_messages" data-scope="moderator%3Amanage%3Achat_messages" class="scope" /> <label for="moderator_manage_chat_messages">moderator:manage:chat_messages</label></p>

<p><input type="checkbox" id="moderator_manage_chat_settings" data-scope="moderator%3Amanage%3Achat_settings" class="scope" /> <label for="moderator_manage_chat_settings">moderator:manage:chat_settings</label></p>

<p><input type="checkbox" id="moderator_manage_guest_star" data-scope="moderator%3Amanage%3Aguest_star" class="scope" /> <label for="moderator_manage_guest_star">moderator:manage:guest_star</label></p>

<p><input type="checkbox" id="moderator_manage_shield_mode" data-scope="moderator%3Amanage%3Ashield_mode" class="scope" /> <label for="moderator_manage_shield_mode">moderator:manage:shield_mode</label></p>

<p><input type="checkbox" id="moderator_manage_shoutouts" data-scope="moderator%3Amanage%3Ashoutouts" class="scope" /> <label for="moderator_manage_shoutouts">moderator:manage:shoutouts</label></p>

<p><input type="checkbox" id="moderator_read_automod_settings" data-scope="moderator%3Aread%3Aautomod_settings" class="scope" /> <label for="moderator_read_automod_settings">moderator:read:automod_settings</label></p>

<p><input type="checkbox" id="moderator_read_blocked_terms" data-scope="moderator%3Aread%3Ablocked_terms" class="scope" /> <label for="moderator_read_blocked_terms">moderator:read:blocked_terms</label></p>

<p><input type="checkbox" id="moderator_read_chat_settings" data-scope="moderator%3Aread%3Achat_settings" class="scope" /> <label for="moderator_read_chat_settings">moderator:read:chat_settings</label></p>

<p><input type="checkbox" id="moderator_read_chatters" data-scope="moderator%3Aread%3Achatters" class="scope" /> <label for="moderator_read_chatters">moderator:read:chatters</label></p>

<p><input type="checkbox" id="moderator_read_followers" data-scope="moderator%3Aread%3Afollowers" class="scope" /> <label for="moderator_read_followers">moderator:read:followers</label></p>

<p><input type="checkbox" id="moderator_read_guest_star" data-scope="moderator%3Aread%3Aguest_star" class="scope" /> <label for="moderator_read_guest_star">moderator:read:guest_star</label></p>

<p><input type="checkbox" id="moderator_read_shield_mode" data-scope="moderator%3Aread%3Ashield_mode" class="scope" /> <label for="moderator_read_shield_mode">moderator:read:shield_mode</label></p>

<p><input type="checkbox" id="moderator_read_shoutouts" data-scope="moderator%3Aread%3Ashoutouts" class="scope" /> <label for="moderator_read_shoutouts">moderator:read:shoutouts</label></p>

<p><input type="checkbox" id="user_edit" data-scope="user%3Aedit" class="scope" /> <label for="user_edit">user:edit</label></p>

<p><input type="checkbox" id="user_edit_follows" data-scope="user%3Aedit%3Afollows" class="scope" /> <label for="user_edit_follows">user:edit:follows</label></p>

<p><input type="checkbox" id="user_manage_blocked_users" data-scope="user%3Amanage%3Ablocked_users" class="scope" /> <label for="user_manage_blocked_users">user:manage:blocked_users</label></p>

<p><input type="checkbox" id="user_manage_chat_color" data-scope="user%3Amanage%3Achat_color" class="scope" /> <label for="user_manage_chat_color">user:manage:chat_color</label></p>

<p><input type="checkbox" id="user_manage_whispers" data-scope="user%3Amanage%3Awhispers" class="scope" /> <label for="user_manage_whispers">user:manage:whispers</label></p>

<p><input type="checkbox" id="user_read_blocked_users" data-scope="user%3Aread%3Ablocked_users" class="scope" /> <label for="user_read_blocked_users">user:read:blocked_users</label></p>

<p><input type="checkbox" id="user_read_broadcast" data-scope="user%3Aread%3Abroadcast" class="scope" /> <label for="user_read_broadcast">user:read:broadcast</label></p>

<p><input type="checkbox" id="user_read_email" data-scope="user%3Aread%3Aemail" class="scope" /> <label for="user_read_email">user:read:email</label></p>

<p><input type="checkbox" id="user_read_follows" data-scope="user%3Aread%3Afollows" class="scope" /> <label for="user_read_follows">user:read:follows</label></p>

<p><input type="checkbox" id="user_read_subscriptions" data-scope="user%3Aread%3Asubscriptions" class="scope" /> <label for="user_read_subscriptions">user:read:subscriptions</label></p>

<p><input type="checkbox" id="whispers_edit" data-scope="whispers%3Aedit" class="scope" checked /> <label for="whispers_edit">whispers:edit</label></p>

<p><input type="checkbox" id="whispers_read" data-scope="whispers%3Aread" class="scope" checked /> <label for="whispers_read">whispers:read</label></p>


-- list 

- Click the button below. It will take you to a Twitch
page where you'll be asked to login (if you aren't
already) and Authorize the app (if you haven't already). 
After that it will redirect back to this site. If everything 
goes well you're Access Token and Refresh Token will be 
delivered. 

-- html

Launch Page: <button id="stepOne">Fill in Client ID and Client Secret above</button>

-- script

const prepStepOne = () => {
  if (clientId.value !== "" && clientSecret.value !== "") {
    stepOne.innerText = "Open Twitch Auth Window"
    stepOne.addEventListener("click", sendStepOne)
  } else  {
    stepOne.innerText = "Fill in Client ID and Client Secret above"
  }
}

const sendStepOne = () => {
  if (clientId.value !== "" && clientSecret.value !== "") {
    const redirect_uri = `${window.location.origin}/twitch-auth/page-2.html`
    localStorage.setItem("twitch_client_id", clientId.value)
    localStorage.setItem("twitch_client_secret", clientSecret.value)
    let url = `https://id.twitch.tv/oauth2/authorize`

    const scopes = document.getElementsByClassName("scope")
    const scopeList = []
    for (let i = 0; i < scopes.length; i ++) {
      if (scopes[i].checked) {
        scopeList.push(scopes[i].dataset.scope)
      }
    }

    url += `?response_type=code`
    url += `&client_id=${clientId.value}`
    url += `&redirect_uri=${redirect_uri}`
    url += `&scope=${scopeList.join("+")}`
    // url += `&scope=channel%3Amoderate+chat%3Aedit+chat%3Aread+whispers%3Aread+whispers%3Aedit`
    url += `&state=kehjssc3a609ea11e793ae92361f002671`
    // console.log(url)
    window.location.href = url
  }
}

document.addEventListener("DOMContentLoaded", () => {
  clientId.addEventListener("input", () => {
    prepStepOne()
  })

  clientSecret.addEventListener("input", () => {
    prepStepOne()
  })
})



-- notes
-- title: Notes

- Do not just select every scope. Per the twitch page

"An application must request only the scopes required 
by the APIs that their app calls. If you request more 
scopes than is required to support your app’s 
functionality, Twitch may suspend your application’s 
access to the Twitch API"

- I'm not sure, but getting a new credential might
wipe out any existing credentials you have. 

- This works as of Aug. 2023. Twitch has
announced plans to change things so no
guarantees on its longevity 

- This page does not send anything back to
my server. But, I'm a random person on the 
internet. I encourage you to check the
source to confirm that

- It does, however, use localstorage to 
pass credentials to the second page. 
Those values get cleared by the do
pass through that mechanism

- The app Client Secret is only available 
to view/copy when it's first generated. 
Make sure to put it in your password 
manager

- There's not a lot of error handling 
here. If something goes sideways you're
kinda on your own. 

-- ref
-- title: Twitch Scopes
-- url: https://dev.twitch.tv/docs/authentication/scopes/

-- ref
-- title: Register A Twitch App
-- url: https://dev.twitch.tv/docs/authentication/register-app/

-- ref
-- title: Twitch Developer Console Apps Page
-- url: https://dev.twitch.tv/console/apps

-- ref
-- title: Twitch: Authorization Code Grant Flow
-- url: https://dev.twitch.tv/docs/authentication/getting-tokens-oauth/#authorization-code-grant-flow

-- ref
-- title: Authenticating with the Twitch IRC Server
-- url: https://dev.twitch.tv/docs/irc/authenticate-bot/

-- ref
-- title: Getting Started with Chat and Chatbots
-- url: https://dev.twitch.tv/docs/irc/get-started/

-- categories
-- Twitch 

-- metadata
-- date: 2023-08-21 00:56:55
-- id: 2uhq28kx
-- site: aws
-- type: post
-- status: draft 
-- path: /twitch-auth