GitHub: GoLang/auth

Authentication & Authorization Proof of Concept

This project demonstrates a microservices architecture with decoupled authentication and authorization using JWT tokens.

Architecture

graph TB
    Client[Client]
    
    subgraph Traefik["Traefik Load Balancer :8000"]
        TLB["SINGLE ENTRY POINT<br/>• Rate Limiting: 10 req/sec avg, burst of 20<br/>• Routes: /login, /validate-header → auth-server<br/>• Routes: /api/* → api-gateway<br/>• Dashboard: http://localhost:8080/dashboard/"]
    end
    
    AuthServer["Auth Server<br/>(Internal)<br/>• Issues JWT<br/>• Validates tokens<br/>• User roles"]
    
    subgraph Gateway["API Gateway (2 replicas)"]
        GW1["Gateway 1<br/>• Validates JWT<br/>• Adds X-Username<br/>• Adds X-Role<br/>• Proxies to app"]
        GW2["Gateway 2"]
    end
    
    subgraph Apps["App Service (2 replicas)"]
        App1["App 1<br/>• Business logic<br/>• Authorization<br/>• Trusts gateway"]
        App2["App 2<br/>• Business logic<br/>• Authorization<br/>• Trusts gateway"]
    end
    
    Client -->|All traffic| TLB
    TLB -->|/login, /validate| AuthServer
    TLB -->|/api/*| GW1
    TLB -->|/api/*| GW2
    GW1 -->|Docker DNS| App1
    GW1 -->|Docker DNS| App2
    GW2 -->|Docker DNS| App1
    GW2 -->|Docker DNS| App2

Components

  1. Traefik Load Balancer (:8000 - ONLY exposed port)
    • Single entry point for ALL client traffic including authentication
    • Routes /login and /validate-header to auth-server
    • Routes /api/* to api-gateway replicas
    • Rate limiting: 10 req/sec average, burst of 20 (DDoS protection)
    • Round-robin load balancing
    • Dashboard at http://localhost:8080/dashboard/
  2. Auth Server (internal only - not directly accessible)
    • Issues JWT tokens upon successful login
    • Validates JWT tokens for the gateway
    • Manages user credentials and roles
    • Accessed through Traefik routing
    • Decoupled from business logic
  3. API Gateway (2 replicas)
    • Authentication boundary - validates JWT once per request
    • Calls auth server to validate tokens
    • Adds X-Username and X-Role headers to proxied requests
    • Forwards requests to app services (no JWT forwarded)
  4. App Service (2 replicas)
    • Business logic services (cowsay implementation)
    • Authorization with role-based access control (RBAC)
    • Trusts X-Username and X-Role headers from gateway
    • No direct JWT validation (trusts the gateway)
    • Horizontally scalable with Docker Compose replicas

Key Features

Rate Limiting: Traefik blocks DDoS attacks with 10 req/sec average, burst of 20
Load Balancing: Round-robin across 2 gateway + 2 app replicas
Decoupled Auth: Authentication at gateway, authorization at app layer
JWT-based: Stateless authentication using JWT tokens
Role-Based Access Control (RBAC): Users have roles (user, admin) with different permissions
High Availability: Multiple replicas ensure no single point of failure
Monitoring: Traefik dashboard shows service health and routing
Horizontal Scaling: App service uses Docker Compose replicas for easy scaling
Load Balancing: Round-robin distribution across multiple app instances
Health Checks: All services expose health endpoints
Containerized: Fully containerized with Docker Compose

Quick Start

Prerequisites

  • Docker and Docker Compose installed
  • curl or any HTTP client

1. Start all services

cd auth
docker-compose up --build

Wait for all services to be healthy (~30 seconds).

2. Login to get JWT token

# Login as alice
curl -X POST http://localhost:8000/login \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"password123"}'

Response:

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_at": "2025-12-30T15:30:00Z"
}

Save the token for subsequent requests.

3. Call the protected API

# Set your token
TOKEN="<your-token-from-login>"

# Make cowsay request
curl -X POST http://localhost:8000/api/v1/cowsay \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"message":"Hello from Go!"}'

Response:

{
  "cow": " ---------------- \n< Hello from Go! >\n ---------------- \n        \\   ^__^\n         \\  (oo)\\_______\n            (__)\\       )\\/\\\n                ||----w |\n                ||     ||\n",
  "message": "Hello from Go!",
  "service": "app1",
  "user": "alice"
}

Notice the service field alternates between app1 and app2 due to load balancing!

Available Users

The auth server has the following test users with different roles:

Username Password Role Permissions
alice password123 user Can access /api/v1/cowsay
bob password456 user Can access /api/v1/cowsay
admin admin123 admin Can access all endpoints including /api/v1/admin

Role-Based Access Control (RBAC)

The system implements role-based access control where different users have different permissions:

Testing RBAC

# Run the comprehensive RBAC test
./test-rbac.sh

Manual RBAC Testing

# Login as admin
ADMIN_TOKEN=$(curl -s -X POST http://localhost:8000/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin123"}' | jq -r '.token')

# Access admin endpoint (should work)
curl -X GET http://localhost:8000/api/v1/admin \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq '.'

# Login as regular user
USER_TOKEN=$(curl -s -X POST http://localhost:8000/login \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"password123"}' | jq -r '.token')

# Try to access admin endpoint (should fail with 403)
curl -X GET http://localhost:8000/api/v1/admin \
  -H "Authorization: Bearer $USER_TOKEN"

API Endpoints

API Gateway (:8000)

Endpoint Method Auth Required Role Required Description
/login POST No - Get JWT token
/api/v1/cowsay POST Yes any Cowsay service (load balanced)
/api/v1/admin GET Yes admin Admin panel (admin only)
/health GET No - Health check
/info GET No - Gateway information

Auth Server (:8080) - Internal

Endpoint Method Description
/login POST Issue JWT token
/validate POST Validate token (body)
/validate-header GET Validate token (header)
/health GET Health check

App1/App2 (:8081, :8082) - Internal

Endpoint Method Auth Required Role Required Description
/api/v1/cowsay POST Yes any Generate cowsay
/api/v1/admin GET Yes admin Admin panel
/health GET No - Health check
/info GET No - Service info

Testing Script

#!/bin/bash

echo "=== Auth & Authorization PoC Test ==="
echo ""

# 1. Login
echo "1. Logging in as alice..."
LOGIN_RESPONSE=$(curl -s -X POST http://localhost:8000/login \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","password":"password123"}')

TOKEN=$(echo $LOGIN_RESPONSE | grep -o '"token":"[^"]*' | cut -d'"' -f4)

if [ -z "$TOKEN" ]; then
  echo "❌ Failed to get token"
  exit 1
fi

echo "✅ Token obtained: ${TOKEN:0:50}..."
echo ""

# 2. Test without auth (should fail)
echo "2. Testing without authentication..."
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST http://localhost:8000/api/v1/cowsay \
  -H "Content-Type: application/json" \
  -d '{"message":"Test"}')
  
STATUS=$(echo "$RESPONSE" | tail -n1)
if [ "$STATUS" = "401" ]; then
  echo "✅ Correctly rejected (401 Unauthorized)"
else
  echo "❌ Should have been rejected"
fi
echo ""

# 3. Test with valid auth
echo "3. Testing with valid authentication..."
for i in {1..4}; do
  echo "   Request $i:"
  curl -s -X POST http://localhost:8000/api/v1/cowsay \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"message\":\"Request $i\"}" | grep -o '"service":"[^"]*' | cut -d'"' -f4
done
echo ""
echo "✅ Notice the load balancing between app1 and app2!"
echo ""

# 4. Test with invalid token
echo "4. Testing with invalid token..."
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST http://localhost:8000/api/v1/cowsay \
  -H "Authorization: Bearer invalid-token-here" \
  -H "Content-Type: application/json" \
  -d '{"message":"Test"}')
  
STATUS=$(echo "$RESPONSE" | tail -n1)
if [ "$STATUS" = "401" ]; then
  echo "✅ Correctly rejected (401 Unauthorized)"
else
  echo "❌ Should have been rejected"
fi

echo ""
echo "=== All tests completed ==="

Save this as test.sh, make it executable (chmod +x test.sh), and run it!

Architecture Decisions

Why API Gateway?

  1. Single Entry Point: Clients only need to know one URL
  2. Authentication at the Edge: Validate tokens before routing to services
  3. Load Balancing: Distribute traffic across multiple service instances
  4. Service Discovery: Backend services can scale independently

Why Separate Auth Server?

  1. Separation of Concerns: Auth logic is isolated from business logic
  2. Reusability: Multiple services can use the same auth server
  3. Scalability: Can scale auth independently based on demand
  4. Security: Centralized security management

Security Considerations

⚠️ This is a PoC - Not production ready!

For production, consider:

  • Use environment variables for JWT secret (not hardcoded)
  • Implement token refresh mechanism
  • Use HTTPS/TLS for all communication
  • Add rate limiting
  • Implement proper logging and monitoring
  • Use a database for user management (not in-memory)
  • Add CORS configuration
  • Implement token revocation/blacklisting
  • Use more sophisticated load balancing (e.g., Nginx, Traefik)

Monitoring

Check service health:

# API Gateway
curl http://localhost:8000/health
curl http://localhost:8000/info

# Auth Server
curl http://localhost:8080/health

# App1
curl http://localhost:8081/health
curl http://localhost:8081/info

# App2
curl http://localhost:8082/health
curl http://localhost:8082/info

Logs

View logs from all services:

docker-compose logs -f

View logs from specific service:

docker-compose logs -f api-gateway
docker-compose logs -f auth-server
docker-compose logs -f app1
docker-compose logs -f app2

Stopping the Services

docker-compose down

Clean up (remove volumes):

docker-compose down -v

Project Structure

auth/
├── docker-compose.yml          # Orchestrates all services
├── README.md                   # This file
├── test.sh                     # Testing script
├── auth-server/
│   ├── main.go                 # JWT issuer & validator
│   ├── go.mod
│   └── Dockerfile
├── api-gateway/
│   ├── main.go                 # Load balancer & auth proxy
│   ├── go.mod
│   └── Dockerfile
├── app1/
│   ├── main.go                 # Cowsay service instance 1
│   ├── go.mod
│   └── Dockerfile
└── app2/
    ├── main.go                 # Cowsay service instance 2
    ├── go.mod
    └── Dockerfile

Further Enhancements

  • Add database for user management
  • Implement token refresh mechanism
  • Add role-based access control (RBAC)
  • Implement circuit breaker pattern
  • Add distributed tracing (OpenTelemetry)
  • Add metrics (Prometheus)
  • Add more sophisticated load balancing algorithms
  • Implement service mesh (Istio, Linkerd)

License

MIT License - Feel free to use this as a learning resource!

Recent changes

  • 2026-01-01 b08ef92 Add Tilt support for auth microservices architecture
  • 2026-01-01 c66d138 Convert text diagrams to Mermaid in all auth documentation
  • 2026-01-01 6aa3dc0 Fix shell test scripts: add -e flag for color support and improve load balancing test
  • 2025-12-31 9f0c855 Update dependencies: Go 1.23.5 and Traefik v3.6.6
  • 2025-12-31 6324ad6 Fix architecture: Traefik as single entry point for ALL traffic
  • 2025-12-31 0744b94 Update documentation to reflect Traefik architecture
  • 2025-12-31 0663a71 Add QUICKSTART.md with comprehensive usage examples
  • 2025-12-31 49a09f5 Add Traefik load balancer with rate limiting for DDoS protection

Categories: experiments, Go

Tags: golang

← Previous · Next →