Search code examples
vue.jsnuxt.jskeycloakkeycloak-js

NuxtJS + keycloak-js: frontend doesn't render the correct value


I have a Nuxt3 app which I want to secure with Keycloak. For the integration, I use the keycloak-js library, which is defined as a plugin:

plugins/keycloak.client.ts

import Keycloak from 'keycloak-js';
import type {KeycloakConfig} from 'keycloak-js';

export default defineNuxtPlugin((nuxtApp) => {
    const runtimeConfig = useRuntimeConfig();
    const initOptions: KeycloakConfig = {
        url: runtimeConfig.public.KEYCLOAK_URL!,
        realm: runtimeConfig.public.KEYCLOAK_REALM!,
        clientId: runtimeConfig.public.KEYCLOAK_CLIENT_ID!,
    };

    const keycloak = new Keycloak(initOptions);

    nuxtApp.$keycloak = keycloak;
    keycloak
        .init({
            onLoad: 'check-sso',
        });

    console.log('KeyCloak Plugin Loaded: ', nuxtApp.$keycloak);
});

The console logs this correctly.
For test purposes, I have a simple app.vue:

app.vue

<script setup lang="ts">
import Keycloak from 'keycloak-js';
const keycloak: Keycloak = useNuxtApp()['$keycloak'] as Keycloak
</script>

<template>
  <div>
    <div>
      Authenticated?
      {{keycloak.authenticated}}
    </div>
    <br/>
    <button @click="keycloak.login()">Login via Keycloak</button>
    <button @click="keycloak.logout()">Logout from Keycloak</button>
  </div>
</template>

Logging in and out works and the log of the keycloak object shows the correct value. This page always displays false when I load it, even if authenticated shows up as true in the console. However, if I do a small change in the code and the app hot-reloads, the correct value is shown (until the next refresh).

What's more, if I do:

console.log(keycloak);
console.log(keycloak.authenticated);

The log of the complete object will show the value to be true, but the second log will show false.

I should add that Nuxt is quite new for me. With my half-knowledge, I tried:

  • Different ways to import,
  • Putting the code in onBeforeMount(),
  • Declaring keycloak as a ref,
  • Putting a watch on keycloak

SSR is disabled in the nuxt.config.ts.
I'm fresh out of ideas.
What am I missing?


Solution

  • keycloak.init returns a Promise. keycloak.authenticated is read before the keycloak instance is initiated. Furthermore, keycloak.authenticated is not reactive, thus mutating its value will not reflect in the template.

    Method 1

    <script setup lang="ts">
    import Keycloak from 'keycloak-js';
    const keycloak: Keycloak = useNuxtApp()['$keycloak'] as Keycloak
    const authenticated = ref(false)
    
    keycloak.onReady = (v: boolean) => { authenticated.value = v }
    
    </script>
    
    <template>
      <div>
        <div>
          Authenticated?
          {{authenticated}}
        </div>
        <br/>
        <button @click="keycloak.login()">Login via Keycloak</button>
        <button @click="keycloak.logout()">Logout from Keycloak</button>
      </div>
    </template>
    

    Method 2

    await the initialization of keycloak in nuxt plugin

    export default defineNuxtPlugin({
      name: 'keycloak',
      order: 0,
      async setup() {
        await keycloak.init({ onLoad: 'check-sso' })
      },
    })