105 lines
2.8 KiB
TypeScript
105 lines
2.8 KiB
TypeScript
import { NextAuthOptions, User as NextAuthUser, Account, Profile, JWT as NextAuthJWT } from 'next-auth';
|
|
import KeycloakProvider from 'next-auth/providers/keycloak';
|
|
|
|
interface User extends NextAuthUser {
|
|
id: string;
|
|
email: string;
|
|
name: string;
|
|
role: string[];
|
|
first_name: string;
|
|
last_name: string;
|
|
username: string;
|
|
}
|
|
|
|
interface Session {
|
|
user: User;
|
|
accessToken: string;
|
|
}
|
|
|
|
interface JWT extends Omit<NextAuthJWT, 'accessToken' | 'refreshToken' | 'accessTokenExpires'> {
|
|
sub: string;
|
|
email: string | null;
|
|
name: string | null;
|
|
role: string[];
|
|
username: string;
|
|
first_name: string;
|
|
last_name: string;
|
|
accessToken: string;
|
|
refreshToken: string;
|
|
accessTokenExpires: number;
|
|
error?: string;
|
|
}
|
|
|
|
interface KeycloakProfile extends Profile {
|
|
sub: string;
|
|
email?: string;
|
|
name?: string;
|
|
roles?: string[];
|
|
preferred_username?: string;
|
|
given_name?: string;
|
|
family_name?: string;
|
|
}
|
|
|
|
export const authOptions: NextAuthOptions = {
|
|
providers: [
|
|
KeycloakProvider({
|
|
clientId: process.env.KEYCLOAK_CLIENT_ID!,
|
|
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!,
|
|
issuer: process.env.KEYCLOAK_ISSUER!,
|
|
authorization: {
|
|
params: {
|
|
scope: 'openid email profile',
|
|
},
|
|
},
|
|
}),
|
|
],
|
|
session: {
|
|
strategy: 'jwt',
|
|
maxAge: 30 * 24 * 60 * 60, // 30 days
|
|
},
|
|
callbacks: {
|
|
async jwt({ token, account, profile }) {
|
|
if (account && profile) {
|
|
const keycloakProfile = profile as KeycloakProfile;
|
|
token.accessToken = account.access_token ?? '';
|
|
token.refreshToken = account.refresh_token ?? '';
|
|
token.accessTokenExpires = account.expires_at ?? 0;
|
|
token.sub = keycloakProfile.sub;
|
|
token.role = keycloakProfile.roles ?? [];
|
|
token.username = keycloakProfile.preferred_username ?? '';
|
|
token.first_name = keycloakProfile.given_name ?? '';
|
|
token.last_name = keycloakProfile.family_name ?? '';
|
|
}
|
|
return token;
|
|
},
|
|
async session({ session, token }) {
|
|
console.log('Session callback - token:', token);
|
|
console.log('Session callback - session before:', session);
|
|
|
|
if (token.error) {
|
|
throw new Error(token.error);
|
|
}
|
|
|
|
// Only update session if token has changed
|
|
if (session.user?.id !== token.sub) {
|
|
session.user = {
|
|
id: token.sub,
|
|
email: token.email ?? '',
|
|
name: token.name ?? '',
|
|
role: token.role,
|
|
first_name: token.first_name,
|
|
last_name: token.last_name,
|
|
username: token.username,
|
|
};
|
|
}
|
|
|
|
session.accessToken = token.accessToken;
|
|
console.log('Session callback - session after:', session);
|
|
return session;
|
|
},
|
|
},
|
|
pages: {
|
|
signIn: '/signin',
|
|
error: '/signin',
|
|
},
|
|
};
|