← Back to list

Implementing OpenID Connect Authentication in Nuxt 3 (SSR): A Complete Guide

#oidc #nuxt3 #serversiderendering

Sandra Sasi · 2025-05-08 09:38 · 1 claps · 3.7 min read
#openid-connect #nuxtjs #serverside-rendering #authentication
Open on Medium ↗
Wiki topics: 🌐 · Web Development

Implementing OpenID Connect Authentication in Nuxt 3 (SSR): A Complete Guide

oidc #nuxt3 #serversiderendering

Introduction

Recently, I embarked on a new project using Nuxt 3, TypeScript, and Pinia store. My focus was to build a robust application with a well-structured codebase, so I implemented the repository pattern for API calls and used Vitest for unit testing.

When it came to authentication, I relied on a helpful blog post titled “Implementing OpenID Connect (OIDC) Authentication with Nuxt 3”. This guide provided a solid foundation for integrating OIDC into my project. However, I encountered a few missing details, such as issues with the refresh token process, which required additional steps to achieve a fully functional implementation.

For those wondering why I’m taking these manual steps when libraries are available, it’s because Nuxt 3 currently lacks a fully complete authentication module. As stated on their website, “This module is still in development, feedback and contributions are welcome! Use at your own risk.” Additionally, I found Sidebase library to be not as user-friendly as I hoped. Therefore, I decided to integrate the authentication module into my project by leveraging the oidc-client-ts library.

In this article, I’ll walk you through those missing parts and share the solutions I applied to ensure a seamless OIDC authentication process in my Nuxt 3 application.

Installation

The first step is to install the oidc-client-ts library and the Pinia library for the store and configure it to the nuxt.config.ts file:

npm install oidc-client-ts
npm install pinia @pinia/nuxt

Add these to nuxt.config.ts:

//nuxt.config.ts
export default defineNuxtConfig({
  plugins: [
    '@/plugins/oidc.ts'
  ],
  modules: [
    '@pinia/nuxt'
  ]
});

Creating the OIDC Plugin

The first improvement I made to the implementation described in the aforementioned article was creating the OIDC as a plugin rather than a standalone functionality. While it is not strictly necessary to create the oidc.ts file in the plugins folder, it is highly recommended for several reasons. Organizing the OIDC configuration in a plugin provides better structure, maintainability, and integration with Nuxt's lifecycle hooks. Plugins in Nuxt.js are designed to allow you to extend the functionality of the framework and provide a centralized way to manage third-party integrations like OIDC.

Creating the Plugin File

Replace the AuthService.ts file with oidc.ts in the plugins folder:

//plugin/oidc.ts
import { defineNuxtPlugin } from '#app';
import { UserManager, WebStorageStateStore, InMemoryWebStorage } from 'oidc-client-ts';
import { useAuth } from '@/store/user';

export default defineNuxtPlugin(nuxtApp => {
   if (process.server) return;
  const config = useRuntimeConfig();
  const settings = {
    authority: config.public.authorityUrl,
    client_id: config.public.clientId,
    client_secret: config.public.clientSecret,
    redirect_uri: `${config.public.redirectUrl}`,
    silent_redirect_uri: `${config.public.silentUrl}`,
    post_logout_redirect_uri: config.public.applicationUrl,
    response_type: 'code',
    scope: config.public.clientScope,
    metadataUrl : config.public.metadataUrl,
    userStore: new WebStorageStateStore({ store: new InMemoryWebStorage() }),
    loadUserInfo: true,
  };

  const userManager = new UserManager(settings);
  userManager.events.addUserLoaded(user => {
    const authStore = useAuth();
    authStore.setUpUserCredentials(user);
  });

  nuxtApp.provide('oidc', userManager);
});

Creating the Auth Store

Convert the useAuth file into a composable for better integration within the Nuxt 3 framework:

//store/user.ts
import { acceptHMRUpdate, defineStore } from "pinia";
import { User } from "oidc-client-ts";

export const useAuth = defineStore("auth", () => {
  const authUser = ref<User | null>(null);

  const access_token = computed(() => authUser.value?.access_token ?? "");

  const isLoggedIn = computed(() => !!authUser.value);

  const config = useRuntimeConfig();

  const setUpUserCredentials = (user: User) => {
    authUser.value = user;
  };

  const clearUserSession = () => {
    authUser.value = null;
  };

  return {
    access_token,
    isLoggedIn,
    setUpUserCredentials,
    clearUserSession,
    authUser
  };
});

if (import.meta.hot) {
  import.meta.hot.accept(acceptHMRUpdate(useAuth, import.meta.hot));
}

Creating Middleware

Next, create a middleware to handle authentication. This middleware ensures users are redirected to the login page if they are not authenticated:

//middleware/auth.global.ts
import { useAuth } from '@/store/user'
import { UserManager } from 'oidc-client-ts';

export default defineNuxtRouteMiddleware(async (to, _from) => {
  if (process.server) 
    return;
  const authStore = useAuth();
  const userManager = useNuxtApp().$oidc as UserManager;;
  const user = await userManager.getUser();

  if (!user && !['/silent-refresh', '/logout','/openid'].includes(to.path)) {
    await userManager.signinRedirect();
  } else if(user) {
    authStore.setUpUserCredentials(user);
  }

});

**if (process.server) return;**: This line ensures that the subsequent code is only executed on the client side. If the code is running on the server, it will return early and skip the initialization of the OIDC client.

Creating Callback, Silent-Refresh, and Logout Pages

Ensure the /logout, /callback, and /silent-refresh routes match the values provided to the OIDC provider.

Callback Page

//pages/callback.vue
<template>
  <div>Loading...</div>
</template>

<script setup lang="ts">
import { useRouter } from 'vue-router'
import { UserManager } from 'oidc-client-ts'
const router = useRouter()

const handleCallback = async () => {
    const services = useNuxtApp().$oidc as UserManager

  try {
    await services.signinRedirectCallback()
    router.push('/')
  } catch (error) {
    console.error(error)
  }
}

handleCallback()
</script>

Silent-Refresh page

//pages/silent-resfresh.ts
<template>
  <div>Loading...</div>
</template>

<script setup lang="ts">
import { useRouter } from 'vue-router';
import { UserManager } from 'oidc-client-ts';
const router = useRouter();

const silentRefresh = async () => {
  const services = useNuxtApp().$oidc as UserManager;

  try {
    await services.signinSilent();
    router.push('/');
  } catch (error) {
    console.error(error);
  }
};

silentRefresh();
</script>

Logout Page

//pages/logout.vue
<template>
  <h3 class="question-header-inside-paragraph-mb-corpo pa-1 pl-5">Please wait logging out...</h3>
</template>

  <script lang="ts" setup>
  import { useAuth } from '@/store/user'
  import { UserManager } from 'oidc-client-ts';
  const authStore = useAuth()

  const logOutOidc = async () => {
    const services = useNuxtApp().$oidc as UserManager;
    try {
      authStore.clearUserSession()
      await services.signoutRedirect()
    } catch (error) {
      console.log(error)

    }
  }

  await logOutOidc()
  </script>

Using the Login Details in Your Application

You can now use the login details in your Nuxt app:

<template>
    <p v-if="user && user.profile" class="title-name nav-dropdown-mb-corpo"
      :title="`Hello ${user?.profile?.given_name}`">      
    </p>
</template>
<script setup lang="ts">
import { useAuth } from '@/store/user';
const authStore = useAuth();
const user = computed(() => authStore.authUser);
</script>

With these steps, your Nuxt 3 application will have OIDC authentication integrated seamlessly. This approach leverages the full power of Nuxt 3’s lifecycle hooks and provides a clean, maintainable codebase.


메타데이터
post_id
ef0603e08d1e
slug
implementing-openid-connect-authentication-in-nuxt-3-ssr-a-complete-guide-ef0603e08d1e
url
https://medium.com/@nia1234ruby/implementing-openid-connect-authentication-in-nuxt-3-ssr-a-complete-guide-ef0603e08d1e
canonical_url
https://medium.com/@nia1234ruby/implementing-openid-connect-authentication-in-nuxt-3-ssr-a-complete-guide-ef0603e08d1e
author_url
https://medium.com/@nia1234ruby
status
ok
fetched_at
2026-07-21 01:33:17