If you’ve used react-oidc-context in a React app, you already know the appeal: a thin, predictable wrapper around oidc-client-ts that turns the OIDC / OAuth 2.0 protocol into clean, reactive state. For a long time Vue 3 had nothing quite like it – so I built vue-oidc-context, a deliberate port of react-oidc-context v3 that keeps the same option names, the same lifecycle, and the same callbacks, but exposes them the way Vue wants: a plugin, composables, renderless components, and a vue-router guard.
The short version
vue-oidc-context is OpenID Connect & OAuth 2.0 authentication for Vue 3 – a lightweight, fully typed bridge from oidc-client-ts into Vue’s reactivity system. All the real protocol work (authorization code + PKCE, token storage, silent renewal, session monitoring) stays in oidc-client-ts. This library just makes it reactive and Vue-idiomatic.
One detail up front: it’s published as @dlukt/vue-oidc-context on npm. The unscoped name vue-oidc-context belongs to an unrelated package, so install the scoped one.
Install
|
1 2 3 |
pnpm add @dlukt/vue-oidc-context oidc-client-ts # or npm install @dlukt/vue-oidc-context oidc-client-ts |
oidc-client-ts is a peer dependency, so it installs as its own package. Requirements: Vue 3.5+, oidc-client-ts 3.3+, and Node 20+. vue-router 4.2+ is optional – you only need it for the route guard.
Set it up once
Create the auth instance and install it as a plugin:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// main.ts import { createApp } from "vue"; import { createOidcAuth } from "@dlukt/vue-oidc-context"; import App from "./App.vue"; const auth = createOidcAuth({ authority: "https://demo.duendesoftware.com", client_id: "interactive.public", redirect_uri: `${window.location.origin}/`, post_logout_redirect_uri: `${window.location.origin}/`, onSigninCallback: () => { // strip ?code=...&state=... from the URL after the redirect window.history.replaceState({}, document.title, window.location.pathname); }, }); createApp(App).use(auth).mount("#app"); |
That options object is flat – UserManagerSettings plus a handful of callbacks side by side. If you’re coming from react-oidc-context, your existing config object works unchanged.
Use it anywhere with useAuth()
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<script setup lang="ts"> import { useAuth } from "@dlukt/vue-oidc-context"; const { user, isAuthenticated, isLoading, error, signinRedirect, signoutRedirect, } = useAuth(); </script> <template> <p v-if="isLoading">Signing you in/out…</p> <p v-else-if="error">Something went wrong: {{ error.message }} ({{ error.source }})</p> <div v-else-if="isAuthenticated"> Hello, {{ user?.profile.name }} <button @click="signoutRedirect()">Log out</button> </div> <button v-else @click="signinRedirect()">Log in</button> </template> |
useAuth() returns reactive refs – user, isAuthenticated, isLoading, activeNavigator, and error – plus the full UserManager surface: signinRedirect, signinPopup, signinSilent, signoutRedirect, removeUser, clearStaleState, revokeTokens, startSilentRenew, stopSilentRenew, the raw events bus, and more.
Reactivity tip: destructure the fields you need. If you keep the whole object (const auth = useAuth()), nested refs don’t unwrap in templates and you’d have to write auth.user.value. Destructuring sidesteps that entirely.
Protect routes with the guard
|
1 2 3 |
import { createAuthGuard } from "@dlukt/vue-oidc-context/router"; router.beforeEach(createAuthGuard(auth)); |
By default it protects any route with meta: { requiresAuth: true }, awaits auth.initialized so there’s no redirect loop, and preserves the return path in signinArgs. Not using vue-router? Drop in the <AuthenticationRequired> component to guard a subtree instead.
Why this exists
Vue has auth options, but I wanted exactly the react-oidc-context experience: a thin layer that doesn’t hide the protocol, a config object I recognize, and upstream docs that still apply. So the move from React is mostly mechanical:
| react-oidc-context | vue-oidc-context |
|---|---|
| <AuthProvider {…config}> | app.use(createOidcAuth(config)) |
| useAuth() | useAuth() (same name; refs to destructure) |
| withAuthenticationRequired(…) | createAuthGuard(auth) or <AuthenticationRequired> |
| useAutoSignin() | useAutoSignin() |
| hasAuthParams() | hasAuthParams() |
Same option names, same lifecycle, same callbacks. Copy your config and keep reading the oidc-client-ts / react-oidc-context docs.
What you get
- Authorization Code flow with PKCE (via oidc-client-ts)
- Silent renewal – refresh-token based when you request offline_access (no third-party cookies needed), or hidden-iframe otherwise
- A typed events bus (addAccessTokenExpiring, addUserLoaded, …)
- Multiple navigators: redirect, popup, silent, and resource-owner-credentials sign-in
- useAutoSignin() for automatic sign-in
- Renderless <AuthProvider> for nested / multi-IdP setups
- Strict TypeScript throughout, types sourced from oidc-client-ts
- SSR-safe by construction – importing it never touches window
A note on SSR
It’s SSR-safe out of the box: importing any entry point is side-effect-free and never touches window/document; environment detection happens at construction time. On the server, createOidcAuth() yields an inert context – user stays undefined, isLoading stays true, and navigators reject with a clear “only available in a browser” error. There’s a Nuxt recipe (client-only plugin) in the docs, with a proper Nuxt module on the roadmap.
Try it
There’s a playground in the repo – clone it, run pnpm play, and log in against the public Duende demo IdP. Full docs live at dlukt.github.io/vue-oidc-context, including a migration guide and an API reference.
Honest caveat
It’s at 0.1.0 – brand new. The API is stable in intent, but treat it as early. I’d love feedback, bug reports, and weird edge cases.
MIT-licensed, on GitHub and npm as @dlukt/vue-oidc-context. If you’re porting an app off React or just adding OIDC to a Vue project, give it a spin and tell me what breaks.
#development #vue #vuejs #oidc #openidconnect #oauth2 #authentication #typescript #webdev #frontend #identity #sso #opensource