Auth Return

Architecture & How It Works

Version: 1.0.30

What Is Auth Return?

Auth Return is a centralized authentication service that wraps AWS Cognito to provide isolated, secure authentication for multiple applications. Each app gets its own dedicated Cognito user pool, ensuring complete isolation between applications.

6 Principles of Authentication

Key Features

  • App Isolation: Each app has its own Cognito user pool - users, passwords, and sessions are completely separate
  • Zero Password Storage: Auth Return never sees passwords - credentials go directly to AWS Cognito
  • Stateless Authentication: Uses JWT tokens - no server-side session storage required
  • Automatic Pool Management: User pools are created automatically when you register an app
  • Embedded Components: Drop-in authentication UI components for login/signup
  • API Key Management: Centralized API key system for service-to-service authentication

Authentication Flow

Here's the complete flow from user clicking "Sign In" to making authenticated API requests:

User                    Your App                 Auth Return              AWS Cognito
  |                           |                           |                           |
  |-- Click "Sign In" ------->|                           |                           |
  |                           |-- Fetch Cognito config -->|                           |
  |                           |<-- {poolId, clientId} ----|                           |
  |                           |                           |                           |
  |<-- Show login modal ------|                           |                           |
  |                           |                           |                           |
  |-- Enter credentials ----->|---------------------------------------->|
  |                           |                           |             |
  |                           |                    (SRP protocol - password never sent)
  |                           |                           |             |
  |<---------------------------------------- JWT tokens ----------------|
  |                           |                           |                           |
  |-- Store in localStorage ->|                           |                           |
  |                           |                           |                           |
  |-- API request + token --->|                           |                           |
  |                           |-- Verify signature (no network call needed) -------->|
  |                           |<-- Valid or Expired                                   |

Key Points

1. Configuration Fetch: Your app requests Cognito config (pool ID, client ID) from Auth Return.
2. Direct Authentication: User credentials go directly to AWS Cognito via SRP protocol - Auth Return never sees passwords.
3. Token Storage: JWTs are stored client-side in localStorage.
4. Stateless Verification: Your backend verifies tokens using Cognito's public keys - no database lookup needed.

App Isolation

Each App Has Its Own User Pool

When you register an app, Auth Return automatically creates a dedicated Cognito user pool. Users who sign up for App A are completely separate from App B - different passwords, different user IDs, different sessions. A credential leak in one app cannot affect others.

Why This Matters

If apps shared a user pool, the same email would have the same password everywhere. Compromise one app, compromise them all.

With separate pools: same email, different passwords, different user IDs. Each app is a security boundary. Even if an attacker gains access to one app's user database, they cannot use those credentials to access other apps.

How JWTs Work

After login, Cognito returns a JWT (JSON Web Token). This is a signed, self-contained credential that contains user information and can be verified without database lookups.

JWT Structure

Three parts separated by dots: header.payload.signature

{
  "header": {
    "alg": "RS256",           // Signing algorithm
    "kid": "key-id..."        // Which key signed it
  },
  "payload": {
    "sub": "a438f478-9061...", // User ID (unique per pool)
    "email": "user@example.com",
    "aud": "1bb0d4cj0pqb...",  // Client ID (which app)
    "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_xxx",
    "iat": 1767485656,         // Issued at (unix timestamp)
    "exp": 1767489255          // Expires at (1 hour later)
  },
  "signature": "..." // Proves token wasn't tampered with
}

Stateless Authentication

JWTs are stateless - the server doesn't track sessions. On each request:

  1. Client sends token in Authorization: Bearer <token> header
  2. Server verifies signature using Cognito's public key (fetched from JWKS endpoint)
  3. Server checks expiry time from exp field
  4. Server verifies aud matches your app's client ID
  5. If valid, request proceeds - no database lookup required

The token contains everything needed to verify the user. This makes authentication scalable and fast - no session storage, no database queries.

Token Lifecycle

Token Type Lifetime Purpose
ID Token 1 hour Contains user info (email, sub). Used for API requests to identify the user.
Access Token 1 hour Used for Cognito API calls (change password, update profile, etc).
Refresh Token 30 days Used to get new ID/Access tokens without re-login. Stored securely client-side.

Automatic Refresh

The auth component automatically refreshes tokens before they expire. Users stay logged in for up to 30 days without re-entering credentials. After 30 days, they must log in again.

API Key Management

Auth Return provides centralized API key management for service-to-service authentication. Keys are separate from user authentication and are used when services need to verify requests.

Key Features

  • Format: Keys use ar_ prefix followed by 32 bytes of random data
  • Storage: Keys are hashed with bcrypt - only the prefix is stored for identification
  • One-Time Display: Full key is shown only once at creation - store it securely
  • Verification: Public endpoint at POST /api/keys/verify (rate-limited)
  • Revocation: Keys can be revoked immediately - verification will fail after revocation
  • Usage Tracking: Last used timestamp is updated on each successful verification

Verification Flow

Service receives request with X-API-Key header
  ↓
Calls POST /api/keys/verify with the key
  ↓
Auth Return:
  1. Looks up by prefix (fast indexed lookup)
  2. Verifies against bcrypt hash
  3. Checks if revoked
  4. Updates last_used_at
  ↓
Returns { valid: true, app_id, user_id, ... } or { valid: false }

Security Model

Passwords Never Touch Auth Return

Credentials go directly to AWS Cognito via the embedded component. Auth Return only provides configuration (pool ID, client ID). It never sees passwords, password hashes, or any authentication secrets.

SRP Protocol

Cognito uses Secure Remote Password (SRP) protocol. The actual password is never transmitted - only a cryptographic proof. Even if traffic is intercepted, the password cannot be recovered.

Signature Verification

JWTs are signed with Cognito's private key. Your backend verifies using their public JWKS endpoint. Tampered tokens are rejected - the signature won't match.

Token Scoping

Each token contains aud (audience/client ID). Your backend should verify the token was issued for YOUR app, not another. This prevents token reuse across apps.

Rate Limiting

Key verification endpoint is rate-limited (100 requests/minute per IP). Login attempts are also rate-limited to prevent brute force attacks.

HTTPS Only

All communication must use HTTPS. HTTP is only allowed for localhost during development. This ensures tokens and credentials are encrypted in transit.

Why Server-Side Logout Is Hard

The Tradeoff of Stateless Auth

Because JWTs are stateless, once issued, a token is valid until it expires. There's no server-side session to invalidate. This is both a feature (scalability) and a limitation (immediate logout is difficult).

To force logout, you'd need:

Token blacklist Store revoked tokens in DB, check every request. Defeats stateless benefit, requires database lookups.
Short expiry 1-5 minute tokens. Users re-authenticate constantly. Bad UX, defeats refresh token purpose.
Cognito GlobalSignOut Invalidates refresh tokens, but existing access tokens work until expiry (1 hour max).

Practical reality: A compromised token works for up to 1 hour. Design your security model accordingly. For sensitive operations, require re-authentication or use shorter-lived tokens.

Architecture Components

Frontend Components

  • Auth Component: Embedded login/signup UI (auth_component.js)
  • Auth Header Component: User menu with logout (auth_header_component.js)
  • Dashboard: App management interface for creating and configuring apps

Backend Services

  • Auth Return Server: Flask app providing API endpoints and dashboard
  • Cognito Service: Wrapper for AWS Cognito operations (pool creation, user management)
  • Token Verification: JWT signature verification using Cognito's JWKS endpoint
  • API Key Management: Key generation, hashing, and verification system

Database

  • Apps Table: Stores app configuration, Cognito pool IDs, API keys
  • API Keys Table: Stores hashed API keys with metadata (app_id, name, last_used_at)
  • Rate Limits Table: Tracks rate limit attempts for security

Ready to integrate? See the Integration Guide for code examples.