OpenApe Auth
OpenApe Auth
@openape/core
The foundation package. Framework-agnostic, zero dependencies.
- DNS Discovery β resolve
_ddisa.{domain}TXT records to find the IdP - PKCE β code challenge/verifier generation for secure OAuth flows
- JWT β sign, verify, and validate DDISA assertion tokens
- Types β
DDISAAssertionClaims,ActorType,AuthFlowState, etc.
@openape/auth
Complete OIDC login protocol logic β both sides in one package.
IdP Side
handleAuthorize()β validate authorization requestshandleTokenExchange()β exchange codes for signed assertionsissueAssertion()β create minimal AuthN-JWTs withactclaim- WebAuthn β
createRegistrationOptions(),verifyRegistration(),createAuthenticationOptions(),verifyAuthentication()
SP Side
discoverIdP()β DNS-based IdP discovery via DoHcreateAuthorizationURL()β build OAuth redirect with PKCEhandleCallback()β exchange code, validate assertion
@openape/nuxt-auth-idp
Drop-in Nuxt module. Add it, configure it, you're an IdP.
Auto-registered routes:
/login,/register,/accountβ Passkey-based UI (overridable)/adminβ User and agent management/authorize,/tokenβ OAuth endpoints/.well-known/jwks.jsonβ Public key discovery/api/webauthn/*β Registration and login flows/api/session/ssh-keysβ SSH-key enrollment and listing (see below)/api/auth/*β Unified challenge/authenticate for SSH-key auth (humans + agents)/api/admin/*β User, agent, and registration URL management
Configuration via openapeIdp in nuxt.config.ts:
openapeIdp: {
rpName: 'My IdP',
rpID: 'id.example.com',
rpOrigin: 'https://id.example.com',
requireUserVerification: true, // NIS2 strict mode
residentKey: 'required', // true passkey experience
attestationType: 'none', // or 'direct' for enterprise
}
See the dedicated nuxt-auth-idp docs for the full module option surface, environment variables, and store interfaces.
SSH-key authentication
Alongside passkeys, the IdP accepts Ed25519 SSH keys as a first-class credential for agents (and humans who prefer key-based auth, e.g. CI scripts). Keys are email-keyed β they are not bound to an RP, so the same SSH key authenticates against every hostname the IdP serves.
Manage keys on a logged-in user account:
| Route | Purpose |
|---|---|
GET /api/session/ssh-keys | List keys registered to the caller's account |
POST /api/session/ssh-keys | Add a public key β body { publicKey: string, name?: string } |
DELETE /api/session/ssh-keys/:keyId | Remove a key |
The publicKey field accepts the standard OpenSSH one-line format (ssh-ed25519 AAAA⦠comment). The comment is captured as the key's display name if name is omitted.
Authenticate with an SSH key (challenge/response, used by @openape/apes and escapes):
POST /api/auth/challenge { email, publicKey }
β { challenge: "<random>", challengeId: "<id>" }
(client signs the challenge with the matching private key)
POST /api/auth/authenticate { challengeId, signature }
β { token: "<JWT>" }
The returned JWT carries the same act claim discipline as passkey sessions (human or agent). SPs verifying the token do not need to distinguish the authentication method.
Admin-side key management is available at /api/admin/users/:email/ssh-keys β GET, POST, DELETE /:keyId β for operators provisioning CI or agent keys on behalf of users.
@openape/nuxt-auth-sp
Drop-in Nuxt module. Stateless. Zero server storage.
Auto-registered routes:
/api/loginβ initiate DDISA login flow/api/callbackβ handle OAuth callback/api/logoutβ destroy session/api/meβ current user info/.well-known/sp-manifest.jsonβ SP metadata
Composable: useOpenApeAuth() for client-side auth state.
Component: <OpenApeAuth /> β prebuilt login form with customizable slots and CSS variables.
See the dedicated nuxt-auth-sp docs for full API reference.