@authup/client-web-nuxt
Why @authup/client-web-nuxt?
@authup/client-web-kit is a plain Vue plugin. In a Nuxt app it has to read the session from cookies during the server render, hand what it loaded on the server to the browser, know a different API address on each side, and check every navigation. This module does that wiring once.
| Concern | Without it | With it |
|---|---|---|
| Session cookies | Bridge the kit's cookie functions to useCookie on both sides |
Wired, including Secure from the request's scheme and an optional name prefix |
| API address | Pass one URL that has to work from the browser and from the server | apiURL for the browser, serverApiURL for the server, kept out of the page |
| Route guards | Write a middleware that resolves the session and checks every route | A global middleware reading requireLoggedIn, requireLoggedOut and requirePermissions page meta |
| Login callback | Redeem ?code= yourself and strip it from the URL |
The middleware exchanges it against the saved PKCE request and lands on the redirect path |
| Server rendering | Lists fetched on the server and again on hydration | The kit's hydration handoff rides nuxtApp.payload.data |
Installation
npm install @authup/client-web-nuxt @pinia/nuxt pinia
@pinia/nuxt and pinia are peer dependencies (the module registers @pinia/nuxt itself). Install the kit's own peers too, the Vue, vue-router, ilingo, validup and @vuecs/* packages listed in its README, plus a theme (@authup/client-web-kit-theme or @authup/client-web-theme). The module is built for Nuxt 4, is ESM-only and needs Node.js ^22.13.0 || ^23.5.0 || >=24.0.0.
Usage
Register the module
export default defineNuxtConfig({
modules: ['@authup/client-web-nuxt'],
authup: {
apiURL: 'https://auth.example.com',
serverApiURL: 'http://authup:3000',
},
css: ['@authup/client-web-theme/index.css'],
});
Every option except serverApiURL lands in the public runtimeConfig.public.authup, so NUXT_PUBLIC_AUTHUP_API_URL overrides apiURL at runtime. serverApiURL goes to the private runtimeConfig.authup and is overridden with NUXT_AUTHUP_SERVER_API_URL. It only changes where the server render sends its requests: every URL the kit's client derives from the API URL (identity provider links, the OAuth2 endpoints) keeps the public one, since hydration does not correct a rendered href. A buildAuthorizeURL call takes its base from you, so pass it the public apiURL.
Install vuecs after the kit
The module installs the kit, not vuecs. Add a plugin that runs after it:
import vuecs from '@vuecs/core';
import installForms from '@vuecs/forms';
import clientWebKitTheme from '@authup/client-web-kit-theme';
import clientWebTheme from '@authup/client-web-theme';
import { buildVuecsInstallOptions } from '@authup/client-web-kit';
export default defineNuxtPlugin({
name: 'vuecs',
dependsOn: ['authup:kit'],
setup(nuxtApp) {
const options = nuxtApp.vueApp.runWithContext(() => buildVuecsInstallOptions({
themes: [clientWebKitTheme(), clientWebTheme()],
}));
nuxtApp.vueApp.use(vuecs, options);
nuxtApp.vueApp.use(installForms);
},
});
Use dependsOn: ['authup:kit'], not enforce: 'pre': the kit provides the translator buildVuecsInstallOptions() reads, and running before it fails the whole plugin chain.
Guard a page
<script lang="ts">
import { PermissionName } from '@authup/core-kit';
import { defineComponent } from 'vue';
import { RouteMetaKey } from '#authup/nuxt';
definePageMeta({
[RouteMetaKey.REQUIRE_LOGGED_IN]: true,
[RouteMetaKey.REQUIRE_PERMISSIONS]: [PermissionName.USER_READ],
});
export default defineComponent({
setup() {
return {};
},
});
</script>
The global authup middleware runs on every navigation:
- A
?code=in the URL is exchanged on the client against the saved authorization request, then the user lands on theredirectquery (site-relative paths only) or on the same route without the OAuth2 parameters. - Otherwise it awaits
store.resolve(). On failure it signs out and sends the user tologinRoutewithredirectset to the page they asked for. requireLoggedIn(on the route or any parent) sends a signed-out user tologinRoute.requireLoggedOutsigns out a signed-in user and lets the navigation continue.requirePermissions(a name or a list; any one of them passes) sends a denied user back to the page they came from, home after a fresh login, or tologinRoutewhen they were denied the page they are on.
The authup:session-expiry plugin also sends the user to loginRoute when a background refresh fails on a page that requires login.
Read the session
import { StoreAuthStatus, injectStore } from '@authup/client-web-kit';
const store = injectStore();
const signedIn = store.status === StoreAuthStatus.AUTHENTICATED;
Everything else (permission checks, the login form, the authorization code helpers) comes from @authup/client-web-kit; see its README.
Options
Set under the authup key of nuxt.config:
| Option | Type | Default | Description |
|---|---|---|---|
apiURL |
string |
http://localhost:3001 |
API URL for the browser, and for the server when nothing more specific is set. |
serverApiURL |
string |
apiURL |
Address the server render dials. URLs the kit renders keep the public API URL. Private runtime config only. |
apiURLRuntimeKey |
string |
none | Path into the runtime config to read the API URL from: the whole runtime config on the server, the public one in the browser. serverApiURL still wins on the server. |
cookieDomain |
string |
host only | Domain attribute of the session cookies. 127.0.0.1 is written as localhost. |
cookieDomainRuntimeKey |
string |
none | Path into the public runtime config to read the cookie domain from. |
cookiePrefix |
string |
none | Prepended verbatim to every session cookie name (flame_ writes flame_access_token). Use letters, digits, _, - and . only. |
homeRoute |
string |
/ |
Where a user lands after a fresh login whose target they may not open. |
loginRoute |
string |
/login |
Where the middleware sends signed-out and expired users. |
API
| Export | Kind | Purpose |
|---|---|---|
| default | Nuxt module | Registers @pinia/nuxt, the plugins authup:kit, authup:session-expiry and authup, and the global authup route middleware |
ModuleOptions |
type | The authup options |
RouteMetaKey (from #authup/nuxt) |
enum | requireLoggedIn, requireLoggedOut, requirePermissions |
authup:middleware:start, authup:middleware:redirect, authup:middleware:end |
runtime hooks | Called by the middleware with { to, from } |
Notes
- The module brings no login page. Build
loginRouteyourself, for example with the kit'sALoginForm, or start the authorization code flow from it. - Keep the callback route client-only. The PKCE verifier lives in
sessionStorage, so the middleware skips the code exchange during the server render; arouteRulesentry withssr: falsefor the callback page avoids rendering it on the server at all. cookiePrefixis a contract across your services. A resource server of yours that reads the access token from the cookie must read the prefixed name.- The account preferences sync only in the browser. The plugin hands the kit
useCookierefs forvc-localeandvc-color-mode, written with the cookie options of@vuecs/nuxtwhen present, so the account's language and color mode reach your switchers and back. - This module does not enable the kit's
cookieSessionmode; a Nuxt app keeps the token pair in its own cookies.
License
Made with 💚
Published under the Apache 2.0 License.
Related Packages
Nuxt 4 module providing OpenID Connect / OAuth 2.0 authorization code grant with...
The Nuxt module for Idenzy: the Vue bindings, plus a server render seeded from t...
Nuxt integrator for Qelos: server middleware/module that identifies the user and...
Version History
Pre-releases (50)
| Version | Released | License |
|---|---|---|
| 1.0.1-beta.34 | Apache-2.0 | |
| 1.0.0-beta.71 | Apache-2.0 | |
| 1.0.0-beta.70 | Apache-2.0 | |
| 1.0.0-beta.69 | Apache-2.0 | |
| 1.0.0-beta.68 | Apache-2.0 | |
| 1.0.0-beta.67 | Apache-2.0 | |
| 1.0.0-beta.66 | Apache-2.0 | |
| 1.0.0-beta.65 | Apache-2.0 | |
| 1.0.0-beta.64 | Apache-2.0 | |
| 1.0.0-beta.63 | Apache-2.0 | |
| 1.0.0-beta.62 | Apache-2.0 | |
| 1.0.0-beta.61 | Apache-2.0 | |
| 1.0.0-beta.60 | Apache-2.0 | |
| 1.0.0-beta.59 | Apache-2.0 | |
| 1.0.0-beta.58 | Apache-2.0 | |
| 1.0.0-beta.57 | Apache-2.0 | |
| 1.0.0-beta.56 | Apache-2.0 | |
| 1.0.0-beta.55 | Apache-2.0 | |
| 1.0.0-beta.54 | Apache-2.0 | |
| 1.0.0-beta.53 | Apache-2.0 | |
| 1.0.0-beta.52 | Apache-2.0 | |
| 1.0.0-beta.51 | Apache-2.0 | |
| 1.0.0-beta.50 | Apache-2.0 | |
| 1.0.0-beta.49 | Apache-2.0 | |
| 1.0.0-beta.48 | Apache-2.0 | |
| 1.0.0-beta.47 | Apache-2.0 | |
| 1.0.0-beta.46 | Apache-2.0 | |
| 1.0.0-beta.45 | Apache-2.0 | |
| 1.0.0-beta.44 | Apache-2.0 | |
| 1.0.0-beta.42 | Apache-2.0 |
Showing the latest 30 of 50. See every release on npm