Bearer tokens for mobile apps, email verification, and password reset flows.
For mobile apps and API clients that cannot use cookies, add ?mode=token to login and signup requests. The server returns a JWT access token and an opaque refresh token instead of setting a cookie:
curl -X POST http://localhost:3000/__nk_auth/login?mode=token -H "Content-Type: application/json" -d '{"email": "[email protected]", "password": "s3cret!!"}' // Response (200) { "accessToken": "eyJhbGciOiJIUzI1NiJ9...", "refreshToken": "rt_a7b3c9d1e5f2...", "expiresIn": 900, "tokenType": "Bearer", "user": { "sub": "a1b2c3...", "email": "[email protected]", ... } }
Use the access token in subsequent requests:
curl http://localhost:3000/api/data -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."
Refresh: exchange an expired access token for a new pair (rotation: old refresh token is deleted):
curl -X POST http://localhost:3000/__nk_auth/refresh -H "Content-Type: application/json" -d '{"refreshToken": "rt_a7b3c9d1e5f2..."}' // Response (200) { "accessToken": "eyJ...", "refreshToken": "rt_new...", "expiresIn": 900 }
Revoke: invalidate a refresh token (mobile logout):
curl -X POST http://localhost:3000/__nk_auth/revoke -H "Content-Type: application/json" -d '{"refreshToken": "rt_a7b3c9d1e5f2..."}' // Response (200) { "ok": true }
token.enabled: true). Access tokens are JWTs signed with the session secret (default TTL: 15 min). Refresh tokens are opaque strings stored in SQLite (default TTL: 7 days). Refresh token rotation is automatic. Every refresh deletes the old token and issues a new one.
Enable email verification for native auth to require users to confirm their email before signing in:
// lumenjs.auth.ts export default { providers: [ { type: 'native', name: 'native', requireEmailVerification: true, }, ], session: { secret: process.env.AUTH_SECRET }, };
Flow:
POST /__nk_auth/signuponEvent callback with type verification-email, containing a signed token and verification URLGET /__nk_auth/verify-email?token=...?verified=trueIf you have the email integration configured (lumenjs.email.ts), verification emails are sent automatically using your configured email provider (SMTP, SendGrid, or Resend). Otherwise, handle the event manually:
// lumenjs.auth.ts export default { providers: [ { type: 'native', name: 'native', requireEmailVerification: true }, ], session: { secret: process.env.AUTH_SECRET }, onEvent: async (event) => { if (event.type === 'verification-email') { console.log('Verify URL:', event.url); // Send email via your own provider } if (event.type === 'password-reset') { console.log('Reset URL:', event.url); } if (event.type === 'password-changed') { console.log('Password changed for:', event.email); } }, };
The forgot/reset password flow works similarly to email verification:
Request a reset link:
curl -X POST http://localhost:3000/__nk_auth/forgot-password -H "Content-Type: application/json" -d '{"email": "[email protected]"}' // Response (200) - always succeeds (doesn't reveal if email exists) { "message": "If an account with that email exists, a password reset link has been sent." }
The framework fires an onEvent callback with type password-reset. If the email integration is configured, the reset email is sent automatically.
Reset the password:
curl -X POST http://localhost:3000/__nk_auth/reset-password -H "Content-Type: application/json" -d '{"token": "eyJhb...", "password": "newP@ssword123"}' // Response (200) { "message": "Password has been reset. You can now sign in." }
forgot-password endpoint never reveals whether an email is registered. It always returns the same success message to prevent user enumeration.