Вопрос или проблема
Я пытаюсь аутентифицироваться, используя Keycloak JS в проекте nuxt, поэтому я определил плагин следующим образом:
// plugins/keycloakjs.client.ts
import Keycloak, { KeycloakConfig } from "keycloak-js";
export default defineNuxtPlugin(_nuxtApp => {
const runtimeConfig = useRuntimeConfig();
try {
const keycloakConfig : KeycloakConfig = {
url: runtimeConfig.public.keycloakUrl,
realm: runtimeConfig.public.keycloakRealm,
clientId: runtimeConfig.public.keycloakClientId,
}
const keycloak = new Keycloak(keycloakConfig);
keycloak.init({
onLoad: "check-sso"
});
return {
provide: {
keycloak,
},
};
} catch (e) {
console.error(e)
throw createError({ statusCode: 401, message: "Ошибка Keycloak" });
}
});
Я пытаюсь использовать его в компоненте навигационной панели, который используется непосредственно в файле app.vue следующим образом:
// ~/app.vue
<script setup lang="ts">
import AppNavbar from "~/components/AppNavbar.vue";
</script>
<template>
<div class="h-screen">
<app-navbar />
<NuxtPage />
</div>
</template>
И, наконец, компонент:
// components/AppNavbar.vue
<script setup lang="ts">
import Keycloak from "keycloak-js";
import { onMounted } from "vue";
const {$keycloak} = useNuxtApp()
const state = reactive({
authenticated: false,
token: null
})
onMounted( () => {
console.log('Keycloak', $keycloak)
console.log('Keycloak аутентифицирован', $keycloak.authenticated)
if ($keycloak.authenticated) {
state.authenticated = true
state.token = $keycloak?.token
}
console.log('состояние', state)
})
</script>
<template>
<div class="bg-yellow p-4 flex items-center gap-3">
<button class="bg-purple p-2 rounded" @click="$keycloak.logout" v-if="state.authenticated">Выйти</button>
<button class="bg-purple p-2 rounded" @click="$keycloak.login" v-else>Войти</button>
<div class="bg-purple p-2 rounded w-1/2">{{ state.token }}</div>
</div>
</template>
Но когда я загружаю приложение, локальное состояние аутентификации остается ложным. При логировании значения плагина я получаю значение аутентификации истинным, но при вызове свойства оно всегда возвращает ложное. Что я пропускаю, пожалуйста?
Я забыл разобраться с промисом init. Вот исправленная версия:
// plugins/keycloakjs.client.ts
import Keycloak, { KeycloakConfig } from "keycloak-js";
export default defineNuxtPlugin(async _nuxtApp => {
const runtimeConfig = useRuntimeConfig();
try {
const keycloakConfig : KeycloakConfig = {
url: runtimeConfig.public.keycloakUrl,
realm: runtimeConfig.public.keycloakRealm,
clientId: runtimeConfig.public.keycloakClientId,
}
const keycloak = new Keycloak(keycloakConfig);
await keycloak.init({
onLoad: "check-sso"
});
return {
provide: {
keycloak,
},
};
} catch (e) {
console.error(e)
throw createError({ statusCode: 401, message: "Ошибка Keycloak" });
}
});
Ответ или решение
При работе с аутентификацией в Nuxt проекте с использованием Keycloak JS, вы столкнулись с проблемой, что состояние аутентификации остается неверным (false), несмотря на то, что в логе при проверке значения оно отображается как true. Это может быть связано с тем, как вы обрабатываете инициализацию Keycloak. Давайте разберем ваш код и определим, что именно не так.
Проблема: Необработанная Промис Инициализация
В первоначальной версии вашего кода вы не обрабатываете Promise, возвращаемый методом keycloak.init()
. Это ключевой момент, так как инициализация Keycloak является асинхронной и требует времени для завершения, чтобы корректно установить состояние аутентификации.
Исправление: Асинхронная Инициализация
Чтобы решить вашу проблему, вам необходимо убедиться, что вы ждете завершения инициализации Keycloak перед тем, как вызывать состояния аутентификации и другие свойства. Ниже представлено исправленное решение:
// plugins/keycloakjs.client.ts
import Keycloak, { KeycloakConfig } from "keycloak-js";
export default defineNuxtPlugin(async (_nuxtApp) => {
const runtimeConfig = useRuntimeConfig();
try {
const keycloakConfig: KeycloakConfig = {
url: runtimeConfig.public.keycloakUrl,
realm: runtimeConfig.public.keycloakRealm,
clientId: runtimeConfig.public.keycloakClientId,
};
const keycloak = new Keycloak(keycloakConfig);
await keycloak.init({
onLoad: "check-sso"
});
return {
provide: {
keycloak,
},
};
} catch (e) {
console.error(e);
throw createError({ statusCode: 401, message: "Ошибка Keycloak" });
}
});
Использование В Компоненте
В вашем компоненте AppNavbar.vue
вы вызываете $keycloak.authenticated
сразу после монтирования компонента, как показано ниже:
onMounted(() => {
console.log('Keycloak', $keycloak);
console.log('Keycloak authenticated', $keycloak.authenticated);
if ($keycloak.authenticated) {
state.authenticated = true;
state.token = $keycloak?.token;
}
console.log('state', state);
});
Почему Это Не Работает?
При монтировании компонента состояние $keycloak.authenticated
может еще не обновиться, так как инициализация происходит асинхронно. Поэтому, даже если после инициализации состояние будет true, ваш компонент может попытаться получить это значение слишком рано.
Рекомендации по Исправлению
-
Ожидание Инициализации: Используйте
keycloak.onAuthSuccess
после успешной аутентификации для обновления локального состояния. -
Проверка Аутентификации: Вы можете подписаться на изменения статуса аутентификации, используя обработчики событий Keycloak.
Примерно так:
onMounted(() => {
$keycloak.onAuthSuccess = () => {
state.authenticated = true;
state.token = $keycloak?.token;
};
if ($keycloak.authenticated) {
state.authenticated = true;
state.token = $keycloak.token;
}
});
Заключение
Обработка асинхронных операций в JavaScript и Vue важна для корректной работы приложения. Убедитесь, что вы ожидаете завершения инициализации Keycloak перед обращением к его свойствам. Таким образом, ваше состояние аутентификации будет корректным. Не забудьте протестировать изменения и провести отладку, чтобы убедиться, что все работает так, как ожидалось.