Architecture & How It Works
Version: 1.0.30
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.
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 |
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.
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.
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.
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.
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
}
JWTs are stateless - the server doesn't track sessions. On each request:
Authorization: Bearer <token> headerexp fieldaud matches your app's client IDThe token contains everything needed to verify the user. This makes authentication scalable and fast - no session storage, no database queries.
| 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. |
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.
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.
ar_ prefix followed by 32 bytes of random dataPOST /api/keys/verify (rate-limited)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 }
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.
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.
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.
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.
Key verification endpoint is rate-limited (100 requests/minute per IP). Login attempts are also rate-limited to prevent brute force attacks.
All communication must use HTTPS. HTTP is only allowed for localhost during development. This ensures tokens and credentials are encrypted in transit.
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.
auth_component.js)auth_header_component.js)Ready to integrate? See the Integration Guide for code examples.