DEV Community

Cover image for How to connect keycloak and Nuxt
Ismael Garcia
Ismael Garcia

Posted on

How to connect keycloak and Nuxt

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
<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>
Enter fullscreen mode Exit fullscreen mode

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

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,
        },
      },
    },
  },
Enter fullscreen mode Exit fullscreen mode

Then create a middleware/auth.global.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);
  }
});

Enter fullscreen mode Exit fullscreen mode

for public pages, you can set the meta attribute:

<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>

Enter fullscreen mode Exit fullscreen mode

for the pages that need authentication:

<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>

Enter fullscreen mode Exit fullscreen mode

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
Example with keycloak-js

**Happy hacking!

Working on the audio version

The Loop VueJs Podcast

Top comments (1)

Collapse
 
leamsigc profile image
Ismael Garcia

Is there a better way to do this? do you have any questions or want to see the example repo, just let me know