Overview
BunShip implements a stateful JWT authentication system. Short-lived access tokens (15 minutes) authorize API requests, while long-lived refresh tokens (7 days) are backed by database sessions that can be individually revoked. Two-factor authentication, account lockout, and API key auth are included out of the box.Auth Flow
Register
The user submits their email, password, and name. BunShip validates the password against strength rules, hashes it with Argon2id, creates the user record, and sends a verification email.
Verify email
The user clicks the link in their verification email. The API marks
emailVerified with the current timestamp.Login
The user submits their email and password. On success, the API returns an access token, a refresh token, and basic user info.
Make authenticated requests
Include the access token in the
Authorization header of subsequent requests.JWT Structure
BunShip uses two separate JWT secrets and the jose library for signing and verification.- Access Token
- Refresh Token
| Field | Value |
|---|---|
| Algorithm | HS256 |
| Expiry | 15 minutes |
| Secret | JWT_SECRET env var (min 32 chars) |
| Issuer | bunship |
Session Management
Every login creates a database-backed session record. Sessions store the hashed refresh token, the client’s user agent, IP address, and an expiration timestamp.- List sessions —
GET /api/v1/users/sessions - Revoke one session —
DELETE /api/v1/users/sessions/:id - Revoke all sessions —
DELETE /api/v1/users/sessions(logs out everywhere)
The maximum number of concurrent sessions per user defaults to 5 and is configurable via
featuresConfig.auth.maxSessionsPerUser.Two-Factor Authentication
BunShip supports TOTP-based two-factor authentication (compatible with Google Authenticator, Authy, 1Password, and similar apps) plus single-use backup codes.Setup Flow
Request 2FA setup
The user provides their current password. The API generates a TOTP secret, a QR code URI, and 10 backup codes.
Verify with a TOTP code
The user enters a 6-digit code from their authenticator app. This confirms the secret was saved correctly and activates 2FA on the account.
TOTP Parameters
| Parameter | Value |
|---|---|
| Algorithm | SHA-1 |
| Digits | 6 |
| Period | 30 seconds |
| Window | 1 step (accepts codes from 30s before and after) |
| Secret length | 20 bytes, base32 encoded |
Backup Codes
- 10 codes generated per setup
- Each code is an 8-character hexadecimal string
- Codes are hashed with SHA-256 before storage (the plaintext is never persisted)
- Each code can only be used once — the
usedAttimestamp is set on consumption - Re-running 2FA setup regenerates all backup codes and invalidates the previous set
Password Policies
BunShip validates password strength at registration and password reset. The rules are defined infeaturesConfig.auth.password:
| Rule | Default | Configurable |
|---|---|---|
| Minimum length | 8 characters | Yes |
| Require uppercase letter | Yes | Yes |
| Require lowercase letter | Yes | Yes |
| Require number | Yes | Yes |
| Require special character | No | Yes |
Bun.password API or the argon2 library):
- Memory: 65536 KB
- Iterations: 3
- Parallelism: 4
BunShip never stores plaintext passwords. The
passwordHash field in the users table contains
only the Argon2id hash output.Account Lockout
To protect against brute-force attacks, BunShip tracks failed login attempts and temporarily locks accounts.| Parameter | Default |
|---|---|
| Max failed attempts | 5 |
| Lockout duration | 15 minutes |
| Counter reset | On successful login |
auth.service.ts:
The login endpoint uses constant-time password verification even when the user does not exist,
preventing timing-based user enumeration.
API Key Authentication
API keys provide an alternative to JWT tokens for server-to-server integrations and automated scripts. Keys are scoped to an organization and carry explicit permission scopes.How API Keys Work
- A team member with
api-keys:createpermission generates a key through the API or dashboard - The full key is shown once (format:
bsk_live_...); only the prefix and hash are stored - The caller includes the key in the
Authorizationheader:Bearer bsk_live_... - The API resolves the key to an organization and checks that the key’s scopes grant the required permission
Available Scopes
Key Properties
| Property | Description |
|---|---|
name | Human-readable label (e.g., “CI/CD Pipeline”) |
keyPrefix | First 8 characters of the key, stored for identification |
keyHash | SHA-256 hash of the full key |
scopes | Array of granted permission scopes |
rateLimit | Per-key rate limit override (default: 1000 req/min) |
expiresAt | Optional expiration timestamp |
isActive | Can be deactivated without deletion |
Limits
| Parameter | Default |
|---|---|
| Max keys per organization | 10 |
| Default rate limit | 1000 requests/minute |
Auth Middleware Reference
BunShip provides two auth middleware variants:Supported Auth Methods
Email + Password
Traditional registration and login with password strength validation and Argon2id hashing.
Magic Link
Passwordless login via a one-time link sent to the user’s email address.
Google OAuth
Social login with Google. Requires
GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET environment
variables.GitHub OAuth
Social login with GitHub. Requires
GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET environment
variables.featuresConfig.auth:

