nuxt-auth-sp
@openape/nuxt-auth-sp
Drop-in Nuxt module for Service Provider authentication. Stateless, zero server storage.
Quick Start
1. Install
pnpm add @openape/nuxt-auth-sp
2. Add to nuxt.config.ts
export default defineNuxtConfig({
modules: ['@openape/nuxt-auth-sp'],
})
That's it for development. The module auto-generates a sessionSecret and derives clientId from your dev server port.
3. Add a login page
<template>
<OpenApeAuth />
</template>
The <OpenApeAuth /> component handles the full login flow: email input, IdP redirect, error display, and loading states.
Zero-Config Dev Mode
In development (nuxt dev), the module auto-configures:
| Setting | Auto-value |
|---|---|
sessionSecret | Random UUID (regenerated each restart) |
clientId | localhost:{port} |
fallbackIdpUrl | https://id.openape.at |
No .env file or runtimeConfig needed to start developing.
<OpenApeAuth /> Component
A prebuilt login form shipped with the module. Handles email input, loading/error states, and the DDISA redirect flow.
Props
| Prop | Type | Default |
|---|---|---|
title | string | 'Sign in' |
subtitle | string | 'Enter your email to continue' |
buttonText | string | 'Continue' |
placeholder | string | 'you@example.com' |
Events
| Event | Payload | Description |
|---|---|---|
error | Error | Emitted when login fails |
Slots
| Slot | Props | Description |
|---|---|---|
header | — | Replace the title/subtitle block |
button | { submitting: boolean } | Replace the submit button |
error | { error: string } | Replace the error display |
footer | — | Add content below the form |
Styling
The component uses CSS custom properties for theming. Override them on .openape-auth:
.openape-auth {
--oa-bg: #ffffff;
--oa-border: #e2e2e2;
--oa-text: #1a1a1a;
--oa-text-muted: #6b7280;
--oa-primary: #18181b;
--oa-primary-hover: #27272a;
--oa-primary-text: #ffffff;
--oa-error: #dc2626;
--oa-error-bg: #fef2f2;
--oa-input-bg: #ffffff;
--oa-input-border: #d1d5db;
--oa-input-focus: #18181b;
--oa-radius: 8px;
--oa-font: system-ui, -apple-system, sans-serif;
}
Dark mode is supported automatically via prefers-color-scheme.
Examples
Minimal:
<template>
<OpenApeAuth />
</template>
Customized:
<template>
<OpenApeAuth
title="Welcome back"
subtitle="Sign in to your account"
button-text="Sign in"
placeholder="name@company.com"
@error="handleError"
>
<template #footer>
<p>Don't have an account? Contact your admin.</p>
</template>
</OpenApeAuth>
</template>
useOpenApeAuth() Composable
For custom login UIs, use the composable directly:
const { user, loading, fetchUser, login, logout } = useOpenApeAuth()
| Property | Type | Description |
|---|---|---|
user | Ref<DDISAAssertionClaims | null> | Current authenticated user |
loading | Ref<boolean> | Whether auth state is being fetched |
fetchUser() | () => Promise<void> | Fetch current user from /api/me |
login(email) | (email: string) => Promise<void> | Initiate login — redirects to IdP |
logout() | () => Promise<void> | Destroy session and redirect to / |
Custom Login Page Example
<script setup lang="ts">
const { user, loading, fetchUser, login, logout } = useOpenApeAuth()
onMounted(async () => {
await fetchUser()
if (user.value) navigateTo('/dashboard')
})
</script>
<template>
<div v-if="loading">Loading...</div>
<div v-else-if="user">
<p>Logged in as {{ user.sub }}</p>
<button @click="logout()">Logout</button>
</div>
<form v-else @submit.prevent="login(email)">
<input v-model="email" type="email" placeholder="you@example.com">
<button type="submit">Login</button>
</form>
</template>
Server API Routes
The module auto-registers these server routes:
| Route | Method | Description |
|---|---|---|
/api/login | POST | Initiate DDISA login flow |
/api/callback | GET | Handle OAuth callback from IdP |
/api/logout | POST | Destroy session |
/api/me | GET | Return current user claims |
/.well-known/sp-manifest.json | GET | SP metadata for IdP discovery |
Configuration
Full configuration in nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@openape/nuxt-auth-sp'],
openapeSp: {
clientId: 'myapp.example.com', // Your SP domain
spName: 'My App', // Display name
sessionSecret: '', // 32+ char secret (auto-generated in dev)
openapeUrl: '', // Direct IdP URL (bypasses DNS discovery)
fallbackIdpUrl: 'https://id.openape.at', // Fallback when DNS has no record
},
})
All options can be set via environment variables:
| Variable | Config Key |
|---|---|
NUXT_OPENAPE_SP_CLIENT_ID | clientId |
NUXT_OPENAPE_SP_SP_NAME | spName |
NUXT_OPENAPE_SP_SESSION_SECRET | sessionSecret |
NUXT_OPENAPE_SP_OPENAPE_URL | openapeUrl |
NUXT_OPENAPE_SP_FALLBACK_IDP_URL | fallbackIdpUrl |
Production Deployment
For production, you must set:
sessionSecret— a random string of 32+ charactersclientId— your production domain (e.g.myapp.example.com)
NUXT_OPENAPE_SP_SESSION_SECRET=$(openssl rand -hex 32)
NUXT_OPENAPE_SP_CLIENT_ID=myapp.example.com
The module logs warnings at startup if these are missing in production.
Nitro Configuration
For Vercel or other serverless deployments, add to nuxt.config.ts:
export default defineNuxtConfig({
nitro: {
externals: {
inline: ['@openape/nuxt-auth-sp'],
},
},
})