Refactor authOptions into a separate module to improve structure and reusability.
This commit is contained in:
@@ -1,92 +1,5 @@
|
|||||||
import NextAuth from "next-auth";
|
import NextAuth from "next-auth";
|
||||||
import KeycloakProvider from "next-auth/providers/keycloak";
|
import {authOptions} from "@/lib/auth/authOptions";
|
||||||
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,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log("[auth] NextAuth handler initialized");
|
|
||||||
|
|
||||||
const handler = NextAuth(authOptions);
|
const handler = NextAuth(authOptions);
|
||||||
export {handler as GET, handler as POST};
|
export {handler as GET, handler as POST};
|
||||||
|
|||||||
86
internal_frontend/lib/auth/authOptions.ts
Normal file
86
internal_frontend/lib/auth/authOptions.ts
Normal 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,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user