Refactor authOptions into a separate module to improve structure and reusability.

This commit is contained in:
2025-07-02 10:14:21 +09:00
parent b1d7eb906f
commit ae425e4e28
2 changed files with 87 additions and 88 deletions

View File

@@ -0,0 +1,86 @@
import KeycloakProvider from "next-auth/providers/keycloak";
import type {NextAuthOptions} from "next-auth";
interface TypedJWT {
access_token?: string;
refresh_token?: string;
[key: string]: unknown;
}
const {
KEYCLOAK_CLIENT_ID,
KEYCLOAK_CLIENT_SECRET,
KEYCLOAK_ISSUER,
NEXTAUTH_SECRET,
} = process.env;
if (!KEYCLOAK_CLIENT_ID) throw new Error("Missing KEYCLOAK_CLIENT_ID");
if (!KEYCLOAK_CLIENT_SECRET) throw new Error("Missing KEYCLOAK_CLIENT_SECRET");
if (!KEYCLOAK_ISSUER) throw new Error("Missing KEYCLOAK_ISSUER");
if (!NEXTAUTH_SECRET) throw new Error("Missing NEXTAUTH_SECRET");
console.log("[auth] Using Keycloak provider:");
console.log(" - Client ID:", KEYCLOAK_CLIENT_ID);
console.log(" - Issuer:", KEYCLOAK_ISSUER);
async function isTokenValid(token: string): Promise<boolean> {
try {
const res = await fetch(`${KEYCLOAK_ISSUER}/protocol/openid-connect/userinfo`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
return res.ok;
} catch (error) {
console.error("[auth] Failed to validate access token:", error);
return false;
}
}
export const authOptions: NextAuthOptions = {
providers: [
KeycloakProvider({
clientId: KEYCLOAK_CLIENT_ID,
clientSecret: KEYCLOAK_CLIENT_SECRET,
issuer: KEYCLOAK_ISSUER,
}),
],
secret: NEXTAUTH_SECRET,
session: {
strategy: "jwt",
},
callbacks: {
async jwt({token, account}) {
if (account) {
token.access_token = account.access_token;
token.refresh_token = account.refresh_token;
console.log("[auth] JWT callback: new login from Keycloak");
return token;
}
const {access_token} = token as TypedJWT;
if (access_token) {
const valid = await isTokenValid(access_token);
if (!valid) {
console.warn("[auth] Access token invalid — clearing session");
return {};
}
}
console.log("[auth] JWT callback: reusing existing token");
return token;
},
async session({session, token}) {
const {access_token, refresh_token} = token as TypedJWT;
console.log("[auth] Session callback: enriching session with tokens");
return {
...session,
accessToken: access_token,
refreshToken: refresh_token,
};
},
},
};