Files
query-orchestration/internal/cognitoauth
Jay Brown dae7bd4cc6 Merged in feature/textExtractionsPart4 (pull request #197)
implement GET /identity

* GET /identity
2025-12-16 04:26:06 +00:00
..
2025-07-21 13:11:27 -07:00
2025-08-01 13:35:29 -07:00
2025-07-17 11:43:21 -07:00
2025-04-08 16:27:06 -07:00
2025-07-23 09:47:01 -07:00
2025-07-17 11:43:21 -07:00
2025-07-17 11:43:21 -07:00

Cognito Auth

Package Documentation

For comprehensive package-level documentation, see README.md.

Cognito Auth

A reusable Go package for AWS Cognito authentication and Permit.io authorization with Echo framework using PKCE flow.

Features

  • Complete OAuth 2.0 PKCE flow for AWS Cognito authentication
  • Automatic JWT token refresh for seamless user experience
  • JWT token validation and verification
  • Dynamic route-based authorization using Permit.io Policy Decision Point (PDP)
  • Automatic resource mapping from HTTP routes to Permit.io resources
  • HTTP method to action mapping for authorization checks
  • Middleware for token handling and permission enforcement
  • Cookie-based token storage with refresh token support
  • Default home page with authentication status
  • Simple integration with Echo framework

Feature flag for Auth

To disable all of these features for all endpoints you must set DISABLE_AUTH=true in your environment.

Package details

For a detailed explanation of all of the files in this package see the summary file.

Configuration

The package reads configuration from environment variables:

AWS Cognito (Authentication)

  • COGNITO_CLIENT_ID: Your AWS Cognito App Client ID
  • COGNITO_CLIENT_SECRET: Your AWS Cognito App Client Secret
  • COGNITO_USER_POOL_ID: Your AWS Cognito User Pool ID
  • COGNITO_DOMAIN: Your AWS Cognito domain
  • AWS_REGION: AWS region where your Cognito User Pool is located (default: us-east-2)
  • COGNITO_REGION: AWS region for Cognito service (default: us-east-2)

Permit.io (Authorization)

  • PERMIT_IO_API_KEY: Your Permit.io API key for accessing the PDP
  • PERMIT_IO_PDP_URL: URL of your Permit.io Policy Decision Point (default: http://localhost:7766)

General

  • DEBUG: Set to "true" for debug logging

Environment Variable Loading

Environment variables are loaded in the following hierarchy (first found wins):

  1. System environment variables
  2. devbox.json "env" section - Development environment defaults
  3. .env file - Local overrides and secrets (loaded via env_from in devbox.json)

Feature flags

Disable Authentication

You can disable all of the auth features with the following environment variable: DISABLE_AUTH=true

Test Mode (JWT Validation Bypass)

For testing purposes only, you can bypass JWT signature validation with: NO_JWT_VALIDATION=true

Warning: This flag should ONLY be used in testing environments. When enabled:

  • JWT signature validation is bypassed
  • Token expiration is ignored
  • Unsigned test tokens can be used for authorization testing
  • The system logs warnings when this mode is active

This allows testing of authorization logic without requiring valid AWS Cognito tokens.

Middleware Architecture

Middleware Chain Order

The authentication system uses two middleware components that must be registered in the correct order:

  1. JWTAuthMiddleware (First)

    • Converts JWT tokens from cookies to Authorization headers
    • Skips processing for public paths (/login, /logout, /home, /swagger/*)
    • Handles logout by clearing auth cookies
    • Does NOT perform authentication - only prepares the request
    • Passes requests to the next middleware in the chain
  2. TokenValidationMiddleware (Second)

    • Performs authentication: Validates JWT tokens and extracts user claims
    • Automatic token refresh: Detects expired tokens and refreshes them transparently
    • Performs authorization: Checks permissions via Permit.io PDP
    • Returns JSON error responses for authentication/authorization failures
    • Sets user context for subsequent handlers

Why order matters: The first middleware prepares the request for token validation, while the second middleware performs the actual authentication and authorization enforcement.

Automatic Token Refresh

The system provides seamless token refresh functionality to maintain user sessions without interruption:

How It Works

  1. Proactive Refresh: Tokens are automatically refreshed 5 minutes before expiration
  2. Transparent Process: Users never experience session interruptions
  3. Dual Cookie Storage: Both access tokens (short-lived) and refresh tokens (30-day) are stored as HTTP-only cookies
  4. Fallback Handling: When refresh fails, users are gracefully redirected to login

Token Storage Strategy

  • Access Token Cookie: auth_token - expires with the JWT token (typically 1 hour)
  • Refresh Token Cookie: refresh_token - longer expiration (30 days)
  • Security: Both cookies are HTTP-only and secure in production environments

Refresh Process Flow

  1. Token Expiration Check: Middleware checks if access token expires within 5 minutes
  2. Refresh Token Retrieval: Extracts refresh token from secure cookie
  3. Token Exchange: Uses OAuth 2.0 refresh grant to get new tokens from Cognito
  4. Cookie Update: Sets new access and refresh tokens in cookies
  5. Request Continuation: Processes the original request with fresh tokens

Error Handling

  • Missing Refresh Token: Redirects to login with "Session expired" message
  • Invalid Refresh Token: Clears cookies and redirects to login
  • Cognito Service Issues: Logs errors and falls back to re-authentication
  • Network Errors: Retries on next request, maintains user experience

Session Expiration Scenarios

  • Access Token Expired + Valid Refresh Token: Automatic refresh (transparent)
  • Both Tokens Expired: Redirect to login with clear messaging
  • Refresh Token Invalid: Clear cookies and redirect to login
  • User Logout: Both cookies are immediately cleared

Client Caching

The system implements efficient client caching for Permit.io connections:

  • Singleton pattern: One client instance per unique (apiKey, pdpURL) combination
  • Thread-safe: Uses sync.RWMutex for concurrent access protection
  • Automatic cleanup: Clients are reused across requests to the same PDP

Startup Validation

The system performs comprehensive validation at startup to ensure proper configuration:

Configuration Validation (ValidatePermitIOConfiguration)

  1. Skip validation: If DISABLE_AUTH=true (case-insensitive)
  2. PDP connectivity test: HTTP health check to /healthy endpoint
  3. API key validation: Quick permission check to verify API key validity
  4. Error handling: Application halts if validation fails

Validation Process

  • PDP URL: Uses PERMIT_IO_PDP_URL or defaults to http://localhost:7766
  • API key: Requires PERMIT_IO_API_KEY environment variable
  • Health check: Tests connectivity with 10-second timeout
  • Permission test: Validates API key with dummy authorization request

Error Handling

Authentication vs Authorization Errors

Authentication Errors (No valid JWT token):

{
  "error": "Authorization required",
  "message": "This endpoint requires authentication. Please obtain a valid JWT token.",
  "login_url": "/login"
}
  • Status Code: 401 Unauthorized
  • When: Missing, invalid, or malformed JWT tokens

Session Expiration Errors (Token refresh failed):

{
  "error": "Session expired",
  "message": "Your session has expired. Please log in again.",
  "login_url": "/login"
}
  • Status Code: 401 Unauthorized
  • When: Access token expired and refresh token is missing or invalid

Authorization Errors (Valid token, insufficient permissions):

{
  "error": "Insufficient permissions",
  "message": "Access denied by authorization service",
  "user": "user-subject-id",
  "resource": "document",
  "action": "get"
}
  • Status Code: 403 Forbidden
  • When: Valid JWT but Permit.io denies access

Error Response Format

All error responses return JSON with consistent structure:

  • error: Brief error identifier
  • message: Human-readable description
  • Additional fields: Context-specific information (user, resource, action, etc.)

Testing

Test Mode JWT Handling

When NO_JWT_VALIDATION=true is set:

  • Pretty-printed JWTs: Handles formatted JSON payloads with newlines/tabs
  • 2-part JWT format: Automatically handles header.payload format (adds empty signature)
  • Unsigned tokens: Bypasses signature verification for testing
  • Flexible parsing: More permissive token parsing for development

Running Integration Tests

# Requires local PDP and API key
task test:permitio

# Or directly with verbose output
go test -tags=permitio -parallel 2 -count=1 -v ./internal/cognitoauth

Test Environment Requirements

  • Local PDP: Permit.io Policy Decision Point running on port 7766
  • API key: Valid PERMIT_IO_API_KEY in environment or .env file
  • Test data: Configured users, resources, and permissions in Permit.io

Authorization System

Permit.io Integration

This package uses Permit.io for dynamic authorization instead of static group-based permissions. The system:

  1. Authenticates users via AWS Cognito (JWT tokens)
  2. Authorizes requests via Permit.io Policy Decision Point (PDP)
  3. Maps HTTP routes to Permit.io resources automatically
  4. Converts HTTP methods to Permit.io actions

Automatic Resource Mapping

The system automatically maps HTTP routes to Permit.io resources using the following logic:

  • Extracts the first path segment from the route as the resource name
  • Removes leading slashes to match Permit.io naming conventions
  • Ignores path parameters and nested segments

Examples:

  • /documentdocument
  • /document/123document
  • /document/foo/123document
  • /api/v1/usersapi
  • /query-endpoint/runquery-endpoint

HTTP Method to Action Mapping

HTTP methods are mapped to lowercase Permit.io actions:

  • GETget
  • POSTpost
  • PUTput
  • PATCHpatch
  • DELETEdelete

Authorization Flow

  1. User makes request to protected endpoint
  2. Middleware extracts JWT token and validates it with Cognito
  3. User subject (sub claim) is extracted from JWT
  4. Route is mapped to resource name (first path segment)
  5. HTTP method is mapped to action name
  6. Permit.io PDP is queried: CheckPermission(user, action, resource)
  7. Request is allowed/denied based on PDP response