How to connect keycloak and Nuxt
Tags: nuxt, vue, tutorial
Published: 2024-07-08 16:51:06




While working in an internal project, I got the task of getting the connection between keycloak and our nuxt application.





After an hour of research, I found two feasible options to get this working fast and easy.


→ Using the `keycloak-js` 

1. manage the keycloak manually 
```vue
<script setup>
import Keycloak from 'keycloak-js'
import { useKeycloak } from '@/stores/keycloak'

useHead({
    title: 'Home page'
})

const config = useRuntimeConfig()
const store = useKeycloak()

const state = reactive({
    loggedIn: false
})

if (config.public.keycloakDisabled) {
    state.loggedIn = true
} else {
    const initOptions = {
        url: config.public.keycloakUrl,
        realm: config.public.keycloakRealm,
        clientId: config.public.keycloakClientId,
        onLoad: 'login-required'
    }

    const keycloak = new Keycloak(initOptions)
    keycloak
        .init({ onLoad: initOptions.onLoad })
        .then((auth) => {
            if (!auth) {
                window.location.reload()
            } else {
                store.setup(keycloak)
                state.loggedIn = true
            }
        })
}
</script>
<template>
    <div>
        <div v-if="state.loggedIn">
            <Header />
            <NuxtPage />
        </div>
    </div>
</template>
```
With this option you don’t have public pages




→ Using `# Nuxt OpenID-Connect` Module 
that is using `node-openid-client`

With this option, you can have public routes by just extending the `nuxt-config`

```ts
openidConnect: {
    addPlugin: true,
    op: {
      issuer: "http://keycloak:8080/realms/dev-realm", // change to your OP addrress
      clientId: "CLIENT_ID",
      clientSecret: "SECRET_KEY",
      callbackUrl: "", // optional
      scope: ["email", "profile", "address"],
    },
    config: {
      debug: true,
      response_type: "code",
      secret: "oidc._sessionid",
      cookie: { loginName: "" },
      cookiePrefix: "oidc._",
      cookieEncrypt: true,
      cookieEncryptKey: "SECRET_KEY",
      cookieEncryptIV: "ab83667c72eec9e4",
      cookieEncryptALGO: "aes-256-cbc",
      cookieMaxAge: 24 * 60 * 60, //  default one day
      cookieFlags: {
        access_token: {
          httpOnly: true,
          secure: false,
        },
      },
    },
  },
```

Then create a `middleware/auth.global.ts`

```ts
export default defineNuxtRouteMiddleware((to, from) => {
  if (import.meta.server) {
    return;
  }
  const isAuthRequired = to.meta.auth || false;

  const oidc = useOidc();

  if (isAuthRequired && !oidc.isLoggedIn) {
    oidc.login(to.fullPath);
  }
});

```


for public pages, you can set the meta attribute:

```vue
<script lang="ts" setup>
/**
 *
 * Component Description:Desc
 *
 * @author Reflect-Media <ismael@leamsigc.com>
 * @version 0.0.1
 *
 * @todo [ ] Test the component
 * @todo [ ] Integration test.
 * @todo [✔] Update the typescript.
 */
definePageMeta({
  auth: false,
  layout: "public-view",
});
</script>

<template>
  <div class="grid place-items-center">
    <RegistrationForm />
  </div>
</template>
<style scoped></style>

```

for the pages that need authentication:

```vue
<script lang="ts" setup>
/**
 *
 * Component Description:Desc
 *
 * @author Reflect-Media <reflect.media GmbH>
 * @version 0.0.1
 *
 * @todo [ ] Test the component
 * @todo [ ] Integration test.
 * @todo [✔] Update the typescript.
 */
definePageMeta({
  auth: true,
});
</script>

<template>
  <div>content</div>
</template>
<style scoped></style>

```


The other option is to create 
- `layouts/default.vue` → that set the auth to true by default
- `layouts/publicView.vue` → will set the auth to false.



Resources:


[Module](https://github.com/aborn/nuxt-openid-connect)
[Example with keycloak-js](https://github.com/FAIRDataTeam/TrainHandler-client)





***Happy hacking!*




{% gist https://gist.github.com/leamsigc/68d3f891d9298c35de273caa2b21e453 %} 


> Working on the audio version


[The Loop VueJs Podcast](https://podcasters.spotify.com/pod/show/the-loop-vuejs)