Ecosystem

nuxt-auth-sp

Add OpenApe login to any Nuxt app in minutes.

@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:

SettingAuto-value
sessionSecretRandom UUID (regenerated each restart)
clientIdlocalhost:{port}
fallbackIdpUrlhttps://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

PropTypeDefault
titlestring'Sign in'
subtitlestring'Enter your email to continue'
buttonTextstring'Continue'
placeholderstring'you@example.com'

Events

EventPayloadDescription
errorErrorEmitted when login fails

Slots

SlotPropsDescription
headerReplace the title/subtitle block
button{ submitting: boolean }Replace the submit button
error{ error: string }Replace the error display
footerAdd 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()
PropertyTypeDescription
userRef<DDISAAssertionClaims | null>Current authenticated user
loadingRef<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:

RouteMethodDescription
/api/loginPOSTInitiate DDISA login flow
/api/callbackGETHandle OAuth callback from IdP
/api/logoutPOSTDestroy session
/api/meGETReturn current user claims
/.well-known/sp-manifest.jsonGETSP 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:

VariableConfig Key
NUXT_OPENAPE_SP_CLIENT_IDclientId
NUXT_OPENAPE_SP_SP_NAMEspName
NUXT_OPENAPE_SP_SESSION_SECRETsessionSecret
NUXT_OPENAPE_SP_OPENAPE_URLopenapeUrl
NUXT_OPENAPE_SP_FALLBACK_IDP_URLfallbackIdpUrl

Production Deployment

For production, you must set:

  1. sessionSecret — a random string of 32+ characters
  2. clientId — 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'],
    },
  },
})