
Security News
Deno 2.2 Improves Dependency Management and Expands Node.js Compatibility
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
twitch-auth
Advanced tools
Utility module for handling Twitch.tv chatbot authentication. The authentication process is detailed here.
The following functions are exported by the module.
Gets a user access token required for login (using tmi.js for example). This should be used once after the initial authorization step. Subsequent logins should use the token refresh process.
creds
is an object structured as follows:
{
"client_id": <your client ID>,
"client_secret": <your client secret>
}
code
is the OAuth 2.0 authorization code returned by Twitch.
Returns a Promise that resolves to an object parsed from the JSON returned from the server, or rejects with an error. The returned object looks like this:
{
"access_token": "<user access token>",
"refresh_token": "<refresh token>",
"expires_in": <number of seconds until the token expires>,
"scope": "<your previously listed scope(s)>",
"token_type": "bearer"
}
Gets a new (refreshed) user access token for login.
creds
...same as above.
refreshToken
is the refresh_token returned from the server in calls to
getUserAccessToken() and/or
refreshUserAccessToken().
Gets an app access token. This is not applicable to chat login, but I wrote the function before I realized that :P
creds
...same as above.
This is basically what I use to launch my chat bot.
It is derived from Twitch's echo/haiku sample and has been built up from there (with the twitch-auth module being refactored out of it).
It uses a local Redis server to cache access/refresh tokens. Run it initially with -c <auth code>
to get your first user access token.
After that, it will automatically refresh tokens as necessary.
#!/usr/bin/nodejs --harmony
//----------------------------------------------------------------
'use strict';
//----------------------------------------------------------------
const process = require('process')
const minimist = require('minimist')
const fs = require('fs')
const tmi = require('tmi.js')
const redis = require('redis');
const util = require('util')
// Twitch Auth Util (tau)
const tau = require('twitch-auth')
const defaultConfigPath = 'config.json';
const defaultConfig = {
credsPath: './creds.json',
reconnect: true
};
//----------------------------------------------------------------
// Called every time a message comes in:
function onMessageHandler(target, context, msg, self) {
// Ignore messages from the bot
if (self) {
return;
}
console.log(msg);
}
function parseArgs(argv) {
let args = minimist(argv.slice(2));
if (args['_'].length > 0) {
args._.forEach((unknownOption) => {
console.log(`I don't know what "${unknownOption}" is.`);
});
return (null);
}
return (args);
}
function getConfigPath(args, defaultPath=defaultConfigPath) {
if ('F' in args) {
return (args['F']);
}
return (defaultPath);
}
function getConfig(args) {
let configPath = getConfigPath(args);
let config = defaultConfig;
try {
fs.accessSync(configPath, fs.constants.F_OK | fs.constants.R_OK);
let configString = fs.readFileSync(configPath, 'utf8');
config = Object.assign(config, JSON.parse(configString));
}
catch (err) {
console.log(err);
}
return (config);
}
function getCreds(credsPath) {
let credString = fs.readFileSync(credsPath, 'utf8');
return (JSON.parse(credString));
}
function redisValue(redisClient, key) {
return (new Promise((resolve, reject) => {
redisClient.get(key, (err, result) => {
console.log('redis client returned: ' + result);
if (err) {
reject(err);
}
resolve(result);
});
}));
}
function accessTokenFromAuthCode(creds, code) {
return (tau.getUserAccessToken(creds, code));
}
function accessTokenFromCache() {
//console.log('accessTokenFromCache()');
let redisClient = redis.createClient();
let tokens = Promise.all([
redisValue(redisClient, "access_token"),
redisValue(redisClient, "refresh_token")])
.then((results) => {
//console.log('results: ' + results);
redisClient.end();
return ({access_token: results[0],
refresh_token: results[1]});
});
return (tokens);
}
function cacheTokenResponse(response) {
let redisClient = redis.createClient();
return (new Promise((resolve, reject) => {
if (('access_token' in response ) &&
('refresh_token' in response)) {
redisClient.mset('access_token', response.access_token,
'refresh_token', response.refresh_token, (err, reply) => {
console.log('redis client returned: ' + reply);
redisClient.end();
if (err) {
reject(err);
}
resolve(reply);
});
}
}));
}
function reconnect(context) {
return (tau.refreshUserAccessToken(context.creds, context.tokens.refresh_token)
.then((response) => {
// Update the tokens in our context oject.
context.tokens.access_token = response.access_token;
context.tokens.refresh_token = response.refresh_token;
return (response);})
.then(cacheTokenResponse)
.then((reply) => {
// reply is useless ("OK")
console.log("let's try to reconnect...");
return (context);})
.then(serve));
}
function getTwitchOptions(context) {
let opts = {
options: {
clientId: context.creds.client_id,
debug: true
},
identity: {
username: context.config.tmi.username,
password: 'oauth:' + context.tokens.access_token
},
channels: context.config.tmi.channels
}
return (opts);
}
function registerTwitchEventHandlers(client, context) {
// Register our event handlers:
client.on('message', onMessageHandler);
client.on('disconnected', (reason) => {
// Login authentication failed
console.log(util.format('Disconnected: %s', reason));
reconnect(context)
.catch((err) => {
console.log(err);
});
});
}
function serve(context) {
// Create a client with our tmi options:
let opts = getTwitchOptions(context);
let client = new tmi.client(opts);
// Connect to Twitch:
return (client.connect()
.then((data) => {
console.log(JSON.stringify(data));
registerTwitchEventHandlers(client, context);
}, (err) => {
// Reconnect is almost always necessary because auth tokens need to be refreshed often.
console.log("It's okay, we probably just need to refresh the auth tokens.");
reconnect(context)
.catch((err) => {
console.log('Uh oh, reconnection attempt failed. Maybe this error message will be helpful:\n' + err);
});
}));
}
function launch(args) {
let config = getConfig(args);
let creds = getCreds(config.credsPath);
let accessToken = Promise.resolve();
if ('c' in args) {
let authCode = args['c'];
accessToken = accessToken
.then(function() {
return (accessTokenFromAuthCode(creds, authCode));
})
.then((response) => {
return (cacheTokenResponse(response));
});
}
else {
accessToken = accessToken
.then(function() {
return (accessTokenFromCache());
});
}
// Inject other context.
accessToken
.then((tokens) => {
let context = {
config: config,
creds: creds,
tokens: tokens
};
return (context);})
.then(serve)
.catch((err) => {
console.log(err);
process.exit(1);
});
}
//----------------------------------------------------------------
launch(parseArgs(process.argv));
//----------------------------------------------------------------
My config.json
looks like this:
{
"tmi": {
"channels": [
"laddspencer"
],
"username": "PhantsBot"
}
}
My creds.json
looks like this (no, those are not real creds, use your own!):
{
"client_id": "4jkcd8ejjwkemvnhuewnc98ku87uyh",
"client_secret": "d9rkkijun4jfunywhqssx6456hey7u"
}
FAQs
Authenticate with Twitch and stop caring about refreshing tokens.
The npm package twitch-auth receives a total of 260 weekly downloads. As such, twitch-auth popularity was classified as not popular.
We found that twitch-auth demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Security News
React's CRA deprecation announcement sparked community criticism over framework recommendations, leading to quick updates acknowledging build tools like Vite as valid alternatives.
Security News
Ransomware payment rates hit an all-time low in 2024 as law enforcement crackdowns, stronger defenses, and shifting policies make attacks riskier and less profitable.