auth flow
This commit is contained in:
parent
d488979109
commit
e76e92b695
@ -60,15 +60,58 @@ export const authOptions: NextAuthOptions = {
|
||||
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 || [];
|
||||
// Debug the raw profile from Keycloak to understand its structure
|
||||
console.log('Raw Keycloak profile:', JSON.stringify(profile, null, 2));
|
||||
|
||||
// Extract roles from realm_access.roles and resource_access
|
||||
let roles: string[] = [];
|
||||
|
||||
// Get roles from realm_access
|
||||
if (profile.realm_access && Array.isArray(profile.realm_access.roles)) {
|
||||
roles = roles.concat(profile.realm_access.roles);
|
||||
}
|
||||
|
||||
// Get roles from resource_access for the client
|
||||
if (profile.resource_access) {
|
||||
const clientId = process.env.KEYCLOAK_CLIENT_ID;
|
||||
if (clientId && profile.resource_access[clientId] && Array.isArray(profile.resource_access[clientId].roles)) {
|
||||
roles = roles.concat(profile.resource_access[clientId].roles);
|
||||
}
|
||||
|
||||
// Also check resource_access roles under 'account'
|
||||
if (profile.resource_access.account && Array.isArray(profile.resource_access.account.roles)) {
|
||||
roles = roles.concat(profile.resource_access.account.roles);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract groups if available
|
||||
if (profile.groups && Array.isArray(profile.groups)) {
|
||||
// Remove any path prefixes (like "/") and add as roles
|
||||
const groupRoles = profile.groups.map((group: string) =>
|
||||
group.replace(/^\//, '').toLowerCase()
|
||||
);
|
||||
roles = roles.concat(groupRoles);
|
||||
}
|
||||
|
||||
// Clean up roles and convert to lowercase
|
||||
const cleanedRoles = roles
|
||||
.filter(Boolean) // Remove empty roles
|
||||
.map((role: string) =>
|
||||
role.replace(/^ROLE_/, '').toLowerCase()
|
||||
);
|
||||
|
||||
// Add some common application-specific role mappings
|
||||
const applicationRoles = mapToApplicationRoles(cleanedRoles);
|
||||
const allRoles = [...new Set([...cleanedRoles, ...applicationRoles, 'user'])];
|
||||
|
||||
console.log('Extracted roles:', allRoles);
|
||||
|
||||
return {
|
||||
id: profile.sub,
|
||||
name: profile.name || profile.preferred_username,
|
||||
email: profile.email,
|
||||
image: null,
|
||||
role: roles.map((role: string) => role.replace(/^ROLE_/, '').toLowerCase()),
|
||||
role: allRoles,
|
||||
first_name: profile.given_name || '',
|
||||
last_name: profile.family_name || '',
|
||||
username: profile.preferred_username || profile.email?.split('@')[0] || '',
|
||||
@ -87,23 +130,24 @@ export const authOptions: NextAuthOptions = {
|
||||
token.accessToken = account.access_token;
|
||||
token.refreshToken = account.refresh_token;
|
||||
|
||||
// Extract roles correctly from the raw Keycloak profile
|
||||
if (profile.realm_access && profile.realm_access.roles) {
|
||||
// Directly extract roles from the Keycloak profile structure
|
||||
token.role = profile.realm_access.roles.map(
|
||||
(role: string) => role.replace(/^ROLE_/, '').toLowerCase()
|
||||
);
|
||||
} else if (profile.role) {
|
||||
// Fallback to using the role property if already processed
|
||||
// Use the roles from the profile function
|
||||
if (profile.role && Array.isArray(profile.role)) {
|
||||
token.role = profile.role;
|
||||
console.log('JWT callback - roles from profile:', profile.role);
|
||||
} else {
|
||||
// Fallback for missing roles
|
||||
token.role = ['user'];
|
||||
console.log('JWT callback - no roles in profile, using fallback');
|
||||
}
|
||||
|
||||
// Store user information
|
||||
token.username = profile.preferred_username || profile.username || '';
|
||||
token.first_name = profile.given_name || profile.first_name || '';
|
||||
token.last_name = profile.family_name || profile.last_name || '';
|
||||
token.username = profile.username || '';
|
||||
token.first_name = profile.first_name || '';
|
||||
token.last_name = profile.last_name || '';
|
||||
}
|
||||
|
||||
// Log the token roles
|
||||
console.log('JWT token roles:', token.role);
|
||||
return token;
|
||||
},
|
||||
async session({ session, token }: any) {
|
||||
@ -118,15 +162,17 @@ export const authOptions: NextAuthOptions = {
|
||||
session.user.username = token.username || '';
|
||||
session.user.first_name = token.first_name || '';
|
||||
session.user.last_name = token.last_name || '';
|
||||
console.log('Session callback - using token roles:', token.role);
|
||||
} else {
|
||||
// Fallback roles
|
||||
session.user.role = ["user"];
|
||||
session.user.username = '';
|
||||
session.user.first_name = '';
|
||||
session.user.last_name = '';
|
||||
console.log('Session callback - no token roles, using fallback');
|
||||
}
|
||||
|
||||
// Add debug log to see what roles are being passed
|
||||
// Log the session user roles
|
||||
console.log('Session user roles:', session.user.role);
|
||||
}
|
||||
return session;
|
||||
@ -147,9 +193,64 @@ export const authOptions: NextAuthOptions = {
|
||||
},
|
||||
},
|
||||
},
|
||||
debug: true, // Enable debug logs temporarily to see role information
|
||||
debug: true, // Enable debug logs to help with troubleshooting
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps Keycloak roles to application-specific roles
|
||||
*/
|
||||
function mapToApplicationRoles(keycloakRoles: string[]): string[] {
|
||||
const mappings: Record<string, string[]> = {
|
||||
// Map Keycloak roles to your application's role names
|
||||
'admin': ['admin', 'dataintelligence', 'coding', 'expression', 'mediation'],
|
||||
'owner': ['admin', 'dataintelligence', 'coding', 'expression', 'mediation'],
|
||||
'cercle-admin': ['admin', 'dataintelligence', 'coding', 'expression', 'mediation'],
|
||||
'manager': ['dataintelligence', 'coding', 'expression', 'mediation'],
|
||||
'developer': ['coding', 'dataintelligence'],
|
||||
'data-scientist': ['dataintelligence'],
|
||||
'designer': ['expression'],
|
||||
'writer': ['expression'],
|
||||
'mediator': ['mediation'],
|
||||
// Default access roles from Keycloak
|
||||
'default-roles-cercle': ['user'],
|
||||
'uma_authorization': ['user'],
|
||||
'offline_access': ['user'],
|
||||
// Add more mappings as needed
|
||||
};
|
||||
|
||||
// Convert all keycloak roles to lowercase for case-insensitive matching
|
||||
const lowerKeycloakRoles = keycloakRoles.map(role => role.toLowerCase());
|
||||
|
||||
// Map roles based on the defined mappings
|
||||
let applicationRoles: string[] = [];
|
||||
|
||||
// Check all Keycloak roles for matches in our mapping
|
||||
for (const role of lowerKeycloakRoles) {
|
||||
if (mappings[role]) {
|
||||
applicationRoles = applicationRoles.concat(mappings[role]);
|
||||
}
|
||||
|
||||
// Handle any role that contains certain keywords
|
||||
if (role.includes('admin')) {
|
||||
applicationRoles.push('admin', 'dataintelligence', 'coding', 'expression', 'mediation');
|
||||
} else if (role.includes('developer') || role.includes('dev')) {
|
||||
applicationRoles.push('coding', 'dataintelligence');
|
||||
} else if (role.includes('design')) {
|
||||
applicationRoles.push('expression');
|
||||
} else if (role.includes('data')) {
|
||||
applicationRoles.push('dataintelligence');
|
||||
} else if (role.includes('mediat')) {
|
||||
applicationRoles.push('mediation');
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure user always has basic access
|
||||
applicationRoles.push('user');
|
||||
|
||||
// Return unique application roles
|
||||
return [...new Set(applicationRoles)];
|
||||
}
|
||||
|
||||
const handler = NextAuth(authOptions);
|
||||
export { handler as GET, handler as POST };
|
||||
|
||||
|
||||
@ -171,28 +171,28 @@ export function Sidebar({ isOpen, onClose }: SidebarProps) {
|
||||
icon: Palette,
|
||||
href: "/design",
|
||||
iframe: process.env.NEXT_PUBLIC_IFRAME_ARTLAB_URL,
|
||||
requiredRole: "Expression",
|
||||
requiredRole: "expression",
|
||||
},
|
||||
{
|
||||
title: "Gite",
|
||||
icon: GitFork,
|
||||
href: "/gite",
|
||||
iframe: process.env.NEXT_PUBLIC_IFRAME_GITE_URL,
|
||||
requiredRole: ["Coding", "DataIntelligence"],
|
||||
requiredRole: ["coding", "dataintelligence"],
|
||||
},
|
||||
{
|
||||
title: "Calcul",
|
||||
icon: Calculator,
|
||||
href: "/calcul",
|
||||
iframe: process.env.NEXT_PUBLIC_IFRAME_CALCULATION_URL,
|
||||
requiredRole: "DataIntelligence",
|
||||
requiredRole: "dataintelligence",
|
||||
},
|
||||
{
|
||||
title: "Médiation",
|
||||
icon: Building2,
|
||||
href: "/mediation",
|
||||
iframe: process.env.NEXT_PUBLIC_IFRAME_MEDIATIONS_URL,
|
||||
requiredRole: ["Mediation", "Expression"],
|
||||
requiredRole: ["mediation", "expression"],
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user