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
-
Audit of All API Endpoints (
ENDPOINTS_AUTH_AUDIT.md)- Classified all endpoints by auth requirements
- Identified public, customer, and admin routes
- Prioritized implementation tasks
-
Clerk Environment Template (
docs/.env.clerk.template)- Created reusable template for all services
- Documented all Clerk environment variables
- Added troubleshooting guide
-
Package Dependencies Updated
- Added
@crop/shared-authto:- โ services/payment
- โ services/search
- โ services/catalog
- All dependencies resolved with
bun install
- Added
-
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)
-
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
-
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 origin3. 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
-
Get test tokens from Clerk Dashboard:
- Users โ Create test users (admin + customer)
- For each user โ Generate JWT (template: 'api')
- Copy tokens
-
Add to
.env.test:
TEST_API_URL=http://localhost:3004/api
TEST_ADMIN_TOKEN=eyJhbGc...
TEST_CUSTOMER_TOKEN=eyJhbGc...- Start service:
cd services/payment
bun run dev- Run tests:
bun test src/__tests__/auth.test.tsExpected 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
| Service | Audit | Package | .env | Implementation | Tests | Status |
|---|---|---|---|---|---|---|
| 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):
-
Payment Service - Complete Implementation
- Add
clerkMiddleware()to customer routes - Create admin endpoints (refunds, delete payments)
- Add ownership checks to GET /api/payments/:id
- Add
-
Run Integration Tests
- Generate test tokens
- Add to
.env.test - Run
bun test src/__tests__/auth.test.ts - Fix any failures
This Week (P1):
-
Search Service - Migrate Admin Auth
- Replace
ADMIN_API_TOKENwith Clerk - Add
requireRole('admin')to admin endpoints - Keep backward compatibility temporarily
- Replace
-
Catalog Service - Add CRUD + Auth
- Create admin endpoints for parts, brands, categories
- Protect with
requireRole('admin') - Add tests
Next Week (P2):
-
Audit Remaining Services
- Identity, Media, Health Analytics
- Classify endpoints
- Add auth where needed
-
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:
- Check Clerk Dashboard โ JWT Templates โ
apiโ Claims โaud - Verify
.env:CLERK_AUDIENCE=https://api.crop.com - 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());๐ Related Documentation
ENDPOINTS_AUTH_AUDIT.md- Full endpoint auditdocs/.env.clerk.template- Env vars templatepackages/shared-auth/README.md- Middleware API referenceCLERK_IMPLEMENTATION_PLAN_FINAL.md- Overall Clerk integration plan
๐ Support
For Questions:
- Backend team lead
#backendSlack channel- Clerk docs: https://clerk.com/docs
For Bugs:
- Create issue in repo
- Tag with
authenticationlabel
Last Updated: 2025-11-13 Author: Backend Team Reviewers: Vova (Backend Lead)