Table of Contents
- SSR, Hybrid, Generate - Authentication per Mode
- Option 1: Integration with nuxt-auth-utils - New and improved
- Option 2: Integration with @sidebase/nuxt-auth - Battle-tested and ready to use
- Conclusion: nuxt-auth-utils or @sidebase/nuxt
Meet the Author
2025-10-10
User Authentication for Nuxt Applications with KeycloakNuxt & Keycloak: A Simple Guide to SSR Integration
Integrating Keycloak into a Nuxt application for robust authentication can be a challenge, especially with regard to Server-Side Rendering (SSR). In this article, we compare two leading modules, nuxt-auth-utils and @sidebase/nuxt-auth, and provide a step-by-step guide to help you choose the right solution.
Prerequisites
You should have the following knowledge to use the article optimally:If you have any questions or if anything is unclear, feel free to use the comment function below the article.
SSR, Hybrid, Generate - Authentication per Mode
First, it's important to identify the mode in which the Nuxt application is running. This determines the different requirements for authentication. A blog article on the different modes can be found here.
Server-Side Rendering - If user information such as permissions, names, etc., is processed during server-side rendering, the Nitro server needs access to it. Accordingly, the Nitro/Nuxt server must be able to validate whether a session is authenticated or authorized.
Client-Side Rendering - There is no server logic. Everything is processed on the client-side (the browser) - accordingly, the handling of user information/session also runs exclusively on the client-side.
Hybrid-Rendering - Depending on whether the SSR routes require user session information or not, the Keycloak integration must be considered.
Option 1: Integration with nuxt-auth-utils - New and improved
Integrating Keycloak and Nuxt with nuxt-auth-utils
could hardly be simpler. A little configuration in nuxt.config.js
and the basic framework is ready:
runtimeConfig: {
oauth: {
keycloak: {
serverUrl: 'https://keycloak.blueshoe.io',
realm: 'Blueshoe',
clientId: 'blueshoe-website',
// clientSecret: '',
redirectURL: 'https://blueshoe.io/auth/keycloak',
},
},
},
modules: [
'nuxt-auth-utils',
]
Now, convenient Composables are available:
const { loggedIn, user, session, fetch, clear, openInPopup } = useUserSession()
With this information, user-specific information can be rendered quickly and easily:
<template>
<span v-if="loggedIn">Hello, {{ user.firstName }} {{ user.lastName }}span>
<span v-else>Hello Guestspan>
template>
Likewise, useful utils are available on the SSR side. If a route absolutely requires a valid user session, this can be easily achieved with the following composable:
const session = await requireUserSession(event)
So far, so good - but how does the login work?
Through the environment variables, the nuxt-auth-utils module has all the information available to generate the login redirect and to use the session on the redirect back to the Nuxt application.
💡 Note: The
runtimeConfig
is not an environment variable itself, but the way Nuxt provides access to environment variables. More on this in the Nuxt documentation.
For this, a server-side route is simply created under server/auth/keycloak.get.ts
:
export default defineOAuthKeycloakEventHandler({
async onSuccess(event, { user }) {
await setUserSession(event, {
user: {
keycloak: user.preferred_username,
},
loggedInAt: Date.now(),
})
return sendRedirect(event, '/')
},
})
If you now call /auth/keycloak/, you will be automatically redirected to the running Keycloak instance and receive a session after a successful login.
The module is by Sébastien Chopin - the creator of Nuxt himself - and has a solid standing with (as of Sep 2025) 95,000 downloads per month, regular updates, and 1,400 stargazers, and can be recommended with confidence.
Option 2: Integration with @sidebase/nuxt-auth - Battle-tested and ready to use
Another way to bring Nuxt and Keycloak together is the @sidebase/nuxt-auth
package. With a few adjustments in nuxt.config.js
, the module can be easily configured:
runtimeConfig: {
public: {
authOrigin: 'http://localhost:3000',
},
},
modules: [
'@sidebase/nuxt-auth',
],
auth: {
isEnabled: true,
disableServerSideAuth: false,
provider: {
type: 'authjs',
trustHost: false,
defaultProvider: 'keycloak',
addDefaultCallbackUrl: true,
},
sessionRefresh: {
enablePeriodically: true,
enableOnWindowFocus: true,
},
},
As the configuration already shows, @sidebase/nuxt-auth
takes care of refreshing the session automatically. Keycloak exists as a pre-configured provider. We create the following file at server/api/auth/[...].ts
and configure the provider:
import KeycloakProvider from 'next-auth/providers/keycloak'
import { NuxtAuthHandler } from '#auth'
export default NuxtAuthHandler({
secret: 'your-secret-here',
providers: [
KeycloakProvider.default({
clientId: process.env.KEYCLOAK_ID,
clientSecret: process.env.KEYCLOAK_SECRET,
issuer: process.env.KEYCLOAK_ISSUER,
})
]
})
It is important to note here that the URL to the Keycloak realm must be specified as the issuer: https://my-keycloak-domain.com/realms/My_Realm.
Additionally, callbacks can be defined to react to various events:
export default NuxtAuthHandler({
...
callbacks: {
/* on before signin */
async signIn({ user, account, profile, email, credentials }) {
return true
},
/* on redirect to another url */
async redirect({ url, baseUrl }) {
return baseUrl
},
/* on session retrieval */
async session({ session, user, token }) {
return session
},
/* on JWT token creation or mutation */
async jwt({ token, user, account, profile, isNewUser }) {
return token
}
}
})
@sidebase/nuxt-auth
automatically creates a page that provides the login and integrates with the given providers.
The user's data is then available in the application as follows:
<script setup>
const {
status,
data,
lastRefreshedAt,
getCsrfToken,
getProviders,
getSession,
signIn,
signOut
} = useAuth()
script>
The status indicates whether the user is authenticated or not. data
contains user-specific data.
The module is SSR compatible and allows rendering different information based on the session.
@sidebase/nuxt-auth
is developed by sidebase and has established itself as a reliable solution for authentication in Nuxt applications. With a well-established company behind it and regular updates, it offers a solid foundation for production applications and can be recommended with confidence.
Conclusion: nuxt-auth-utils or @sidebase/nuxt
As is often the case, the devil is in the details of the use case. 😉 Both modules have a good track record regarding further development, the integration is simple, and the composables are very good.
If you want to customize the typical authentication pages yourself, @sidebase/nuxt-auth
is more suitable. The module also comes with a configurable simple refresh logic for the user session.
nuxt-auth-utils
, on the other hand, "feels" a bit more lightweight. A request for automatic session refresh is still pending. However, this can usually be easily retrofitted yourself.
We can warmly recommend both modules and look forward to feedback on your experiences with Nuxt and Keycloak in the comments!
How did you "plug together" Nuxt and Keycloak? Are there better ways? What are your experiences with the modules?