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:
1 2 3 4 5 6 7 |
<html> <body> <script> parent.postMessage(location.href, location.origin) </script> </body> </html> |
Put that file in the public
directory as its path is
public/silent-check-sso.html
.
2. Create boot/keycloak.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import VueKeyCloak from '@dsb-norge/vue-keycloak-js' import axios from 'axios' export default async ({ Vue, router, store, app }) => { async function tokenInterceptor () { axios.interceptors.request.use(config => { config.headers.Authorization = `Bearer ${Vue.prototype.$keycloak.token}` return config }, error => { return Promise.reject(error) }) } return new Promise(resolve => { 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: 'https://your.keycloak.installation/auth', realm: 'your-realm-name', clientId: 'your-client-id' }, onReady: (keycloak) => { tokenInterceptor() resolve() } }) }) } |
3. Reference the created boot file in quasar.conf.js
1 2 3 4 5 6 7 |
// ... boot: [ 'i18n', 'axios', 'keycloak' ], // ... |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import { boot } from 'quasar/wrappers' import VueKeyCloak from '@dsb-norge/vue-keycloak-js' import axios from 'axios' export default boot(async ({ app, router, store }) => { async function tokenInterceptor () { axios.interceptors.request.use(config => { config.headers.Authorization = `Bearer ${app.config.globalProperties.$keycloak.token}` return config }, error => { return Promise.reject(error) }) } return new Promise(resolve => { app.use(VueKeyCloak, { init: { onLoad: 'login-required', // or 'check-sso' flow: 'standard', pkceMethod: 'S256', silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html', checkLoginIframe: false }, config: { url: 'https://my.keylcoak.domain/auth', realm: 'my.realm', clientId: 'my-client_id' }, onReady: (keycloak) => { tokenInterceptor() resolve() } }) }) }) |
and of course don’t forget to add it to the boot array in quasar.conf.js
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. 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.
And you use it like
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.
Thank you for helping 🙂
I realized it to late that this is only for Quasar v.1.
I tried to create a Version for Quasar v.2. It works with only one redirection error.
https://github.com/quasarframework/quasar/issues/10062
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.
updated the post, tyvm for your trouble and the created issues 🙂
Since I’m not using hash mode (re: your issue on github) I don’t have any issues so far with it
Also used the dsb-norge/vue-keycloak-js libraries to make keycloak working on Quasar but to make it working on Ios & android devices, I had to use the Ionic-Keycloak library too.
Here is the code: https://github.com/marchalb/qkeycloak
I hope it helps …
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?
It’s not working. It redirects to localhost#state and not back to the app