implement GET /identity * GET /identity
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 IDCOGNITO_CLIENT_SECRET: Your AWS Cognito App Client SecretCOGNITO_USER_POOL_ID: Your AWS Cognito User Pool IDCOGNITO_DOMAIN: Your AWS Cognito domainAWS_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 PDPPERMIT_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):
- System environment variables
- devbox.json "env" section - Development environment defaults
- .env file - Local overrides and secrets (loaded via
env_fromin 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:
-
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
-
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
- Proactive Refresh: Tokens are automatically refreshed 5 minutes before expiration
- Transparent Process: Users never experience session interruptions
- Dual Cookie Storage: Both access tokens (short-lived) and refresh tokens (30-day) are stored as HTTP-only cookies
- 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
- Token Expiration Check: Middleware checks if access token expires within 5 minutes
- Refresh Token Retrieval: Extracts refresh token from secure cookie
- Token Exchange: Uses OAuth 2.0 refresh grant to get new tokens from Cognito
- Cookie Update: Sets new access and refresh tokens in cookies
- 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.RWMutexfor 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)
- Skip validation: If
DISABLE_AUTH=true(case-insensitive) - PDP connectivity test: HTTP health check to
/healthyendpoint - API key validation: Quick permission check to verify API key validity
- Error handling: Application halts if validation fails
Validation Process
- PDP URL: Uses
PERMIT_IO_PDP_URLor defaults tohttp://localhost:7766 - API key: Requires
PERMIT_IO_API_KEYenvironment 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.payloadformat (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_KEYin environment or.envfile - 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:
- Authenticates users via AWS Cognito (JWT tokens)
- Authorizes requests via Permit.io Policy Decision Point (PDP)
- Maps HTTP routes to Permit.io resources automatically
- 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:
/document→document/document/123→document/document/foo/123→document/api/v1/users→api/query-endpoint/run→query-endpoint
HTTP Method to Action Mapping
HTTP methods are mapped to lowercase Permit.io actions:
GET→getPOST→postPUT→putPATCH→patchDELETE→delete
Authorization Flow
- User makes request to protected endpoint
- Middleware extracts JWT token and validates it with Cognito
- User subject (
subclaim) is extracted from JWT - Route is mapped to resource name (first path segment)
- HTTP method is mapped to action name
- Permit.io PDP is queried:
CheckPermission(user, action, resource) - Request is allowed/denied based on PDP response