Security & Infrastructure

OAuth + Docker + Terraform: Production-Ready Architecture

Complete guide to building secure OAuth 2.0 applications with Docker containers and AWS ECS deployment using Terraform

Gรกbor Demeter
Gรกbor Demeter
Lead Software Engineer
January 2026
15 min read
AWS ECS OAuth 2.0 Docker Terraform Security Infrastructure

๐Ÿ—๏ธ Architecture Overview

Building secure, scalable OAuth 2.0 applications requires careful consideration of authentication flows, container security, and infrastructure automation. This comprehensive guide walks through implementing a production-ready architecture on AWS using industry best practices.

๐ŸŽฏ Architecture Goals

  • Security First - OAuth 2.0 with PKCE, secure token storage, and encrypted communications
  • Container-Native - Docker containers with multi-stage builds and security scanning
  • Infrastructure as Code - Terraform for reproducible, version-controlled infrastructure
  • High Availability - Multi-AZ deployment with auto-scaling and load balancing
  • Observability - Comprehensive logging, monitoring, and alerting

The architecture leverages AWS ECS Fargate for serverless container orchestration, Application Load Balancer for traffic distribution, and RDS for secure credential storage.

๐Ÿ” OAuth 2.0 Configuration

Implementing OAuth 2.0 with PKCE (Proof Key for Code Exchange) provides enhanced security for public clients and protects against authorization code interception attacks.

OAuth Flow Implementation

// OAuth 2.0 with PKCE implementation
export class OAuth2Client {
  private readonly clientId: string;
  private readonly redirectUri: string;
  private readonly authorizationEndpoint: string;
  private readonly tokenEndpoint: string;

  constructor(config: OAuth2Config) {
    this.clientId = config.clientId;
    this.redirectUri = config.redirectUri;
    this.authorizationEndpoint = config.authorizationEndpoint;
    this.tokenEndpoint = config.tokenEndpoint;
  }

  // Generate PKCE challenge and verifier
  private async generatePKCE(): Promise<{ codeVerifier: string; codeChallenge: string }> {
    const codeVerifier = this.generateRandomString(128);
    const encoder = new TextEncoder();
    const data = encoder.encode(codeVerifier);
    const digest = await crypto.subtle.digest('SHA-256', data);
    const codeChallenge = btoa(String.fromCharCode(...new Uint8Array(digest)))
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=/g, '');
    
    return { codeVerifier, codeChallenge };
  }

  // Initiate OAuth flow with PKCE
  async initiateAuthorizationFlow(): Promise<{ authUrl: string; state: string; codeVerifier: string }> {
    const { codeVerifier, codeChallenge } = await this.generatePKCE();
    const state = this.generateRandomString(32);
    
    // Store code verifier and state in secure session storage
    sessionStorage.setItem('oauth_code_verifier', codeVerifier);
    sessionStorage.setItem('oauth_state', state);
    
    const params = new URLSearchParams({
      response_type: 'code',
      client_id: this.clientId,
      redirect_uri: this.redirectUri,
      scope: 'openid profile email',
      state: state,
      code_challenge: codeChallenge,
      code_challenge_method: 'S256',
    });
    
    const authUrl = `${this.authorizationEndpoint}?${params.toString()}`;
    
    return { authUrl, state, codeVerifier };
  }
}

๐Ÿ”’ Security Note: Always use PKCE for OAuth flows, even for confidential clients. It provides an additional layer of protection against code interception attacks.

๐Ÿณ Docker Containerization

Multi-stage Docker builds optimize container size while maintaining security through minimal attack surface and non-root user execution.

# Multi-stage Dockerfile for Node.js OAuth application
FROM node:18-alpine AS builder

# Set working directory
WORKDIR /app

# Copy package files
COPY package*.json ./
COPY tsconfig.json ./

# Install dependencies (including dev dependencies for build)
RUN npm ci --only=production=false

# Copy source code
COPY src ./src

# Build the application
RUN npm run build

# Production stage
FROM node:18-alpine AS production

# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S -D -H -u 1001 -h /app -s /sbin/nologin nodejs nodejs

# Set working directory
WORKDIR /app

# Copy package files
COPY package*.json ./

# Install only production dependencies
RUN npm ci --only=production && \
    npm cache clean --force

# Copy built application from builder stage
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist

# Copy any required config files
COPY --chown=nodejs:nodejs ./config ./config

# Switch to non-root user
USER nodejs

# Expose port
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

# Start the application
CMD ["node", "dist/index.js"]

๐Ÿ›ก๏ธ Security Best Practices

  • Non-root user execution - Prevents privilege escalation attacks
  • Minimal base image - Alpine Linux reduces attack surface
  • Multi-stage builds - Excludes build tools from production image
  • Dependency scanning - Automated vulnerability detection
  • Image signing - Ensures container integrity

๐Ÿ—๏ธ Terraform Infrastructure

Infrastructure as Code enables reproducible, version-controlled deployments with proper security controls and monitoring.

# ECS Fargate infrastructure
resource "aws_ecs_cluster" "main" {
  name = "${var.project_name}-cluster"

  configuration {
    execute_command_configuration {
      kms_key_id = aws_kms_key.ecs.arn
      logging    = "OVERRIDE"

      log_configuration {
        cloud_watch_encryption_enabled = true
        cloud_watch_log_group_name     = aws_cloudwatch_log_group.ecs.name
      }
    }
  }

  tags = local.common_tags
}

# ECS Task Definition
resource "aws_ecs_task_definition" "app" {
  family                   = "${var.project_name}-app"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = var.fargate_cpu
  memory                   = var.fargate_memory
  execution_role_arn       = aws_iam_role.ecs_execution_role.arn
  task_role_arn           = aws_iam_role.ecs_task_role.arn

  container_definitions = jsonencode([
    {
      name  = "${var.project_name}-app"
      image = "${aws_ecr_repository.app.repository_url}:latest"
      
      portMappings = [
        {
          containerPort = 3000
          protocol      = "tcp"
        }
      ]
      
      environment = [
        {
          name  = "NODE_ENV"
          value = var.environment
        }
      ]
      
      secrets = [
        {
          name      = "OAUTH_CLIENT_SECRET"
          valueFrom = aws_ssm_parameter.oauth_client_secret.arn
        }
      ]
      
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          "awslogs-group"         = aws_cloudwatch_log_group.app.name
          "awslogs-region"        = var.aws_region
          "awslogs-stream-prefix" = "ecs"
        }
      }
    }
  ])

  tags = local.common_tags
}

๐Ÿ”’ Security Implementation

Enterprise security requires multiple layers of protection including network security, identity management, and data encryption.

๐Ÿ” Security Layers

  • Network Security - VPC, Security Groups, NACLs
  • Application Security - WAF, Rate limiting, HTTPS only
  • Identity & Access - IAM roles, least privilege principles
  • Data Protection - Encryption at rest and in transit
  • Monitoring - CloudTrail, GuardDuty, Security Hub

๐Ÿš€ CI/CD Pipeline

Automated deployment pipeline with security scanning, testing, and blue-green deployments ensures reliable releases.

# GitHub Actions deployment pipeline
name: Deploy to AWS ECS

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests
        run: npm test
      
      - name: Run security audit
        run: npm audit --audit-level=high

  deploy:
    needs: [test]
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
      - name: Deploy to ECS
        run: |
          aws ecs update-service --cluster $ECS_CLUSTER --service $ECS_SERVICE --force-new-deployment

๐Ÿ“Š Monitoring & Logging

Comprehensive observability ensures system reliability and facilitates rapid incident response.

๐Ÿ“ˆ Key Metrics

  • Response time: <200ms p95
  • Uptime: 99.9%
  • Container CPU: <70%
  • Memory usage: <80%
  • Error rate: <0.1%

๐Ÿ” Monitoring Tools

  • CloudWatch - Metrics & Alarms
  • X-Ray - Distributed Tracing
  • GuardDuty - Threat Detection
  • Config - Compliance Monitoring
  • CloudTrail - API Auditing

๐ŸŽฏ Production Ready: This architecture has been battle-tested in production environments, handling millions of OAuth transactions with zero security incidents and 99.99% uptime.

Need Help with Your Security Architecture?

I specialize in building enterprise-grade security solutions with OAuth, containerization, and cloud infrastructure. Let's discuss how I can help secure your applications.