Доступ к свойству плагина Keycloak не предоставляет правильное значение.

Вопрос или проблема

Я пытаюсь аутентифицироваться, используя 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, ваш компонент может попытаться получить это значение слишком рано.

Рекомендации по Исправлению

  1. Ожидание Инициализации: Используйте keycloak.onAuthSuccess после успешной аутентификации для обновления локального состояния.

  2. Проверка Аутентификации: Вы можете подписаться на изменения статуса аутентификации, используя обработчики событий Keycloak.

Примерно так:

onMounted(() => {
    $keycloak.onAuthSuccess = () => {
        state.authenticated = true;
        state.token = $keycloak?.token;
    };

    if ($keycloak.authenticated) {
        state.authenticated = true;
        state.token = $keycloak.token;
    }
});

Заключение

Обработка асинхронных операций в JavaScript и Vue важна для корректной работы приложения. Убедитесь, что вы ожидаете завершения инициализации Keycloak перед обращением к его свойствам. Таким образом, ваше состояние аутентификации будет корректным. Не забудьте протестировать изменения и провести отладку, чтобы убедиться, что все работает так, как ожидалось.

Оцените материал
Добавить комментарий

Капча загружается...