Operations
Deployment
Deploy OpenApe IdP and SP to production.
Deployment
Vercel (Recommended)
OpenApe is built on Nuxt/Nitro and works out of the box on Vercel.
IdP Deployment
- Push to GitHub and link the repository in Vercel Dashboard
- Set environment variables in Vercel:
NUXT_OPENAPE_IDP_RP_NAME=My Organization
NUXT_OPENAPE_IDP_RP_ID=id.example.com
NUXT_OPENAPE_IDP_RP_ORIGIN=https://id.example.com
NUXT_OPENAPE_IDP_SESSION_SECRET=<random-32+-chars>
NUXT_OPENAPE_IDP_MANAGEMENT_TOKEN=<random-64-chars>
NUXT_OPENAPE_IDP_ADMIN_EMAILS=admin@example.com
- Configure persistent storage in
nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@openape/nuxt-auth-idp'],
nitro: {
storage: {
'openape-idp': {
driver: 's3',
bucket: 'my-openape-data',
region: 'eu-central-1',
accessKeyId: process.env.S3_ACCESS_KEY,
secretAccessKey: process.env.S3_SECRET_KEY
},
'openape-grants': {
driver: 's3',
bucket: 'my-openape-data',
region: 'eu-central-1',
accessKeyId: process.env.S3_ACCESS_KEY,
secretAccessKey: process.env.S3_SECRET_KEY
}
}
}
})
The default storage driver is in-memory — data is lost on every deployment. Always configure S3 or another persistent driver for production.
- Configure DNS — add a
_ddisaTXT record for your domain:
_ddisa.example.com TXT "v=ddisa1 idp=https://id.example.com; mode=open"
- Deploy:
git push origin main # Vercel deploys automatically
SP Deployment
- Set environment variables:
NUXT_OPENAPE_SP_CLIENT_ID=app.example.com
NUXT_OPENAPE_SP_SESSION_SECRET=<random-32+-chars>
NUXT_OPENAPE_SP_FALLBACK_IDP_URL=https://id.example.com
- Configure Nitro for Vercel in
nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@openape/nuxt-auth-sp'],
nitro: {
preset: 'vercel',
externals: {
inline: ['@openape/nuxt-auth-sp']
}
}
})
Docker
IdP Dockerfile
FROM node:22-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-slim
WORKDIR /app
COPY --from=builder /app/.output .output
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", ".output/server/index.mjs"]
Docker Compose
services:
idp:
build: ./idp
ports:
- "3000:3000"
environment:
NUXT_OPENAPE_IDP_RP_NAME: "My Organization"
NUXT_OPENAPE_IDP_RP_ID: "id.example.com"
NUXT_OPENAPE_IDP_RP_ORIGIN: "https://id.example.com"
NUXT_OPENAPE_IDP_SESSION_SECRET: "${SESSION_SECRET}"
NUXT_OPENAPE_IDP_MANAGEMENT_TOKEN: "${MANAGEMENT_TOKEN}"
S3_ACCESS_KEY: "${S3_ACCESS_KEY}"
S3_SECRET_KEY: "${S3_SECRET_KEY}"
restart: unless-stopped
DNS Setup
DDISA TXT Record
For IdP discovery, add a TXT record to your domain:
_ddisa.example.com TXT "v=ddisa1 idp=https://id.example.com; mode=open"
Fields:
v=ddisa1— protocol version (required)idp=https://...— IdP URL (required)mode=open— enrollment mode:open(anyone can register) orinvite(registration URLs only)
Verify DNS
dig _ddisa.example.com TXT +short
# Expected: "v=ddisa1 idp=https://id.example.com; mode=open"
DNS propagation can take up to 48 hours, but most providers propagate within minutes. Use
dig or dnschecker.org to verify.S3 Storage Configuration
OpenApe supports any S3-compatible storage (AWS S3, MinIO, Cloudflare R2, DigitalOcean Spaces):
nitro: {
storage: {
'openape-idp': {
driver: 's3',
bucket: 'openape-data',
region: 'eu-central-1',
endpoint: 'https://s3.eu-central-1.amazonaws.com', // Optional for AWS
accessKeyId: process.env.S3_ACCESS_KEY,
secretAccessKey: process.env.S3_SECRET_KEY
}
}
}
For MinIO (self-hosted):
'openape-idp': {
driver: 's3',
bucket: 'openape',
endpoint: 'http://minio:9000',
accessKeyId: 'minioadmin',
secretAccessKey: 'minioadmin',
forcePathStyle: true // Required for MinIO
}
Reverse Proxy (nginx / Caddy)
If running behind a reverse proxy, ensure WebSocket and HTTPS headers are forwarded:
Caddy
id.example.com {
reverse_proxy localhost:3000
}
nginx
server {
server_name id.example.com;
listen 443 ssl;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Production Checklist
Security
- Strong
sessionSecret(32+ random characters) - Strong
managementToken(64+ random characters) - Management Token stored in secrets manager, not in code
- HTTPS everywhere (required for Passkeys)
- DNSSEC enabled for your domain
Storage
- Persistent storage configured (S3, filesystem — NOT in-memory)
- Storage backups enabled
- Separate storage keys for IdP data and grants
DNS
-
_ddisaTXT record published and verified - DNSSEC enabled (recommended)
Monitoring
- Audit logs collected and retained
- Alerts on failed authentication attempts
- Alerts on denied grants