Files
Mace-AIO-Discord-Bot---With…/dashboard/lib/auth.ts

121 lines
4.0 KiB
TypeScript

/**
* ╔══════════════════════════════════════════════════════════════════╗
* ║ ║
* ║ +-+-+-+-+-+-+-+-+ ║
* ║ |H|e|x|a|H|o|s|t| ║
* ║ +-+-+-+-+-+-+-+-+ ║
* ║ ║
* ║ © 2026 HexaHost — All Rights Reserved ║
* ║ ║
* ║ discord ── https://discord.gg/hexahost ║
* ║ github ── https://github.com/theoneandonlymace ║
* ║ ║
* ╚══════════════════════════════════════════════════════════════════╝
*/
import DiscordProvider from "next-auth/providers/discord";
import { AuthOptions } from "next-auth";
async function refreshDiscordAccessToken(token: {
accessToken?: string;
refreshToken?: string;
expiresAt?: number;
error?: string;
[key: string]: unknown;
}) {
if (!token.refreshToken) {
return { ...token, error: "RefreshTokenMissing" as const };
}
try {
const body = new URLSearchParams({
client_id: process.env.DISCORD_CLIENT_ID || "",
client_secret: process.env.DISCORD_CLIENT_SECRET || "",
grant_type: "refresh_token",
refresh_token: String(token.refreshToken),
});
const res = await fetch("https://discord.com/api/oauth2/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body,
});
const data = await res.json();
if (!res.ok) {
throw data;
}
return {
...token,
accessToken: data.access_token as string,
refreshToken: (data.refresh_token as string) || token.refreshToken,
expiresAt: Math.floor(Date.now() / 1000) + Number(data.expires_in || 604800),
error: undefined,
};
} catch (error) {
console.error("Discord token refresh failed:", error);
return { ...token, error: "RefreshAccessTokenError" as const };
}
}
export const authOptions: AuthOptions = {
providers: [
DiscordProvider({
clientId: process.env.DISCORD_CLIENT_ID || "",
clientSecret: process.env.DISCORD_CLIENT_SECRET || "",
authorization: { params: { scope: "identify guilds" } },
}),
],
callbacks: {
async jwt({ token, account }) {
// Initial sign-in
if (account) {
return {
...token,
accessToken: account.access_token,
refreshToken: account.refresh_token,
expiresAt: account.expires_at,
};
}
const expiresAt = typeof token.expiresAt === "number" ? token.expiresAt : 0;
// Refresh when expired, missing expiry, or previous refresh failed
const stillValid =
Boolean(token.accessToken) &&
expiresAt > 0 &&
Date.now() < expiresAt * 1000 - 60_000 &&
!token.error;
if (stillValid) {
return token;
}
return refreshDiscordAccessToken(token as {
accessToken?: string;
refreshToken?: string;
expiresAt?: number;
error?: string;
});
},
async session({ session, token }) {
if (session.user) {
// @ts-ignore
session.user.id = token.sub;
// @ts-ignore
session.accessToken = token.accessToken;
// @ts-ignore
session.error = token.error;
}
// Surface refresh failures so pages can redirect to sign-in
if (token.error) {
// @ts-ignore
session.error = token.error;
}
return session;
},
},
pages: {
signIn: "/",
},
};