Skip to main content

Feature: Auth - API Tokens

Version: 1.0.0 Last Reviewed: 2026-02-10 Status: Approved

User Story

As a developer/parent, I can generate an API token for programmatic access (Home Assistant, scripts, etc.) so I can integrate baby tracking into my home automation.

MVP Scope

  • Generate a personal API token (random string with bb_ prefix)
  • Optionally name the token (e.g., "Home Assistant", "Shortcut")
  • List active tokens (metadata only, never the token value)
  • Revoke a token
  • Token works as Bearer auth header (same as session tokens)

NOT in MVP

  • Token scopes/permissions (all tokens have full access)
  • Token expiration (tokens are permanent until revoked)
  • Rate limiting per token

API Contract

Generate Token

POST /api/v1/auth/tokens
Authorization: Bearer <token>

Request:
{
"name": "Home Assistant" // optional, defaults to "API Token"
}

Response 201:
{
"api_token": {
"id": "uuid",
"token": "bb_abc123def456...",
"name": "Home Assistant",
"created_at": "2026-02-10T12:00:00.000Z"
}
}

Note: The plain `token` value is shown ONLY in this response.
It is stored as a SHA-256 hash in the database.
The user must save it immediately - it cannot be retrieved later.

List Tokens

GET /api/v1/auth/tokens
Authorization: Bearer <token>

Response 200:
{
"api_tokens": [
{
"id": "uuid",
"name": "Home Assistant",
"created_at": "2026-02-10T12:00:00.000Z",
"last_used": "2026-02-10T15:30:00.000Z"
}
],
"count": 1
}

Note: Token value is NEVER returned in list responses.

Revoke Token

DELETE /api/v1/auth/tokens/:id
Authorization: Bearer <token>

Response 204: (No Content)

Response 404:
{ "error": { "code": "NOT_FOUND", "message": "Token not found", "details": [] } }

Error Responses

Standard error format. Error codes: 400 VALIDATION_ERROR, 401 UNAUTHORIZED, 404 NOT_FOUND

Token Format

  • Prefix: bb_ (Baby Basics) for easy identification in config files
  • Body: crypto.randomBytes(32).toString('hex') (64 hex chars)
  • Full token: bb_<64-hex-chars> (67 chars total)
  • Storage: SHA-256 hash of the full token string stored in DB
  • Lookup: On auth, hash the incoming token, look up the hash

Security

  • Plain token shown only once on creation
  • Stored as SHA-256 hash (not reversible)
  • last_used updated on each authenticated request (fire-and-forget, non-blocking)
  • Revoking a token immediately invalidates it
  • Users can only see/revoke their own tokens
  • API tokens authenticate identically to session tokens - you can manage tokens from either auth method

Audit Logging

  • Token created: AuditLog entry with entity_type="api_token", action="create", changes={name} (never the token value)
  • Token revoked: AuditLog entry with entity_type="api_token", action="delete", changes={name}

Acceptance Criteria

  • Can generate a new API token (wrapped response)
  • Token has bb_ prefix
  • Token is stored as SHA-256 hash (not plaintext)
  • Can list all active tokens (wrapped with count)
  • Token value is NOT included in list response
  • Can revoke a token by ID (returns 204)
  • Revoked token returns 401 on subsequent use
  • API token works as Bearer auth for all authenticated endpoints
  • last_used updates when token is used for authentication
  • Cannot see or revoke another user's tokens (404)

Test Cases

  1. Generate: → 201, {api_token: {token: "bb_..."}}
  2. Authenticate with token: use generated token → success
  3. List tokens: generate 2, list → {api_tokens: [...], count: 2}, no token values
  4. Revoke: revoke token → 204, use it → 401
  5. Ownership: User A generates, User B tries to revoke → 404
  6. Last used: use token, list → last_used is recent
  7. Named token: name="Test" → list shows "Test"
  8. Default name: no name → list shows "API Token"
  9. Hash verification: check DB → token column is hex hash, not bb_ prefix
  10. Manage via API token: authenticate with API token A, revoke API token B → 204

Boundaries

  • No token scopes (full access)
  • No token expiration
  • No limit on number of tokens per user
  • API tokens and session tokens are interchangeable for authentication