Skip to content

Instantly share code, notes, and snippets.

@Christopher-Hayes
Created July 21, 2024 12:12
Show Gist options
  • Save Christopher-Hayes/9034b894d796dd922f55a0b4d4f065e6 to your computer and use it in GitHub Desktop.
Save Christopher-Hayes/9034b894d796dd922f55a0b4d4f065e6 to your computer and use it in GitHub Desktop.
OpenRouter PKCE Node.JS example
const crypto = require("crypto");
const https = require("https");
const http = require("http");
const url = require("url");
// S256 is more secure, but if you have trouble making it work, use "plain" instead.
let METHOD = "S256";
// let METHOD = "plain";
// Generates a PKCE code verifier
const generateVerifier = (length) =>
crypto.randomBytes(length).toString("base64url").slice(0, length);
// Generates a PKCE code challenge from a verifier
const generateChallenge = (verifier) => {
if (METHOD === "plain") return verifier;
const hash = crypto.createHash("sha256").update(verifier).digest("base64url");
return hash;
};
// Exchanges the authorization code for an API key
const exchangeCodeForKey = async (code, code_verifier) => {
const data = JSON.stringify({
code,
code_verifier,
code_challenge_method: METHOD,
});
const options = {
hostname: "openrouter.ai",
port: 443,
path: "/api/v1/auth/keys",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(data),
},
};
console.log(`Code Verifier Length: ${Buffer.byteLength(code_verifier)}`);
console.log(`Code Challenge Length: ${Buffer.byteLength(code_verifier)}`);
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let responseData = "";
res.on("data", (chunk) => responseData += chunk);
res.on("end", () => {
if (res.statusCode === 200) {
return resolve(responseData);
} else {
console.error(`โŒ Error: ${responseData}`);
return reject(new Error(`Server responded with status code ${res.statusCode}`));
}
});
});
req.on("error", reject);
req.write(data);
req.end();
});
};
// Starts the PKCE authorization process
const startPKCEProcess = async () => {
const code_verifier = generateVerifier(43);
console.log("๐Ÿ” Code Verifier:", code_verifier);
const code_challenge = generateChallenge(code_verifier);
console.log("๐Ÿ“š Code Challenge:", code_challenge);
const server = http.createServer(async (req, res) => {
const parsedUrl = url.parse(req.url, true);
if (parsedUrl.query.code) {
const code = parsedUrl.query.code;
console.log("๐Ÿ“ฅ Received code:", code);
try {
await exchangeCodeForKey(code, code_verifier);
res.end("Authorization successful. Check the console for details.");
console.log("โœ… Authorization successful. You can now use your API key.");
} catch (error) {
console.error(`โŒ Error: ${error.message}`);
res.end("Authorization failed. Check the console for details.");
} finally {
server.close();
}
}
});
server.listen(7878, "localhost", () => {
console.log("\n๐ŸŒ Server running at http://localhost:7878");
const authUrl = `https://openrouter.ai/auth?code_challenge_method=${METHOD}&code_challenge=${encodeURIComponent(code_challenge)}&callback_url=${encodeURIComponent("http://localhost:7878")}`;
console.log("๐Ÿ—‚๏ธ Please open the following URL in your browser:");
console.log(authUrl);
});
};
startPKCEProcess();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment