Full-Stack Developer

Delegating a Full Feature to Claude Code β€” JWT Auth Built in 20 Minutes

Full feature: 4-8 hours β†’ 20 minutes, 94% coverageEngineering & DevOps5 min read

Key Takeaway

I told an AI coding agent to build JWT authentication for our Django API β€” models, views, serializers, middleware, and tests. 20 minutes later: complete working feature, 15 test cases, 94% coverage. Nico reviewed it, flagged 2 minor issues, agent fixed them. Total: 30 minutes.

The Problem

Building features from scratch is slow. Not because writing code is hard β€” because writing complete code is tedious.

JWT authentication in Django. I've built it before. You've probably built it before. Models for users and tokens. Serializers for registration, login, token refresh. Views. URL routing. Middleware for token validation. Settings configuration. Tests for every endpoint β€” happy path, expired token, invalid token, missing token, wrong permissions.

It's 4-8 hours of work. Not because it's complex β€” because it's thorough. The auth logic is maybe 30 minutes. The serializer boilerplate is an hour. The test cases are 2-3 hours. Edge cases, error messages, documentation β€” another hour.

Every hour of that work follows patterns I already know. I'm not solving problems. I'm typing patterns.

The Solution

Claude Code, orchestrated through Mr.Chief's ACP (Agent Communication Protocol), builds the entire feature from a single task description. Thom delegates the work, Claude Code generates the implementation, Nico reviews it, and the agent fixes any issues. I'm involved for 30 seconds β€” the time it takes to type the request.

The Process

The request is simple:

View details
Me: @thom build a REST API for user authentication with JWT β€”
    Django, include tests. Project: eskimoai-api.

Thom delegates to Claude Code via ACP:

yamlShow code
# ACP task delegation
task:
  type: feature_implementation
  project: eskimoai-api
  framework: django
  description: |
    Build complete JWT authentication:
    - User model (extend AbstractUser if needed)
    - Registration endpoint (email, password, name)
    - Login endpoint (returns access + refresh tokens)
    - Token refresh endpoint
    - Token validation middleware
    - User profile endpoint (authenticated)
    - Password reset flow
    - Comprehensive test suite
  constraints:
    - Use djangorestframework-simplejwt
    - Follow existing project patterns (check existing views/serializers)
    - Include docstrings and type hints
    - Tests must cover: happy path, auth failures, edge cases
    - Minimum 90% coverage for new code

Claude Code reads the existing project structure, understands the patterns, and generates:

Models (accounts/models.py):

pythonShow code
from django.contrib.auth.models import AbstractUser
from django.db import models


class User(AbstractUser):
    """Extended user model with email as primary identifier."""
    email = models.EmailField(unique=True)
    name = models.CharField(max_length=255)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['name', 'username']

    class Meta:
        db_table = 'accounts_user'

Serializers (accounts/serializers.py):

pythonShow code
from rest_framework import serializers
from django.contrib.auth import get_user_model
from django.contrib.auth.password_validation import validate_password

User = get_user_model()


class RegisterSerializer(serializers.ModelSerializer):
    password = serializers.CharField(
        write_only=True, validators=[validate_password]
    )
    password_confirm = serializers.CharField(write_only=True)

    class Meta:
        model = User
        fields = ['email', 'name', 'password', 'password_confirm']

    def validate(self, attrs):
        if attrs['password'] != attrs.pop('password_confirm'):
            raise serializers.ValidationError(
                {"password_confirm": "Passwords don't match."}
            )
        return attrs

    def create(self, validated_data):
        return User.objects.create_user(
            username=validated_data['email'],
            **validated_data
        )

Middleware (accounts/middleware.py):

pythonShow code
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.exceptions import InvalidToken, TokenError
from django.http import JsonResponse


class JWTAuthMiddleware:
    """Validate JWT on protected endpoints."""

    EXEMPT_PATHS = [
        '/api/auth/register/',
        '/api/auth/login/',
        '/api/auth/token/refresh/',
        '/health/',
    ]

    def __init__(self, get_response):
        self.get_response = get_response
        self.jwt_auth = JWTAuthentication()

    def __call__(self, request):
        if any(request.path.startswith(p) for p in self.EXEMPT_PATHS):
            return self.get_response(request)

        try:
            auth_result = self.jwt_auth.authenticate(request)
            if auth_result:
                request.user, request.auth = auth_result
        except (InvalidToken, TokenError) as e:
            return JsonResponse(
                {"error": "Invalid or expired token.", "detail": str(e)},
                status=401
            )

        return self.get_response(request)

Tests (accounts/tests/test_auth.py) β€” 15 test cases:

pythonShow code
class TestRegistration(APITestCase):
    def test_register_success(self): ...
    def test_register_duplicate_email(self): ...
    def test_register_weak_password(self): ...
    def test_register_password_mismatch(self): ...
    def test_register_missing_fields(self): ...

class TestLogin(APITestCase):
    def test_login_success_returns_tokens(self): ...
    def test_login_wrong_password(self): ...
    def test_login_nonexistent_user(self): ...
    def test_login_inactive_user(self): ...

class TestTokenRefresh(APITestCase):
    def test_refresh_valid_token(self): ...
    def test_refresh_expired_token(self): ...
    def test_refresh_invalid_token(self): ...

class TestProtectedEndpoints(APITestCase):
    def test_profile_with_valid_token(self): ...
    def test_profile_without_token_returns_401(self): ...
    def test_profile_with_expired_token(self): ...

The full output: 8 files, ~450 lines of production code, ~350 lines of tests. Generated in 18 minutes.

Then Nico reviews:

View details
πŸ” Code Review: JWT Authentication (Nico)

Overall: βœ… Good implementation. Clean patterns. 2 minor issues.

Issue 1 (LOW): middleware.py line 28
  EXEMPT_PATHS uses startswith() β€” "/health-check/" would
  also be exempted. Use exact match or regex.

Issue 2 (LOW): serializers.py line 19
  create_user() doesn't explicitly set is_active=True.
  Default is True, but explicit is better for auth code.

Verdict: Approve after fixes.

The agent fixes both issues in 2 minutes. Nico re-reviews. Approved.

View details
Thom: βœ… JWT Authentication β€” complete
  πŸ“ 8 files created/modified
  πŸ§ͺ 15 tests β€” all passing
  πŸ“Š Coverage: 94%
  βœ… Code review: Approved by Nico

  Ready for merge. Create MR?

The Results

MetricManual DevelopmentAgent + ReviewImprovement
Implementation time4-8 hours20 minutes92% faster
Test writing time2-3 hoursIncluded (0 extra)Eliminated
Test coverage60-80% (typical)94%+14-34%
Code review issues3-5 per first review2 minorFewer iterations
Total time to merge-ready1-2 days30 minutes95% faster
Developer cost$200-400 (at contractor rates)~$0.50 (API cost)99.7% cheaper

Try It Yourself

Mr.Chief's ACP protocol delegates tasks to Claude Code with project context. The key is the task description: be specific about what you want (endpoints, patterns, test coverage), and point to existing code for style consistency. Always route through a code review agent β€” AI-generated code is good, but not infallible. The 10-minute review catches what the 20-minute generation misses.


I didn't write a single line. I described what I needed, reviewed the result, and shipped it. That's the future of feature development.

Claude CodeDjangoJWTAI AgentsTest Coverage

Want results like these?

Start free with your own AI team. No credit card required.

Delegating a Full Feature to Claude Code β€” JWT Auth Built in 20 Minutes β€” Mr.Chief