Reference

Authentication

How to configure authentication in dnax Framework.

dnax Framework provides built-in JWT authentication with customizable login/logout handlers.

Enable Authentication

Add api.auth to your collection definition:

import { define } from '@dnax/core';

export default define.Collection({
  slug: 'users',
  fields: [
    { name: 'email', type: 'email', required: true },
    { name: 'password', type: 'password', required: true },
  ],
  api: {
    auth: {
      enabled: true,
      onLogin: async ({ rest, payload, jwt }) => {
        // Find user by email
        const users = await rest.find('users', {
          $match: { email: payload.email }
        });
        
        const user = users[0];
        if (!user) {
          throw new Error('Invalid credentials');
        }
        
        // Verify password
        const isValid = await Bun.password.verify(payload.password, user.password);
        if (!isValid) {
          throw new Error('Invalid credentials');
        }
        
        // Generate JWT token
        const token = await jwt.sign({
          sub: user._id,
          email: user.email,
        });
        
        return {
          token,
          data: { user },
        };
      },
      onLogout: async ({ jwt, req }) => {
        // Optional: cleanup logic
      },
    },
  },
});

Login Action

POST /api/:tenant_id/users/login
Content-Type: application/json

{
  "payload": {
    "email": "[email protected]",
    "password": "secret"
  }
}

Response:

{
  "token": "<jwt_token>",
  "data": { "user": { ... } }
}

Store token and send it on later API calls:

Authorization: Bearer <jwt_token>

Invalid or expired tokens are rejected with 401 before your collection logic runs. Access control receives token.value (raw string) and token.decoded (verified claims) for each request.

Logout Action

POST /api/:tenant_id/users/logout
Content-Type: application/json

{}

Response:

{
  "message": "Logout successful",
  "ok": true
}

The login handler receives a cookies object to manage cookies:

onLogin: async ({ rest, req }) => {
  const token = await jwt.sign({ sub: userId });
  
  // Set cookie
  req.cookies.set('auth_token', token, {
    httpOnly: true,
    secure: true,
    maxAge: 60 * 60 * 24 * 7, // 7 days
    path: '/',
  });
  
  return { token, data: { user } };
}
OptionTypeDefaultDescription
httpOnlybooleantruePrevent XSS access
securebooleanfalseHTTPS only
maxAgenumber-Expiration in seconds
pathstring'/'Cookie path
domainstring-Cookie domain
sameSite'lax' | 'strict' | 'none''lax'Same-site policy

JWT utilities

The auth context provides jwt.sign and jwt.verify:

onLogin: async ({ jwt }) => {
  const token = await jwt.sign({ sub: userId, role: 'admin' });

  const { value, error } = await jwt.verify(token);
  if (error) {
    // handle invalid token
  }
  // value = decoded payload (claims)
}

For HTTP requests, prefer the Authorization: Bearer flow above: the server verifies the JWT and exposes token.value (string) and token.decoded (claims) to api.access handlers.

Error Handling

Throw errors with appropriate messages:

onLogin: async ({ error }) => {
  throw error('Invalid email or password', {
    code: 'INVALID_CREDENTIALS',
    status: 401,
  });
}
Copyright © 2026