MAJOR: Replace nuxt-openid-connect with official Keycloak JS adapter

- Remove problematic nuxt-openid-connect module that was causing OAuth issues
- Install and implement official keycloak-js adapter for better reliability
- Create new useKeycloak composable with proper token management
- Update useUnifiedAuth to work with new Keycloak implementation
- Fix authentication middleware to support both auth methods
- Update login page to use new Keycloak login function
- Clean up configuration and remove deprecated OIDC settings
- This should resolve all the HTTP/HTTPS redirect and token exchange issues
This commit is contained in:
2025-06-14 14:50:29 +02:00
parent bd8f1d9926
commit a797c13867
7 changed files with 159 additions and 128 deletions

129
composables/useKeycloak.ts Normal file
View File

@@ -0,0 +1,129 @@
import type Keycloak from 'keycloak-js'
export const useKeycloak = () => {
const config = useRuntimeConfig()
const keycloak = ref<Keycloak | null>(null)
const isAuthenticated = ref(false)
const user = ref<any>(null)
const token = ref<string | null>(null)
const isInitialized = ref(false)
const initKeycloak = async () => {
if (process.server) return
try {
// Dynamically import keycloak-js
const KeycloakModule = await import('keycloak-js')
const KeycloakConstructor = KeycloakModule.default
keycloak.value = new KeycloakConstructor({
url: config.public.keycloak.url,
realm: config.public.keycloak.realm,
clientId: config.public.keycloak.clientId,
})
const authenticated = await keycloak.value.init({
onLoad: 'check-sso',
silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html',
checkLoginIframe: false, // Disable iframe checks for better compatibility
})
isAuthenticated.value = authenticated
isInitialized.value = true
if (authenticated && keycloak.value.token) {
token.value = keycloak.value.token || null
user.value = {
id: keycloak.value.subject,
username: keycloak.value.tokenParsed?.preferred_username,
email: keycloak.value.tokenParsed?.email,
firstName: keycloak.value.tokenParsed?.given_name,
lastName: keycloak.value.tokenParsed?.family_name,
fullName: keycloak.value.tokenParsed?.name,
roles: keycloak.value.tokenParsed?.realm_access?.roles || [],
}
// Set up token refresh
keycloak.value.onTokenExpired = () => {
keycloak.value?.updateToken(30).then((refreshed) => {
if (refreshed) {
token.value = keycloak.value?.token || null
console.log('Token refreshed')
} else {
console.log('Token still valid')
}
}).catch(() => {
console.log('Failed to refresh token')
logout()
})
}
}
return authenticated
} catch (error) {
console.error('Failed to initialize Keycloak:', error)
isInitialized.value = true
return false
}
}
const login = async () => {
if (!keycloak.value) {
await initKeycloak()
}
if (keycloak.value) {
try {
await keycloak.value.login({
redirectUri: window.location.origin + '/dashboard'
})
} catch (error) {
console.error('Login failed:', error)
throw error
}
}
}
const logout = async () => {
if (keycloak.value) {
try {
await keycloak.value.logout({
redirectUri: window.location.origin
})
} catch (error) {
console.error('Logout failed:', error)
}
}
// Clear local state
isAuthenticated.value = false
user.value = null
token.value = null
}
const getToken = () => {
return token.value
}
const hasRole = (role: string) => {
return user.value?.roles?.includes(role) || false
}
const hasAnyRole = (roles: string[]) => {
return roles.some(role => hasRole(role))
}
return {
keycloak: readonly(keycloak),
isAuthenticated: readonly(isAuthenticated),
user: readonly(user),
token: readonly(token),
isInitialized: readonly(isInitialized),
initKeycloak,
login,
logout,
getToken,
hasRole,
hasAnyRole,
}
}

View File

@@ -11,13 +11,13 @@ export const useUnifiedAuth = () => {
// Get both auth systems
const directusAuth = useDirectusAuth();
const directusUser = useDirectusUser();
const oidc = useOidc();
const keycloak = useKeycloak();
// Create unified user object
const user = computed<UnifiedUser | null>(() => {
// Check Keycloak user first
if (oidc.user?.value) {
const keycloakUser = oidc.user.value;
if (keycloak.user.value) {
const keycloakUser = keycloak.user.value;
// Construct name from firstName and lastName if available
let name = keycloakUser.name;
if (!name && (keycloakUser.given_name || keycloakUser.family_name)) {
@@ -58,7 +58,7 @@ export const useUnifiedAuth = () => {
const logout = async () => {
if (user.value?.authSource === 'keycloak') {
// Keycloak logout
await oidc.logout();
await keycloak.logout();
} else if (user.value?.authSource === 'directus') {
// Directus logout
await directusAuth.logout();