Skip to content

Comments

Add REST API v1 with token-based authentication#443

Open
mmichelli wants to merge 4 commits intomainfrom
claude/create-api-iTVbN
Open

Add REST API v1 with token-based authentication#443
mmichelli wants to merge 4 commits intomainfrom
claude/create-api-iTVbN

Conversation

@mmichelli
Copy link

@mmichelli mmichelli commented Feb 23, 2026

Description
This PR introduces a comprehensive REST API v1 for the application with token-based authentication, organization scoping, and full CRUD operations for core resources. The API follows RESTful conventions and integrates with the existing authorization policies.

Changes Made

  1. API Base Infrastructure

    • Added Api::V1::BaseController with token-based authentication via Authorization: Bearer <token> header
    • Implemented organization context switching via X-Organization-Id header
    • Integrated ActionPolicy for authorization and Pagy for pagination
    • Added error handling for common scenarios (unauthorized, not found, forbidden, unprocessable entity)
  2. API Endpoints

    • Organizations: GET /api/v1/organizations (index), GET /api/v1/organizations/:id (show)
    • Clients: Full CRUD operations with admin-only create/update/destroy
    • Projects: Full CRUD operations with admin-only access, includes assigned tasks
    • Tasks: Read-only access (index, show)
    • Time Registrations: Full CRUD with user-scoped access, filtering by date/project, pagination
    • Timers: Toggle timer state on time registrations
    • Users: List organization users, get current user info via /me endpoint
    • API Tokens: Regenerate user API tokens
    • Reports: Aggregated time tracking data by project and user with date/filter support
  3. Authentication & Authorization

    • Added api_token column to users table with unique index
    • Implemented ensure_api_token! and regenerate_api_token methods on User model
    • Updated policies (ClientPolicy, ProjectPolicy, TaskPolicy, UserPolicy, OrganizationPolicy, TimeRegPolicy) to support API access patterns
    • Token-based auth with organization scoping for multi-tenant support
  4. Views & Serialization

    • Added Jbuilder templates for all API resources with appropriate attribute selection
    • Implemented nested serialization (e.g., assigned tasks within projects, pagination metadata)
    • Consistent JSON response format across all endpoints
  5. Routing

    • Added API v1 namespace with all resource routes
    • Nested timer resource under time_regs
    • Custom /me route for current user endpoint
    • API token regeneration via PATCH
  6. Testing

    • Added comprehensive integration tests for all API endpoints
    • Authentication tests covering token validation and organization switching
    • Authorization tests verifying admin-only operations
    • Tests for filtering, pagination, and user-scoped access
    • Base test helper with api_headers utility for authenticated requests
  7. Configuration

    • Updated Ruby version requirement to >= 3.2.2 for flexibility

How to Test

  1. Run rails test test/controllers/api/v1/ to execute all API tests
  2. Generate an API token: user.ensure_api_token!
  3. Make authenticated requests with header: Authorization: Bearer <token>
  4. Switch organization context with header: X-Organization-Id: <org_id>
  5. Verify authorization by testing admin vs non-admin access to protected endpoints
  6. Test pagination on time_regs index with ?per_page=10&page=2
  7. Test filtering on time_regs with ?date=2024-01-15 or ?start_date=...&end_date=...

Additional Context

  • The API uses the existing ActionPolicy authorization framework, ensuring consistency with web UI permissions
  • Organization scoping is enforced at the controller level via resolve_organization before_action
  • All destructive operations use soft deletes (discard) rather than hard deletes
  • The API is designed for third-party integrations and mobile clients

Implements a full REST API with Bearer token authentication using Rails
has_secure_token. Endpoints cover all main resources: organizations,
clients, projects, tasks, time registrations, users, and reports.

Key changes:
- Add api_token column to users with unique index
- Create Api::V1::BaseController with token auth and JSON error handling
- Add API controllers for all resources with proper authorization
- Add missing policy methods (index?, show?, create?, etc.) to existing
  policies for Client, Task, TimeReg, User, and Project
- Fix ProjectPolicy scope to not filter by project_accesses for admins
- Add comprehensive integration tests (33 tests, all passing)

https://claude.ai/code/session_01P3uN3uxLcyQwmeYHGkMdgc
Move all JSON serialization out of controller private methods and into
Jbuilder view templates under app/views/api/v1/, following standard
Rails MVC conventions. Controllers now only authorize, scope queries,
and set instance variables.

- Replace Ruby-level group_by in ReportsController with SQL GROUP BY
  aggregation, eliminating full table loads into memory
- Fix OrganizationsController: add missing authorize! calls (security gap)
- Add OrganizationPolicy#index? and #show? so the policy actually covers
  these actions
- Remove redundant `to: :index?` from all authorize! calls; ActionPolicy
  infers the action from the method name
- Replace ActiveSupport String#remove with Ruby's delete_prefix for
  Bearer token extraction
- Drop custom pagy_metadata override from BaseController; pagination
  metadata is now rendered directly in the Jbuilder index view via @pagy
- Add `defaults: { format: :json }` to API route namespace so implicit
  Jbuilder rendering works without explicit render calls
- Use render :show for create/update responses (Rails convention)

https://claude.ai/code/session_01P3uN3uxLcyQwmeYHGkMdgc
…tokens

Address all issues from the DHH-style code review:

1. Extract toggle_active into TimersController — verb-y actions get
   their own resource controller. PATCH /time_regs/:id/timer replaces
   PATCH /time_regs/:id/toggle_active.

2. Extract regenerate_token into ApiTokensController — PATCH /api_token
   replaces POST /users/regenerate_token. One controller, one action.

3. Add X-Organization-Id header — multi-org users can now specify which
   organization context to use per-request. Without the header, falls
   back to the user's active organization. Uses singleton method
   override on the user instance so all existing policies work without
   modification.

4. Replace has_secure_token with on-demand generation — tokens are no
   longer auto-generated on User.create. New ensure_api_token! and
   regenerate_api_token methods generate tokens only when needed.

5. Move query from projects/show view into controller — the
   active_assigned_tasks.includes(:task) query now runs in the
   controller (show/create/update), not in the Jbuilder template.

6. Normalize error responses — all error handlers now return
   { errors: [...] } (always an array) instead of mixed error/errors
   keys.

All 38 API tests pass (97 assertions, 0 failures).

https://claude.ai/code/session_01P3uN3uxLcyQwmeYHGkMdgc
@mmichelli mmichelli requested a review from kaospr February 23, 2026 11:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants