Comprehensive Keycloak authentication module for Nuxt 4 with SSR support, auto token refresh, role-based guards, and TypeScript.
Comprehensive Keycloak authentication module for Nuxt 4 with SSR support, auto token refresh, role-based guards, and TypeScript.
josepnpm add nuxt-keycloak
# or
npm install nuxt-keycloak
# or
yarn add nuxt-keycloak
nuxt-keycloak to the modules section of nuxt.config.ts:export default defineNuxtConfig({
modules: ['nuxt-keycloak'],
keycloak: {
url: 'http://localhost:8080',
realm: 'your-realm',
clientId: 'your-client-id',
}
})
NUXT_PUBLIC_KEYCLOAK_URL=http://localhost:8080
NUXT_PUBLIC_KEYCLOAK_REALM=your-realm
NUXT_PUBLIC_KEYCLOAK_CLIENT_ID=your-client-id
That’s it! You can now use Keycloak authentication in your Nuxt app ✨
Use the useKeycloak() composable in your components:
<script setup>
const { isAuthenticated, user, login, logout } = useKeycloak()
</script>
<template>
<div>
<div v-if="!isAuthenticated">
<button @click="login()">Login</button>
</div>
<div v-else>
<p>Welcome, {{ user?.username }}!</p>
<button @click="logout()">Logout</button>
</div>
</div>
</template>
Protect entire pages by adding middleware:
<!-- pages/dashboard.vue -->
<script setup>
definePageMeta({
middleware: 'keycloak-auth'
})
</script>
Protect pages based on Keycloak roles:
<!-- pages/admin.vue -->
<script setup>
definePageMeta({
middleware: ['keycloak-auth', 'keycloak-role'],
keycloakRoles: {
realm: ['admin'], // Requires 'admin' realm role
resource: { // OR requires client role
clientId: 'my-app',
roles: ['manager']
}
}
})
</script>
Server utilities are automatically available in Nuxt server routes (no import needed):
// server/api/protected.ts
export default defineEventHandler(async (event) => {
// Require authentication - auto-imported
const user = requireKeycloakAuth(event)
return {
message: 'Protected data',
user
}
})
// server/api/admin.ts
export default defineEventHandler(async (event) => {
// Require specific role - auto-imported
const user = requireRealmRole(event, 'admin')
return {
message: 'Admin data',
user
}
})
You can also explicitly import from nuxt-keycloak/server:
// server/api/custom.ts
import { extractToken, verifyKeycloakToken, requireRealmRole } from 'nuxt-keycloak/server'
export default defineEventHandler(async (event) => {
const token = extractToken(event)
const user = await verifyKeycloakToken(token)
// Use imported functions
requireRealmRole(event, 'admin')
return { user }
})
When deploying behind Traefik or another reverse proxy that already verifies JWT tokens, you can configure the module to skip JWKS verification and simply decode tokens:
export default defineNuxtConfig({
keycloak: {
url: process.env.NUXT_PUBLIC_KEYCLOAK_URL,
realm: process.env.NUXT_PUBLIC_KEYCLOAK_REALM,
clientId: process.env.NUXT_PUBLIC_KEYCLOAK_CLIENT_ID,
server: {
verifyToken: 'decode', // Decode only, trust proxy verification
middleware: true,
},
}
})
Or use environment variable:
NUXT_KEYCLOAK_SERVER_VERIFY_TOKEN=decode
undefinedImportant: Only use verifyToken: 'decode' when tokens are pre-verified by a trusted reverse proxy (Traefik ForwardAuth, etc.). This mode skips all signature and expiration validation.
For more control, manually verify tokens:
// server/api/custom.ts
export default defineEventHandler(async (event) => {
const token = extractToken(event)
if (!token) {
throw createError({
statusCode: 401,
message: 'No token provided'
})
}
const user = await verifyKeycloakToken(token)
if (!user) {
throw createError({
statusCode: 401,
message: 'Invalid token'
})
}
// Check custom logic
if (!hasRealmRole(event, 'special-access')) {
throw createError({
statusCode: 403,
message: 'Forbidden'
})
}
return { user }
})
All configuration options:
export default defineNuxtConfig({
keycloak: {
// Keycloak server URL
url: 'http://localhost:8080',
// Realm name
realm: 'my-realm',
// Client ID (must be a public client)
clientId: 'my-client',
// Keycloak initialization options
initOptions: {
onLoad: 'check-sso', // or 'login-required'
pkceMethod: 'S256',
flow: 'standard',
checkLoginIframe: false,
silentCheckSsoRedirectUri: 'http://localhost:3000/silent-check-sso.html'
},
// Server-side configuration
server: {
verifyToken: true, // true = full JWKS verification, false = disabled, 'decode' = decode only (for reverse proxy scenarios)
middleware: false, // Enable global middleware
jwksCacheDuration: 600000, // JWKS cache duration (10 minutes)
rejectUnauthorized: true // Verify SSL certificates for JWKS endpoint
},
// Client-side configuration
client: {
autoRefreshToken: true, // Auto-refresh tokens
minTokenValidity: 30, // Refresh when < 30s validity
persistRefreshToken: true // Store refresh token in localStorage
}
}
})
Use environment variables for different environments:
# Public (exposed to client)
NUXT_PUBLIC_KEYCLOAK_URL=https://keycloak.example.com
NUXT_PUBLIC_KEYCLOAK_REALM=production
NUXT_PUBLIC_KEYCLOAK_CLIENT_ID=nuxt-app
NUXT_PUBLIC_APP_URL=https://app.example.com
# Private (server-only)
NUXT_KEYCLOAK_SERVER_VERIFY_TOKEN=true # true, false, or 'decode'
NUXT_KEYCLOAK_SERVER_MIDDLEWARE=false
useKeycloak()Returns:
keycloak - Keycloak instance (client-side only)isAuthenticated - Authentication statusisInitialized - Initialization statususer - User profile datatoken - Access tokentokenParsed - Parsed token claimslogin(redirectUri?) - Redirect to loginlogout(redirectUri?) - Logout userregister(redirectUri?) - Redirect to registrationupdateToken(minValidity?) - Manually refresh tokenloadUserProfile() - Load user profilehasRealmRole(role) - Check realm rolehasResourceRole(role, resource?) - Check client roleisTokenExpired(minValidity?) - Check token expirationAll utilities are auto-imported in Nuxt server routes. You can also explicitly import them from nuxt-keycloak/server:
import {
verifyKeycloakToken,
extractToken,
requireKeycloakAuth
} from 'nuxt-keycloak/server'
undefinedAvailable Functions:undefined
verifyKeycloakToken(token) - Verify JWT token using JWKSextractToken(event) - Extract Bearer token from Authorization headergetKeycloakUser(event) - Get authenticated user from contextgetKeycloakToken(event) - Get access token from contextisAuthenticated(event) - Check if user is authenticatedhasRealmRole(event, role) - Check if user has realm rolehasResourceRole(event, role, resource?) - Check if user has client rolerequireKeycloakAuth(event) - Require authentication (throws 401 if not authenticated)requireRealmRole(event, role) - Require realm role (throws 403 if missing)requireResourceRole(event, role, resource?) - Require client role (throws 403 if missing)keycloak-auth - Protect routes (requires authentication)keycloak-role - Enforce role-based access (use with route meta)nuxt-app)openid-connectpublic (for SPA/Nuxt)http://localhost:3000/* (or your app URL)http://localhost:3000 (or your app URL)admin, user, managerCheck out the playground directory for comprehensive examples:
/ - No authentication required/dashboard - Requires authentication/admin - Requires ‘admin’ realm role/profile - Shows user info and token detailsMake sure your Keycloak client has the correct Web Origins configured. Add your app’s origin (e.g., http://localhost:3000).
Check that:
client.autoRefreshToken is true in configThe module automatically creates /public/silent-check-sso.html for you. If you need to customize it:
public/ directoryinitOptions.silentCheckSsoRedirectUri in config (e.g., http://localhost:3000/silent-check-sso.html)Note: The file is auto-generated by the module on nuxt prepare/nuxt dev, so you don’t need to manually create it.
Ensure:
keycloakRoles configurationkeycloak-auth and keycloak-role middlewares are applied# Install dependencies
pnpm install
# Generate type stubs
pnpm run dev:prepare
# Develop with the playground
pnpm run dev
# Build the playground
pnpm run dev:build
# Run ESLint
pnpm run lint
# Run Vitest
pnpm run test
pnpm run test:watch
# Release new version
pnpm run release
This project was inspired by and builds upon ideas from:
Special thanks to the open-source community for their contributions to Keycloak integration solutions.
Copyright © 2025
We use cookies
We use cookies to analyze traffic and improve your experience. You can accept or reject analytics cookies.