Welcome to Sock Darning! Have a sock with a thinning heel or a toe hole you want to fix? Share your damage maps, yarn blend questions, and stitch experiments with other darning-minded visitors. Reading along is welcome too, so pull up a chair and join the conversation.

kick-auth.js

  • Thread starter Thread starter Austin Songer, CISSP
  • Start date Start date
A

Austin Songer, CISSP

Guest
kick-auth.js

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Show hidden characters

const express = require("express");
const crypto = require("crypto");
const router = express.Router();
//Put your scopes here
const KICK_SCOPES = [
"user:read",
"channel:read",
"channel:write",
"chat:write",
"events:subscribe",
"streamkey:read",
];
const ENDPOINT = {
authURL: "https://id.kick.com/oauth/authorize",
tokenURL: "https://id.kick.com/oauth/token",
};
//Put your redirect url here
const REDIRECT_URL = "http://localhost:3000/oauth/kick/callback";
//Client ID in env
const KICK_CLIENT_ID = process.env.KICK_CLIENT_ID;
//Client Secret in env
const KICK_CLIENT_SECRET = process.env.KICK_CLIENT_SECRET;
// PKCE Helper Functions
//This is just a helper function to generate a random string
function generateCodeVerifier() {
const buffer = crypto.randomBytes(32);
return buffer.toString("base64url");
}
//This is just a helper function to generate a code challenge(sha256 hash of the verifier)
function generateCodeChallenge(verifier) {
const hash = crypto.createHash("sha256").update(verifier).digest();
return hash.toString("base64url");
}
// Step 1: Generate a verifier, challenge and send the user to the auth page
router.get("/oauth/kick/", (req, res) => {
const codeVerifier = generateCodeVerifier();
const codeChallenge = generateCodeChallenge(codeVerifier);
/* IMPORTANT:
This is a VERY BAD way to store the verifier. The original non-hashed verifier is needed,
later on when swapping the code for a token.
In a real application you should either store the original verifier in your own
database, or encrypt it with a secret before including it in the state.
It's not inherently wrong to store it in the state param, but if you do,
make sure it is encrypted with a secret and not just base64 encoded.
The verifier system is used to "prove" that the request for authorization was
started by your application, and later that the code exchange was also by your application.
*/
const state = Buffer.from(JSON.stringify({ codeVerifier })).toString(
"base64",
);
//using url params to make sure that they are properly encoded
const authParams = new URLSearchParams({
client_id: KICK_CLIENT_ID,
redirect_uri: REDIRECT_URL,
response_type: "code",
scope: KICK_SCOPES.join(" "),
state,
code_challenge: codeChallenge,
code_challenge_method: "S256",
});
res.redirect(`${ENDPOINT.authURL}?${authParams.toString()}`);
});
// Step 2: Handle the redirect from the auth page
router.get("/oauth/kick/callback", async (req, res) => {
const { code, state } = req.query;
if (!code) {
return res.status(400).json({ error: "Missing authorization code" });
}
try {
//As stated above, just using base64 here for simplicity sake. Don't do this in production *please*
const { codeVerifier } = JSON.parse(
Buffer.from(state, "base64").toString(),
);
const tokenParams = new URLSearchParams({
grant_type: "authorization_code",
client_id: KICK_CLIENT_ID,
client_secret: KICK_CLIENT_SECRET,
code,
redirect_uri: REDIRECT_URL,
//This is the PKCE part. It's the ORIGINAL generated code before it was sha256 hashed.
//This is what proves to kick that you are the one that started the original auth request
code_verifier: codeVerifier,
});
const tokenResponse = await fetch(ENDPOINT.tokenURL, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: tokenParams,
});
const token = await tokenResponse.json();
res.json(token);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
 
Top