Using API Gateway and Lambda as a Unified Callback URL to Manage Multiple Subdomains

Using AWS Lambda and API Gateway as a Cognito Callback URL

Overview

When implementing authentication with Amazon Cognito, you need to specify callback URLs for your application. Amazon Cognito has a limit of 100 callback URLs per user pool client, which becomes a challenge when dealing with multiple subdomains or environments, especially in multi-tenant applications where each tenant has its own subdomain.


:high_voltage: The Callback URL Limit Problem

Cognito’s 100 callback URL limit can be quickly reached in scenarios such as:

  • Supporting multiple subdomains (app1.yourdomain.com, app2.yourdomain.com, etc.)
  • Different callback URLs for different buyers, clients, or auction applications.

:white_check_mark: Updated Solution

This solution uses a single API Gateway endpoint backed by a Lambda function that processes the callback and encrypts the authorization code. The encrypted code is then forwarded to the frontend, where the frontend exchanges the code for tokens. This approach bypasses the Cognito callback URL limit by dynamically handling subdomain-specific redirects.


:gear: Architecture

The solution uses the following AWS services:

  1. Amazon Cognito – For user authentication and management.
  2. API Gateway – Provides a single callback endpoint.
  3. Lambda Function – Processes the callback, encrypts the authorization code, and redirects to the target system.

:locked_with_key: Authentication Flow

  1. User initiates login from the frontend

    • The frontend redirects the user to the Cognito hosted UI with the following parameters:
    • client_id: Your Cognito app client ID.
    • redirect_uri: API Gateway endpoint URL.
    • state: Encoded information about the original domain and path.
    • response_type: code.
    • scope: Requested OAuth scopes.
  2. User authenticates with Cognito

  3. Cognito redirects to API Gateway with the authorization code

    • API Gateway triggers the Lambda function.
  4. Lambda function:

    • Receives the callback with the authorization code and state.
    • Encrypts the authorization code.
    • Extracts the original domain and path from the state parameter.
    • Redirects the user to the original domain with the encrypted code in the query string.
  5. Frontend exchanges the encrypted code for tokens.

    • Frontend securely decrypts the code and exchanges it for tokens with Cognito.
    • Tokens are stored securely (e.g., in localStorage or cookies).

:chart_increasing: Federated login flow with the Api as a callback url


:hammer_and_wrench: Frontend Implementation

// Frontend code example

async function exchangeCodeForToken(authCode) {
  try {
    const response = await fetch(`https://${COGNITO_DOMAIN}/oauth2/token`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        code: authCode,
        client_id: your_client_id,
        client_secret: your_client_secret,
        redirect_uri: `api_endpoint`,
        grant_type: 'authorization_code',
      }),
    });

    const data = await response.json();
    if (data.access_token) {
      
      // Store tokens and user info securely
      localStorage.setItem('accessToken', tokens.access_token);
      localStorage.setItem('idToken', tokens.id_token);
      localStorage.setItem('refreshToken', tokens.refresh_token);

      
    } else {
      console.error('Token exchange failed:', data);
    }
  } catch (error) {
    console.error('Error exchanging code:', error);
  }
}

:gear: Lambda Implementation

const crypto = require('crypto');
const algorithm = 'aes-256-cbc';
const secretKey = process.env.SECRET_KEY;
const iv = crypto.randomBytes(16);

exports.handler = async (event) => {
    const queryParams = event.queryStringParameters || {};
    const code = queryParams.code;
    const state = queryParams.state;

    if (!code || !state) {
        return {
            statusCode: 400,
            body: JSON.stringify({ error: 'Missing code or state' })
        };
    }

    const originalDomain = JSON.parse(Buffer.from(state, 'base64').toString('utf-8')).tenant;

    // Encrypt the code
    const cipher = crypto.createCipheriv(algorithm, Buffer.from(secretKey, 'hex'), iv);
    let encrypted = cipher.update(code);
    encrypted = Buffer.concat([encrypted, cipher.final()]);

    const encryptedCode = iv.toString('hex') + ':' + encrypted.toString('hex');

    const redirectUrl = `${originalDomain}?code=${encodeURIComponent(encryptedCode)}`;

    return {
        statusCode: 302,
        headers: {
            Location: redirectUrl,
            'Cache-Control': 'no-cache'
        }
    };
};

:locked: Security Considerations

  1. Token Security:
    • Never expose tokens in URL parameters.
  2. State Parameter Security:
    • Use a nonce and validate the state parameter to prevent CSRF.
  3. Encryption Best Practices:
    • Use strong encryption algorithms (e.g., AES-256-CBC).
    • Rotate encryption keys periodically.
  4. CORS and Domain Validation:
    • Configure CORS headers properly.
    • Validate allowed domains and enforce HTTPS.

:high_voltage: Benefits

  1. Subdomain Flexibility:
    • Single API Gateway endpoint for all subdomains.
  2. Centralized Logic:
    • Token handling and state validation in a single Lambda function.
  3. Security:
    • API Gateway adds an additional security layer.
    • Tokens are exchanged only after encrypted validation.
  4. Maintenance:
    • Easier to update and monitor a single callback handler.

:laptop: Conclusion

This solution effectively bypasses the Cognito callback URL limit by implementing a centralized callback handler using API Gateway and Lambda. It provides a scalable and secure way to handle authentication for multiple subdomains or environments while maintaining security and user experience.

Key benefits:

  • Eliminates the 100 callback URL limit
  • Maintains security of the OAuth flow
  • Provides flexibility for multi-tenant applications
  • Scales automatically with AWS serverless architecture
3 Likes