FEAT: Migrate authentication system from Directus to Keycloak, implementing token refresh and enhancing session management
This commit is contained in:
137
pages/login.vue
137
pages/login.vue
@@ -4,10 +4,23 @@
|
||||
<v-container class="fill-height" fluid>
|
||||
<v-row align="center" justify="center" class="fill-height">
|
||||
<v-col cols="12" class="d-flex flex-column align-center">
|
||||
<v-card class="pa-6" rounded max-width="450" elevation="2">
|
||||
<v-card class="pa-8" rounded max-width="450" elevation="3">
|
||||
<v-row no-gutters>
|
||||
<v-col cols="12">
|
||||
<v-img src="/Port_Nimara_Logo_2_Colour_New_Transparent.png" width="200" class="mb-3 mx-auto" />
|
||||
<v-col cols="12" class="text-center mb-6">
|
||||
<v-img src="/Port_Nimara_Logo_2_Colour_New_Transparent.png" width="200" class="mx-auto mb-4" />
|
||||
<h1 class="text-h5 font-weight-medium mb-2">Welcome to Port Nimara</h1>
|
||||
<p class="text-body-2 text-grey-darken-1">Client Portal Access</p>
|
||||
</v-col>
|
||||
|
||||
<!-- Error Alert -->
|
||||
<v-col cols="12" v-if="errorMessage" class="mb-4">
|
||||
<v-alert
|
||||
:text="errorMessage"
|
||||
color="error"
|
||||
variant="tonal"
|
||||
closable
|
||||
@click:close="errorMessage = ''"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Keycloak SSO Login -->
|
||||
@@ -19,80 +32,13 @@
|
||||
@click="loginWithKeycloak"
|
||||
prepend-icon="mdi-shield-account"
|
||||
:loading="keycloakLoading"
|
||||
:disabled="keycloakLoading"
|
||||
>
|
||||
Login with Single Sign-On
|
||||
{{ keycloakLoading ? 'Connecting...' : 'Login with Single Sign-On' }}
|
||||
</v-btn>
|
||||
</v-col>
|
||||
|
||||
<!-- Divider -->
|
||||
<v-col cols="12">
|
||||
<v-divider class="my-4">
|
||||
<span class="text-caption">OR</span>
|
||||
</v-divider>
|
||||
</v-col>
|
||||
|
||||
<!-- Directus Login Form -->
|
||||
<v-col cols="12">
|
||||
<v-form @submit.prevent="submit" v-model="valid">
|
||||
<v-row no-gutters>
|
||||
<v-scroll-y-transition>
|
||||
<v-col v-if="errorThrown" cols="12" class="my-3">
|
||||
<v-alert
|
||||
text="Invalid email address or password"
|
||||
color="error"
|
||||
variant="tonal"
|
||||
/>
|
||||
</v-col>
|
||||
</v-scroll-y-transition>
|
||||
<v-col cols="12">
|
||||
<v-row dense>
|
||||
<v-col cols="12" class="mt-4">
|
||||
<v-text-field
|
||||
v-model="emailAddress"
|
||||
placeholder="Email address"
|
||||
:disabled="loading"
|
||||
:rules="[
|
||||
(value) => !!value || 'Must not be empty',
|
||||
(value) =>
|
||||
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) ||
|
||||
'Invalid email address',
|
||||
]"
|
||||
variant="outlined"
|
||||
type="email"
|
||||
autofocus
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
@click:append-inner="passwordVisible = !passwordVisible"
|
||||
v-model="password"
|
||||
placeholder="Password"
|
||||
:disabled="loading"
|
||||
:type="passwordVisible ? 'text' : 'password'"
|
||||
:append-inner-icon="
|
||||
passwordVisible ? 'mdi-eye' : 'mdi-eye-off'
|
||||
"
|
||||
:rules="[(value) => !!value || 'Must not be empty']"
|
||||
autocomplete="current-password"
|
||||
variant="outlined"
|
||||
/>
|
||||
</v-col>
|
||||
<v-col cols="12">
|
||||
<v-btn
|
||||
text="Log in"
|
||||
:disabled="!valid"
|
||||
:loading="loading"
|
||||
type="submit"
|
||||
variant="tonal"
|
||||
color="primary"
|
||||
size="large"
|
||||
block
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-form>
|
||||
<p class="text-caption text-center text-grey-darken-1 mt-3">
|
||||
Secure authentication through Keycloak SSO
|
||||
</p>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card>
|
||||
@@ -112,57 +58,40 @@ definePageMeta({
|
||||
auth: false
|
||||
});
|
||||
|
||||
// Directus auth
|
||||
const { login: directusLogin } = useDirectusAuth();
|
||||
|
||||
// Custom Keycloak auth
|
||||
const { login: keycloakLogin } = useCustomAuth();
|
||||
|
||||
const loading = ref(false);
|
||||
const keycloakLoading = ref(false);
|
||||
const errorThrown = ref(false);
|
||||
const errorMessage = ref('');
|
||||
|
||||
const emailAddress = ref();
|
||||
const password = ref();
|
||||
const passwordVisible = ref(false);
|
||||
|
||||
const valid = ref(false);
|
||||
// Check for error in query params
|
||||
const route = useRoute();
|
||||
onMounted(() => {
|
||||
if (route.query.error === 'auth_failed') {
|
||||
errorMessage.value = 'Authentication failed. Please try again.';
|
||||
}
|
||||
});
|
||||
|
||||
// SSO login function using custom Keycloak auth
|
||||
const loginWithKeycloak = async () => {
|
||||
try {
|
||||
errorMessage.value = ''; // Clear any previous errors
|
||||
keycloakLoading.value = true;
|
||||
console.log('[LOGIN] Starting SSO authentication via custom Keycloak auth...');
|
||||
console.log('[LOGIN] Starting SSO authentication via Keycloak...');
|
||||
|
||||
// Use our custom Keycloak authentication
|
||||
keycloakLogin();
|
||||
|
||||
} catch (error) {
|
||||
console.error('[LOGIN] SSO login error:', error);
|
||||
|
||||
const toast = useToast();
|
||||
toast.error('SSO login failed. Please try again or use email/password login.');
|
||||
|
||||
errorMessage.value = 'SSO login failed. Please try again.';
|
||||
} finally {
|
||||
keycloakLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Directus login function
|
||||
const submit = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await directusLogin({ email: emailAddress.value, password: password.value });
|
||||
return navigateTo("/dashboard");
|
||||
} catch (error) {
|
||||
errorThrown.value = true;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
useHead({
|
||||
title: "Login",
|
||||
title: "Port Nimara - Login",
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user