Implement Official Keycloak JS Adapter with Proxy-Aware Configuration

MAJOR ENHANCEMENT: Complete Keycloak integration with proper HTTPS/proxy handling

## Core Improvements:

### 1. Enhanced Configuration (nuxt.config.ts)
- Added proxy trust configuration for nginx environments
- Configured baseUrl for production HTTPS enforcement
- Added debug mode configuration for development

### 2. Proxy-Aware Keycloak Composable (composables/useKeycloak.ts)
- Intelligent base URL detection (production vs development)
- Force HTTPS redirect URIs in production environments
- Enhanced debugging and logging capabilities
- Proper PKCE implementation for security
- Automatic token refresh mechanism

### 3. Dual Authentication System
- Updated middleware to support both Directus and Keycloak
- Enhanced useUnifiedAuth for seamless auth source switching
- Maintains backward compatibility with existing Directus users

### 4. OAuth Flow Implementation
- Created proper callback handler (pages/auth/callback.vue)
- Comprehensive error handling and user feedback
- Automatic redirect to dashboard on success

### 5. Enhanced Login Experience (pages/login.vue)
- Restored SSO login button with proper error handling
- Maintained existing Directus login form
- Clear separation between auth methods with visual divider

### 6. Comprehensive Testing Suite (pages/dashboard/keycloak-test.vue)
- Real-time configuration display
- Authentication status monitoring
- Interactive testing tools
- Detailed debug logging system

## Technical Solutions:

 **Proxy Detection**: Automatically detects nginx proxy and uses correct HTTPS URLs
 **HTTPS Enforcement**: Forces secure redirect URIs in production
 **Error Handling**: Comprehensive error catching with user-friendly messages
 **Debug Capabilities**: Enhanced logging for troubleshooting
 **Security**: Implements PKCE and secure token handling

## Infrastructure Compatibility:
- Works with nginx reverse proxy setups
- Compatible with Docker container networking
- Handles SSL termination at proxy level
- Supports both development and production environments

This implementation specifically addresses the HTTP/HTTPS redirect URI mismatch
that was causing 'unauthorized_client' errors in the proxy environment.
This commit is contained in:
2025-06-14 15:26:26 +02:00
parent fa35fcd235
commit 0c9cd89667
8 changed files with 819 additions and 11 deletions

79
pages/auth/callback.vue Normal file
View File

@@ -0,0 +1,79 @@
<template>
<v-app>
<v-main>
<v-container class="fill-height" fluid>
<v-row align="center" justify="center" class="fill-height">
<v-col cols="12" class="text-center">
<v-progress-circular
:size="70"
:width="7"
color="primary"
indeterminate
></v-progress-circular>
<h3 class="mt-4">Processing authentication...</h3>
<p class="text-body-2 mt-2">Please wait while we complete your login</p>
</v-col>
</v-row>
</v-container>
</v-main>
</v-app>
</template>
<script setup lang="ts">
// Define page meta for public access (no auth required)
definePageMeta({
auth: false,
layout: false
});
// Handle OAuth callback
onMounted(async () => {
try {
const route = useRoute()
const keycloak = useKeycloak()
// Check if we have an authorization code
const code = route.query.code as string
const state = route.query.state as string
const error = route.query.error as string
if (error) {
console.error('OAuth error:', error, route.query.error_description)
throw new Error(`Authentication failed: ${error}`)
}
if (!code) {
throw new Error('No authorization code received')
}
console.log('[CALLBACK] Processing OAuth callback with code:', code.substring(0, 10) + '...')
// Initialize Keycloak if not already done
if (!keycloak.isInitialized.value) {
await keycloak.initKeycloak()
}
// Check if user is now authenticated
if (keycloak.isAuthenticated.value) {
console.log('[CALLBACK] User authenticated successfully')
// Redirect to dashboard
await navigateTo('/dashboard')
} else {
throw new Error('Authentication process failed')
}
} catch (error) {
console.error('[CALLBACK] Authentication error:', error)
// Show error and redirect to login
const toast = useToast()
toast.error('Authentication failed. Unable to complete login. Please try again.')
await navigateTo('/login')
}
})
useHead({
title: 'Authenticating...'
})
</script>