Lumi/src/services/auth.js
2026-05-30 20:37:42 +02:00

189 lines
5.7 KiB
JavaScript

const { getSetting } = require("./settings");
const YOUTUBE_SCOPES = [
"https://www.googleapis.com/auth/youtube",
"https://www.googleapis.com/auth/youtube.force-ssl",
"https://www.googleapis.com/auth/youtube.readonly",
"https://www.googleapis.com/auth/youtube.channel-memberships.creator"
];
function getDiscordRedirectUri(override) {
return override || getSetting("discord_redirect_uri", null);
}
function buildDiscordAuthUrl(state, redirectOverride) {
const clientId = getSetting("discord_client_id");
const redirectUri = getDiscordRedirectUri(redirectOverride);
const params = new URLSearchParams({
client_id: clientId || "",
redirect_uri: redirectUri || "",
response_type: "code",
scope: "identify guilds guilds.members.read",
state
});
return `https://discord.com/api/oauth2/authorize?${params.toString()}`;
}
async function exchangeDiscordCode(code, redirectOverride) {
const clientId = getSetting("discord_client_id");
const clientSecret = getSetting("discord_client_secret");
const redirectUri = getDiscordRedirectUri(redirectOverride);
const body = new URLSearchParams({
client_id: clientId || "",
client_secret: clientSecret || "",
grant_type: "authorization_code",
code,
redirect_uri: redirectUri || ""
});
const response = await fetch("https://discord.com/api/oauth2/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body
});
if (!response.ok) {
throw new Error(`Discord token exchange failed: ${response.status}`);
}
return response.json();
}
async function fetchDiscordUser(accessToken) {
const response = await fetch("https://discord.com/api/users/@me", {
headers: { Authorization: `Bearer ${accessToken}` }
});
if (!response.ok) {
throw new Error(`Discord user fetch failed: ${response.status}`);
}
return response.json();
}
async function fetchDiscordGuildMember(accessToken, guildId) {
const response = await fetch(
`https://discord.com/api/users/@me/guilds/${guildId}/member`,
{
headers: { Authorization: `Bearer ${accessToken}` }
}
);
if (!response.ok) {
return null;
}
return response.json();
}
function buildTwitchAuthUrl(state, redirectOverride) {
const clientId = getSetting("twitch_client_id");
const redirectUri = redirectOverride || getSetting("twitch_redirect_uri");
const params = new URLSearchParams({
client_id: clientId || "",
redirect_uri: redirectUri || "",
response_type: "code",
scope: "user:read:email",
state
});
return `https://id.twitch.tv/oauth2/authorize?${params.toString()}`;
}
async function exchangeTwitchCode(code, redirectOverride) {
const clientId = getSetting("twitch_client_id");
const clientSecret = getSetting("twitch_client_secret");
const redirectUri = redirectOverride || getSetting("twitch_redirect_uri");
const params = new URLSearchParams({
client_id: clientId || "",
client_secret: clientSecret || "",
code,
grant_type: "authorization_code",
redirect_uri: redirectUri || ""
});
const response = await fetch(
`https://id.twitch.tv/oauth2/token?${params.toString()}`,
{ method: "POST" }
);
if (!response.ok) {
throw new Error(`Twitch token exchange failed: ${response.status}`);
}
return response.json();
}
async function fetchTwitchUser(accessToken) {
const clientId = getSetting("twitch_client_id");
const response = await fetch("https://api.twitch.tv/helix/users", {
headers: {
"Client-Id": clientId || "",
Authorization: `Bearer ${accessToken}`
}
});
if (!response.ok) {
throw new Error(`Twitch user fetch failed: ${response.status}`);
}
const data = await response.json();
return data.data?.[0] || null;
}
function buildYouTubeAuthUrl(state, redirectOverride, options = {}) {
const clientId = getSetting("youtube_client_id");
const redirectUri = redirectOverride || getSetting("youtube_redirect_uri");
const scopes = options.scopes || YOUTUBE_SCOPES;
const params = new URLSearchParams({
client_id: clientId || "",
redirect_uri: redirectUri || "",
response_type: "code",
scope: scopes.join(" "),
state,
access_type: "offline",
include_granted_scopes: "true"
});
if (options.prompt) {
params.set("prompt", options.prompt);
}
return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
}
async function exchangeYouTubeCode(code, redirectOverride) {
const clientId = getSetting("youtube_client_id");
const clientSecret = getSetting("youtube_client_secret");
const redirectUri = redirectOverride || getSetting("youtube_redirect_uri");
const body = new URLSearchParams({
client_id: clientId || "",
client_secret: clientSecret || "",
grant_type: "authorization_code",
code,
redirect_uri: redirectUri || ""
});
const response = await fetch("https://oauth2.googleapis.com/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body
});
if (!response.ok) {
throw new Error(`YouTube token exchange failed: ${response.status}`);
}
return response.json();
}
async function fetchYouTubeChannel(accessToken) {
const response = await fetch(
"https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true",
{ headers: { Authorization: `Bearer ${accessToken}` } }
);
if (!response.ok) {
throw new Error(`YouTube channel fetch failed: ${response.status}`);
}
const data = await response.json();
return data.items?.[0] || null;
}
module.exports = {
buildDiscordAuthUrl,
exchangeDiscordCode,
fetchDiscordUser,
fetchDiscordGuildMember,
buildTwitchAuthUrl,
exchangeTwitchCode,
fetchTwitchUser,
buildYouTubeAuthUrl,
exchangeYouTubeCode,
fetchYouTubeChannel
};