@josempgon/vue-keycloak
Version:
Keycloak plugin for Vue 3 with Composition API
271 lines (206 loc) • 10.7 kB
Markdown
<table align="center" cellspacing="0" cellpadding="0" style="border: none;">
<tr style="border: none;">
<td style="border: none;">
<img width="200px" src="https://vuejs.org/images/logo.png" />
</td>
<td style="border: none;">
➕
</td>
<td style="border: none;">
<img width="200px" src="https://www.keycloak.org/resources/images/icon.svg" />
</td>
</tr>
</table>
[](https://www.npmjs.com/package/@josempgon/vue-keycloak)
[](https://bundlephobia.com/package/@josempgon/vue-keycloak)
[](https://npm-stat.com/charts.html?package=%40josempgon%2Fvue-keycloak)
[](https://www.typescriptlang.org/)
A small Vue wrapper library for the [Keycloak JavaScript adapter](https://www.keycloak.org/securing-apps/javascript-adapter).
> This library is made for [Vue 3](https://vuejs.org/) with the [Composition API](https://vuejs.org/guide/extras/composition-api-faq.html#what-is-composition-api).
- [Vue](https://vuejs.org/) 3.4+
- [keycloak-js](https://www.npmjs.com/package/keycloak-js) 18–26
Using npm:
```bash
npm install @josempgon/vue-keycloak
```
Using yarn:
```bash
yarn add @josempgon/vue-keycloak keycloak-js
```
Using pnpm:
```bash
pnpm add @josempgon/vue-keycloak keycloak-js
```
Import the library into your Vue app entry point.
```typescript
import { vueKeycloak } from '@josempgon/vue-keycloak'
```
Apply the library to the Vue app instance.
```typescript
const app = createApp(App)
app.use(vueKeycloak, {
config: {
url: 'http://keycloak-server',
realm: 'my-realm',
clientId: 'my-app',
}
})
```
| Object | Type | Required | Description |
| ----------- | --------------------------------------------- | -------- | ---------------------------------------- |
| config | [`KeycloakConfig`][Config] | Yes | Keycloak configuration. |
| initOptions | [`KeycloakInitOptions`][InitOptions] | No | Keycloak init options. |
```typescript
{
flow: 'standard',
checkLoginIframe: false,
onLoad: 'login-required',
}
```
User-provided `initOptions` are **merged** with the defaults above, so you only need to specify values you want to override.
Use the example below to generate a dynamic Keycloak configuration. In that example the Keycloak adapter is initialized in silent `check-sso` mode. Be aware that this mode could have limited functionality with recent browser versions (check [Modern Browsers with Tracking Protection](https://www.keycloak.org/securing-apps/javascript-adapter#_modern_browsers) for additional info).
```typescript
app.use(vueKeycloak, async () => {
const url = await getAuthBaseUrl()
const silentCheckSsoRedirectUri = `${location.origin}/silent-check-sso.html`
return {
config: {
url,
realm: 'my-realm',
clientId: 'my-app',
},
initOptions: {
onLoad: 'check-sso',
silentCheckSsoRedirectUri,
},
}
})
```
When using `silentCheckSsoRedirectUri`, you must serve a `silent-check-sso.html` file at that path. See the [Keycloak documentation](https://www.keycloak.org/securing-apps/javascript-adapter#_using_the_adapter) for the required file contents.
`app.use()` does not await async plugins, so if you need authentication to complete before the rest of your app setup (e.g. router initialization), call `vueKeycloak.install` directly and await it:
**router/index.js**
```typescript
import { createRouter, createWebHistory } from 'vue-router'
const routes = [ /* Your routes */ ]
const initRouter = () => {
const history = createWebHistory(import.meta.env.BASE_URL)
return createRouter({ history, routes })
}
export { initRouter }
```
**main.js**
```javascript
import { createApp } from 'vue'
import { vueKeycloak } from '@josempgon/vue-keycloak'
import vueKeycloakConfig from './config/vueKeycloak.js'
import App from './App.vue'
import { initRouter } from './router'
const app = createApp(App)
await vueKeycloak.install(app, vueKeycloakConfig)
app.use(initRouter())
app.mount('#app')
```
If you are building for a browser that does not support [Top-level await](https://caniuse.com/mdn-javascript_operators_await_top_level), you should wrap the Vue plugin and router initialization in an async [IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE):
```javascript
(async () => {
await vueKeycloak.install(app, vueKeycloakConfig);
app.use(initRouter());
app.mount('#app');
})();
```
A helper function is exported to manage the access token.
| Function | Type | Description |
| ---------------- | ------------------------------------------------------ | ---------------------------------------------------------------------- |
| getToken | <pre>(minValidity?: number) => Promise\<string\></pre> | Returns a promise that resolves with the current access token. |
The token will be refreshed if expires within `minValidity` seconds. The `minValidity` parameter is optional and defaults to 10. If -1 is passed as `minValidity`, the token will be forcibly refreshed.
A typical usage for this function is to be called before every API call, using a request interceptor in your HTTP client library.
```typescript
import axios from 'axios'
import { getToken } from '@josempgon/vue-keycloak'
// Create an instance of axios with the base URL read from the environment
const baseURL = import.meta.env.VITE_API_URL
const instance = axios.create({ baseURL })
// Request interceptor for API calls
instance.interceptors.request.use(
async config => {
const token = await getToken()
config.headers['Authorization'] = `Bearer ${token}`
return config
},
error => {
Promise.reject(error)
},
)
```
Always call `getToken()` when you need a token to send. The `token` value exposed by `useKeycloak()` is a snapshot from the last time the token was set — it is not refreshed in the background, so it will eventually expire in place and be rejected by your API. `getToken()` refreshes it when needed and updates the reactive state.
```vue
<script setup>
import { useKeycloak } from '@josempgon/vue-keycloak';
const { isPending, isAuthenticated, error, username, userId, keycloak } = useKeycloak();
</script>
<template>
<div v-if="isPending">
<h2>Loading...</h2>
</div>
<div v-if="isAuthenticated">
<h1>Welcome to Your Keycloak Secured Vue.js App</h1>
<h2>User: {{ username }}</h2>
<h2>User ID: {{ userId }}</h2>
<div>
<button @click="keycloak.logout">Logout</button>
</div>
</div>
<div v-if="error">
<h2>Authentication Error</h2>
<h3>{{ error }}</h3>
</div>
</template>
<style>
text-align: center;
}
button {
cursor: pointer;
}
</style>
```
The `useKeycloak` function exposes the following data.
| State | Type | Description |
| --------------- | ------------------------------------------------------ | ------------------------------------------------------------------- |
| keycloak | `ShallowRef<`[`Keycloak`][Instance]`>` | Instance of the keycloak-js adapter. |
| isAuthenticated | `Ref<boolean>` | If `true` the user is authenticated. |
| isPending | `Ref<boolean>` | If `true` the authentication request is still pending. |
| hasFailed | `Ref<boolean>` | If `true` an error occurred on initialization or Keycloak request. |
| error | `Ref<Error>` | Info on error that occurred (null if no error) |
| token | `Ref<string>` | Raw value of the access token. May be expired. |
| decodedToken | `Ref<`[`KeycloakTokenParsed`][TokenParsed]`>` | Decoded value of the access token. |
| username | `Ref<string>` | Username. Extracted from `decodedToken['preferred_username']`. |
| userId | `Ref<string>` | User identifier. Extracted from `decodedToken['sub']`. |
| roles | `Ref<string[]>` | List of the user's roles. |
| resourceRoles | `Ref<Record<string, string[]>>` | List of the user's roles in specific resources. |
#### Functions
| Function | Type | Description |
| ---------------- | --------------------------------------------------------- | ----------------------------------------------------------------- |
| hasRoles | <pre>(roles: string[]) => boolean</pre> | Returns `true` if the user has all the given roles. |
| hasResourceRoles | <pre>(roles: string[], resource: string) => boolean</pre> | Returns `true` if the user has all the given roles in a resource. |
This project is licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0).
Originally developed by Gery Hirschfeld (2021).
Maintained and extended by José Miguel Gonçalves (2021-present).
[Config]: https://github.com/keycloak/keycloak-js/blob/26.2.1/lib/keycloak.d.ts#L27-L40
[InitOptions]: https://github.com/keycloak/keycloak-js/blob/26.2.1/lib/keycloak.d.ts#L82-L231
[TokenParsed]: https://github.com/keycloak/keycloak-js/blob/26.2.1/lib/keycloak.d.ts#L360-L375
[Instance]: https://github.com/keycloak/keycloak-js/blob/26.2.1/lib/keycloak.d.ts#L399-L685