Keycloak-js client with Quasar (now updated for v2)

So you’d like to use openid-connect (oidc), especially keycloak (kc) in your Quasar app.

There’s a package, @dsb-norge/vue-keycloak-js . I’d recommend you fork it and create your own version with the keycloak-js version that matches your Keycloak server. However it also works with just the version used in this git repository.

The git repository is available at
https://github.com/dsb-norge/vue-keycloak-js

This is for Quasar v1

Alright let’s get started.

1. Create a file named silent-check-sso.html with the following content:

Put that file in the public directory as its path is
public/silent-check-sso.html.

2. Create boot/keycloak.js

3. Reference the created boot file in quasar.conf.js

And that’s really all there is to it.

After this is done you can access the keycloak object via $keycloak in your template.

This is for Qusasar v2:

Thanks a bunch to Excel1 and yusufkandemir for figuring it out.

First you have to upgrade or use the v2 branch of @dsb-norge/vue-keycloak-js.
e.g. npm i @dsb-norge/vue-keycloak-js@2
or use your own fork

But essentially you do whatever you would do for v1 only the boot/keycloak.js file is different

The boot/keycloak.js file

and of course don’t forget to add it to the boot array in quasar.conf.js

11 Replies to “Keycloak-js client with Quasar (now updated for v2)”

  1. Thanks for the helpful post.

    I tried also to connect kecloak-js with quasar by using our tutorial but i cant get it work…

    By using your code:
    #boot/keycloak.js#
    import VueKeyCloak from ‘@dsb-norge/vue-keycloak-js’
    import axios from ‘axios’

    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    export default async ({ Vue, router, store, app }) => {
    // eslint-disable-next-line @typescript-eslint/require-await
    async function tokenInterceptor () {
    axios.interceptors.request.use(config => {
    // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/restrict-template-expressions
    config.headers.Authorization = Bearer ${Vue.prototype.$keycloak.token}
    return config
    }, error => {
    return Promise.reject(error)
    })
    }

    return new Promise(resolve => {
    // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
    Vue.use(VueKeyCloak, {
    init: {
    onLoad: ‘login-required’, // or ‘check-sso’
    flow: ‘standard’,
    pkceMethod: ‘S256’,
    silentCheckSsoRedirectUri: window.location.origin + ‘/silent-check-sso.html’,
    checkLoginIframe: false // otherwise it would reload the window every so seconds
    },
    config: {
    url: ‘url’,
    realm: ‘realm’,
    clientId: ‘clientId’
    },
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    onReady: (keycloak) => {
    void tokenInterceptor()
    resolve()
    }
    })
    })
    }
    ###

    by using this i get console error:
    [Quasar] boot error: TypeError: Vue is undefined

    Also i dont get it, why you use ‘silent-check-sso.html’ instead of redirecting directly to the mainpage

    And i cant read out how to use it… maybe iam using it wrong:
    ###

    import {
    defineComponent
    } from ‘vue’;

    export default defineComponent({
    name: ‘LoginComponent’
    });

    ###

    Additionally the auth token didnt get refreshed like
    ###
    setInterval(() => {
    keycloak
    // If token is expiring in that many secs, from now, refresh it
    .updateToken(70)
    .then((refreshed) => {
    if (refreshed) {
    console.log(“Keycloak Token refreshed” + refreshed);
    } else {
    console.log(
    “Keycloak Token not refreshed, valid for another ” +
    Math.round(
    keycloak.tokenParsed.exp +
    keycloak.timeSkew –
    new Date().getTime() / 1000
    ) +
    ” seconds”
    );
    }
    })
    .catch(() => {
    console.log(“Failed to refresh Keycloak Token”);
    });
    // Recheck if the token is expired in order to refresh it (millis)
    }, 60000);
    ### from https://github.com/quasarframework/quasar/issues/4758

    Have a great day! 🙂

    1. 1. You use silent-check-sso.html to silently refresh the token before expiration in the background. That file is referenced in the keycloak.js file.

      2. Line 4 in keycloak.js you pass the {Vue,router,store,app} object so there Vue should be referenced. You don’t have to import Vue in this file since the reference is passed in this object.

      Only reason I can think of is that you’re using Quasar v2 and this is for Quasar v1. I have not done anything with v2 yet.

    2. And you use it like

    3. On 2nd thought I see all those //tslint comments so you’re using typescript.
      I’m not using typescript with Quasar so it’s likely you have to define or find the definition of the object passed in the initializer function.

      Something like
      { Vue, router, store, app } as PluginInitializerObject

      But you’ll have to ask the Quasar guys for details.

        1. Hmm 🙂 Thanks for feedback. Once summer has passed I’ll get back to work and probably play with Quasar v2 (and Vue 3 in general).
          Right now it’s just too hot here in Croatia.

          Thanks for posting a link to your Github issue regarding this in v2. Let’s hope someone from their team can help with it.
          Being stuck on the redirection URL like this means something failed.
          Check console log, http requests and responses. I usually have 3rd party cookies disabled and that also plays a role.
          Not using https on the keycloak server side might also be an issue since OIDC requires HTTPS and Chrome especially can be a princess when it comes to http and https and especially localhost.

    1. It’s a start, but this is only half of it.

      redirectUri: ${process.env.VUE_APP_PACKAGE_ID}://home,

      you don’t explain what goes there.
      How does the adapter know the app “domain”?

      From the keycloak documentation:

      I don’t see them in your package.json .

      Did you test that this works?

Leave a Reply to Benoit Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.