Neah/app/api/auth/[...nextauth]/route.ts
2025-05-02 13:05:56 +02:00

142 lines
3.6 KiB
TypeScript

import NextAuth, { NextAuthOptions } from "next-auth";
import KeycloakProvider from "next-auth/providers/keycloak";
// Define Keycloak profile type
interface KeycloakProfile {
sub: string;
email?: string;
name?: string;
preferred_username?: string;
given_name?: string;
family_name?: string;
realm_access?: {
roles: string[];
};
}
// Define custom profile type
interface CustomProfile {
id: string;
name?: string | null;
email?: string | null;
username: string;
first_name: string;
last_name: string;
role: string[];
}
// Declare module augmentation for NextAuth types
declare module "next-auth" {
interface Session {
user: {
id: string;
name?: string | null;
email?: string | null;
image?: string | null;
username: string;
first_name: string;
last_name: string;
role: string[];
};
accessToken?: string;
}
interface JWT {
sub?: string;
accessToken?: string;
refreshToken?: string;
role?: string[];
username?: string;
first_name?: string;
last_name?: string;
}
}
// Simple, minimal implementation - NO REFRESH TOKEN LOGIC
export const authOptions: NextAuthOptions = {
providers: [
KeycloakProvider({
clientId: process.env.KEYCLOAK_CLIENT_ID || "",
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET || "",
issuer: process.env.KEYCLOAK_ISSUER || "",
profile(profile: any) {
// Extract roles from the profile
const roles = profile.realm_access?.roles || [];
return {
id: profile.sub,
name: profile.name || profile.preferred_username,
email: profile.email,
image: null,
role: roles.map((role: string) => role.replace(/^ROLE_/, '').toLowerCase()),
first_name: profile.given_name || '',
last_name: profile.family_name || '',
username: profile.preferred_username || profile.email?.split('@')[0] || '',
};
}
}),
],
session: {
strategy: "jwt",
maxAge: 8 * 60 * 60, // 8 hours
},
callbacks: {
async jwt({ token, account, profile }: any) {
if (account && profile) {
// Just store access token and critical fields
token.accessToken = account.access_token;
// Make sure roles are available
if (profile.role) {
token.role = profile.role;
token.username = profile.username || '';
token.first_name = profile.first_name || '';
token.last_name = profile.last_name || '';
}
}
return token;
},
async session({ session, token }: any) {
// Pass necessary info to the session
session.accessToken = token.accessToken;
if (session.user) {
session.user.id = token.sub || "";
// Ensure roles are passed to the session
if (token.role) {
session.user.role = token.role;
session.user.username = token.username || '';
session.user.first_name = token.first_name || '';
session.user.last_name = token.last_name || '';
} else {
// Fallback roles
session.user.role = ["user"];
session.user.username = '';
session.user.first_name = '';
session.user.last_name = '';
}
}
return session;
}
},
pages: {
signIn: '/signin',
error: '/signin',
},
cookies: {
sessionToken: {
name: 'next-auth.session-token',
options: {
httpOnly: true,
sameSite: 'none',
path: '/',
secure: true,
},
},
},
debug: false,
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };