CROP
ProjectsParts Services

Backend Authentication Implementation Guide

Status: Phase 1 Complete โœ… Date: 2025-11-13 Version: 1.0

Backend Authentication Implementation Guide

Status: Phase 1 Complete โœ… Date: 2025-11-13 Version: 1.0


๐Ÿ“‹ What Was Done

โœ… Completed Tasks

  1. Audit of All API Endpoints (ENDPOINTS_AUTH_AUDIT.md)

    • Classified all endpoints by auth requirements
    • Identified public, customer, and admin routes
    • Prioritized implementation tasks
  2. Clerk Environment Template (docs/.env.clerk.template)

    • Created reusable template for all services
    • Documented all Clerk environment variables
    • Added troubleshooting guide
  3. Package Dependencies Updated

    • Added @crop/shared-auth to:
      • โœ… services/payment
      • โœ… services/search
      • โœ… services/catalog
    • All dependencies resolved with bun install
  4. Payment Service Implementation (services/payment/src/app.ts)

    • โœ… Webhooks remain public (Svix verification)
    • โœ… Health/ready endpoints remain public
    • โณ Customer routes (need to add middleware)
    • โณ Admin routes (need to create endpoints)
  5. Auth Integration Tests (services/payment/src/__tests__/auth.test.ts)

    • Test cases for authentication
    • Test cases for authorization (roles)
    • Test cases for ownership
    • Placeholder tests for future admin endpoints
  6. Documentation

    • Endpoints audit
    • Clerk env vars template
    • This implementation guide

๐Ÿ”ง How to Use

For Backend Developers

1. Add Auth to Your Service

Step 1: Ensure @crop/shared-auth is in your package.json:

{
  "dependencies": {
    "@crop/shared-auth": "workspace:*"
  }
}

Step 2: Add Clerk env vars from docs/.env.clerk.template to your .env

Step 3: Import and use middleware in your app:

import { clerkMiddleware, requireRole } from '@crop/shared-auth';

const app = new OpenAPIHono();

// Public routes (no auth)
app.post('/webhooks/clerk', webhookHandler);
app.get('/health', healthHandler);

// Protected routes (auth required)
app.use('/api/*', clerkMiddleware());

// Customer routes (any authenticated user)
app.get('/api/items', async (c) => {
  const user = c.get('clerkUser');
  // user.sub = Clerk user ID
  // user.role = 'admin' | 'customer'
  // user.email

  // Return user-specific data
});

// Admin routes (admin role required)
app.use('/api/admin/*', requireRole('admin'));

app.delete('/api/admin/items/:id', async (c) => {
  // Only admins reach here
});

2. Access User Context

After clerkMiddleware(), user context is available:

const user = c.get('clerkUser');

console.log(user.sub);   // Clerk user ID (e.g., "user_2abc...")
console.log(user.email); // Email address
console.log(user.role);  // 'admin' | 'customer'
console.log(user.aud);   // 'https://api.crop.com'
console.log(user.azp);   // Frontend origin

3. Ownership Checks

For customer endpoints, verify ownership:

app.get('/api/orders/:id', clerkMiddleware(), async (c) => {
  const user = c.get('clerkUser');
  const orderId = c.req.param('id');

  const order = await orderRepo.findById(orderId);

  if (!order) {
    return c.json({ error: 'Order not found' }, 404);
  }

  // Check ownership (unless admin)
  if (user.role !== 'admin' && order.clerkId !== user.sub) {
    return c.json({ error: 'Forbidden' }, 403);
  }

  return c.json(order);
});

4. Error Responses

Middleware automatically returns JSON errors:

  • 401 Unauthorized: Missing or invalid token
  • 403 Forbidden: Valid token, insufficient permissions
  • 429 Too Many Requests: Rate limit exceeded (if enabled)

๐Ÿงช Testing

Running Auth Tests

  1. Get test tokens from Clerk Dashboard:

    • Users โ†’ Create test users (admin + customer)
    • For each user โ†’ Generate JWT (template: 'api')
    • Copy tokens
  2. Add to .env.test:

TEST_API_URL=http://localhost:3004/api
TEST_ADMIN_TOKEN=eyJhbGc...
TEST_CUSTOMER_TOKEN=eyJhbGc...
  1. Start service:
cd services/payment
bun run dev
  1. Run tests:
bun test src/__tests__/auth.test.ts

Expected output:

โœ“ should reject request without Authorization header
โœ“ should reject request with invalid JWT token
โœ“ should accept valid customer token
โœ“ should accept valid admin token
โœ“ should allow customer to view own payments
โœ“ should allow admin to view any payment

๐Ÿ“Š Services Status

ServiceAuditPackage.envImplementationTestsStatus
Paymentโœ…โœ…โœ…๐ŸŸก Partialโœ…In Progress
Searchโœ…โœ…โณโณโณTodo
Catalogโœ…โœ…โณโณโณTodo
Identity๐ŸŸกโณโณโณโณNot Started
Media๐ŸŸกโณโณโณโณNot Started
Health Analytics๐ŸŸกโณโณโณโณNot Started

Legend:

  • โœ… Complete
  • ๐ŸŸก Partial
  • โณ Todo

๐Ÿš€ Next Steps

Immediate (P0):

  1. Payment Service - Complete Implementation

    • Add clerkMiddleware() to customer routes
    • Create admin endpoints (refunds, delete payments)
    • Add ownership checks to GET /api/payments/:id
  2. Run Integration Tests

    • Generate test tokens
    • Add to .env.test
    • Run bun test src/__tests__/auth.test.ts
    • Fix any failures

This Week (P1):

  1. Search Service - Migrate Admin Auth

    • Replace ADMIN_API_TOKEN with Clerk
    • Add requireRole('admin') to admin endpoints
    • Keep backward compatibility temporarily
  2. Catalog Service - Add CRUD + Auth

    • Create admin endpoints for parts, brands, categories
    • Protect with requireRole('admin')
    • Add tests

Next Week (P2):

  1. Audit Remaining Services

    • Identity, Media, Health Analytics
    • Classify endpoints
    • Add auth where needed
  2. Production Deployment

    • Switch to production Clerk keys
    • Update Secret Manager
    • Deploy with smoke tests

๐Ÿ” Troubleshooting

"Cannot find module '@crop/shared-auth'"

Solution:

cd /Users/vova/Code/CROP/microservices
bun install

"Invalid token origin (azp mismatch)"

Cause: Frontend domain not in CLERK_AUTHORIZED_PARTIES

Solution: Check .env:

CLERK_AUTHORIZED_PARTIES=https://crop-front-mu.vercel.app,https://crop-front-admin.vercel.app

"Invalid token audience (aud mismatch)"

Cause: JWT template aud โ‰  CLERK_AUDIENCE

Solution:

  1. Check Clerk Dashboard โ†’ JWT Templates โ†’ api โ†’ Claims โ†’ aud
  2. Verify .env: CLERK_AUDIENCE=https://api.crop.com
  3. Must match exactly

Tests fail with "Missing Authorization header"

Cause: Service not applying middleware

Solution: Check app.ts:

// Must be BEFORE route handlers
app.use('/api/*', clerkMiddleware());

Webhook returns 401

Cause: Webhooks incorrectly protected by auth middleware

Solution: Webhooks must be BEFORE clerkMiddleware():

// Webhooks FIRST (no auth)
app.post('/webhooks/clerk', webhookHandler);
app.post('/webhooks/stripe', stripeWebhookHandler);

// Then apply auth to API routes
app.use('/api/*', clerkMiddleware());

  • ENDPOINTS_AUTH_AUDIT.md - Full endpoint audit
  • docs/.env.clerk.template - Env vars template
  • packages/shared-auth/README.md - Middleware API reference
  • CLERK_IMPLEMENTATION_PLAN_FINAL.md - Overall Clerk integration plan

๐Ÿ“ž Support

For Questions:

For Bugs:

  • Create issue in repo
  • Tag with authentication label

Last Updated: 2025-11-13 Author: Backend Team Reviewers: Vova (Backend Lead)

On this page