auth

SKILL.md

Authentication Module

Bknd provides comprehensive authentication with multiple strategies, JWT token management, secure cookies, and role-based authorization.

What You'll Learn

  • Enable and configure the auth module
  • Set up password, OAuth, and email OTP strategies
  • Configure JWT tokens and secure cookies
  • Create users via CLI, programmatic API, or OAuth
  • Use authentication endpoints
  • Follow security best practices

Enable Auth

Add auth configuration to your bknd.config.ts:

export default {
  config: {
    auth: {
      enabled: true,
      jwt: {
        issuer: 'your-app-name',
      },
    },
  },
};

Bknd auto-generates a secure JWT secret if you don't provide one. In production, set a secret via environment variable.

Password Strategy

Email/password authentication with automatic hashing.

Configuration

export default {
  config: {
    auth: {
      enabled: true,
      allow_register: true, // Allow public registration
      strategies: {
        password: {
          type: 'password',
          enabled: true,
          config: {
            hashing: 'sha256', // 'plain' | 'sha256' | 'bcrypt'
            rounds: 4, // For bcrypt only (1-10)
            minLength: 8,
          },
        },
      },
    },
  },
};

API Endpoints

Register new user:

POST /api/auth/password/register
Content-Type: application/json

{ "email": "user@example.com", "password": "securepass123" }

Response:

{ "user": { "id": 1, "email": "...", "role": "user" }, "token": "jwt..." }

Login:

POST /api/auth/password/login
Content-Type: application/json

{ "email": "user@example.com", "password": "securepass123" }

Response:

{ "user": { "id": 1, "email": "...", "role": "user" }, "token": "jwt..." }

Logout:

GET /api/auth/logout

Clears auth cookie and redirects to configured pathLoggedOut.

OAuth Strategy

Built-in support for Google, GitHub, Discord, Facebook.

Built-in Providers

export default {
  config: {
    auth: {
      enabled: true,
      strategies: {
        google: {
          type: 'oauth',
          enabled: true,
          config: {
            name: 'google',
            type: 'oidc',
            client: {
              client_id: process.env.GOOGLE_CLIENT_ID,
              client_secret: process.env.GOOGLE_CLIENT_SECRET,
            },
          },
        },
        github: {
          type: 'oauth',
          enabled: true,
          config: {
            name: 'github',
            type: 'oauth2',
            client: {
              client_id: process.env.GITHUB_CLIENT_ID,
              client_secret: process.env.GITHUB_CLIENT_SECRET,
            },
          },
        },
      },
    },
  },
};

OAuth Flow

Initiate login:

GET /api/auth/google/login

Redirects to Google authorization page.

Callback:

GET /api/auth/google/callback?code=...&state=...

Handles OAuth callback, creates/updates user, sets auth cookie, redirects to configured pathSuccess.

Custom OAuth

Define custom OAuth 2.0 providers:

export default {
  config: {
    auth: {
      enabled: true,
      strategies: {
        my_provider: {
          type: 'custom_oauth',
          enabled: true,
          config: {
            name: 'my_provider',
            type: 'oauth2',
            as: {
              issuer: 'https://auth.example.com',
              authorization_endpoint: 'https://auth.example.com/authorize',
              token_endpoint: 'https://auth.example.com/token',
              userinfo_endpoint: 'https://auth.example.com/userinfo',
              scopes_supported: ['openid', 'email', 'profile'],
            },
            client: {
              client_id: process.env.PROVIDER_CLIENT_ID,
              client_secret: process.env.PROVIDER_CLIENT_SECRET,
              token_endpoint_auth_method: 'client_secret_post',
            },
            profile: (userInfo, config, tokenResponse) => ({
              sub: userInfo.id,
              email: userInfo.email,
              name: userInfo.name,
            }),
          },
        },
      },
    },
  },
};

Email OTP (Passwordless)

Time-based one-time passwords sent via email. Requires the email OTP plugin.

Configuration

import { emailOTP } from 'bknd/plugins';
import { resendEmail } from 'bknd';

export default {
  config: {
    auth: {
      enabled: true,
    },
  },
  options: {
    drivers: {
      email: resendEmail({
        apiKey: process.env.RESEND_API_KEY,
        from: 'noreply@example.com',
      }),
    },
    plugins: [
      emailOTP({
        ttl: 600, // Code expires in 10 minutes
        generateEmail: (otp) => ({
          subject: 'Your Login Code',
          body: `Your code is: ${otp.code}`,
        }),
      }),
    ],
  },
};

OTP Flow

Request code:

POST /api/auth/otp/login
Content-Type: application/json

{ "email": "user@example.com" }

Response:

{ "sent": true, "data": { "email": "...", "expires_at": "..." } }

Verify and login:

POST /api/auth/otp/login
Content-Type: application/json

{ "email": "user@example.com", "code": "123456" }

Response:

{ "user": { "id": "...", "email": "...", "role": "user" }, "token": "jwt..." }

JWT Configuration

Configure token signing and expiration:

export default {
  config: {
    auth: {
      jwt: {
        secret: process.env.JWT_SECRET, // Auto-generated if empty
        alg: 'HS256', // HS256 | HS384 | HS512
        expires: 3600, // Token expires in 1 hour
        issuer: 'your-app-name',
        fields: ['id', 'email', 'role'], // Fields in JWT payload
      },
    },
  },
};

Cookie Configuration

Secure session management with HTTP-only cookies:

export default {
  config: {
    auth: {
      cookie: {
        domain: undefined, // Uses current domain
        path: '/',
        sameSite: 'lax', // strict | lax | none
        secure: true, // HTTPS only
        httpOnly: true, // Prevents XSS
        expires: 604800, // 1 week in seconds
        partitioned: false, // CHIPS partitioned cookie
        renew: true, // Sliding session (refresh on activity)
        pathSuccess: '/', // Redirect after login
        pathLoggedOut: '/', // Redirect after logout
      },
    },
  },
};

Sliding Session: When renew: true, cookie expiration resets on every authenticated request. Session expires after inactivity period, not absolute time.

Create Users

CLI (Recommended for First User)

npx bknd user create

Prompts for email, password, and role.

Programmatic

import { createApp } from 'bknd';

const app = createApp(config);
await app.build();

const user = await app.auth.createUser({
  email: 'admin@example.com',
  password: 'securepassword',
  role: 'admin',
});

OAuth Users

OAuth users are created automatically on first login. No manual creation needed.

Current User

Get authenticated user data:

GET /api/auth/me
Authorization: Bearer <jwt-token>

Response:

{ "user": { "id": 1, "email": "...", "role": "user" } }

Returns 403 if not authenticated.

DOs and DON'Ts

DO

  • Set jwt.expires for defense-in-depth security
  • Use httpOnly: true to prevent XSS attacks
  • Enable renew: true for sliding sessions
  • Use environment variables for secrets
  • Configure sameSite: 'lax' or 'strict' to prevent CSRF
  • Use secure: true in production (HTTPS)
  • Set reasonable cookie expiration (1-7 days typical)

DON'T

  • Use plain text password hashing in production
  • Store JWT secrets in code
  • Set cookie.sameSite: 'none' unless you need cross-site cookies
  • Disable httpOnly - this exposes session to XSS
  • Set extremely short cookie expiration without renew: true
  • Use implicit_allow: true for roles unless absolutely necessary
  • Store passwords directly - use app.auth.createUser for hashing
Weekly Installs
1
GitHub Stars
2
First Seen
Jan 19, 2026
Installed on
opencode1
claude-code1