Skip to content

LambdaAuth

Authentication for AWS Lambda functions behind an Application Load Balancer.

LambdaAuth

Bases: BaseAuth

Authentication for AWS Lambda functions behind an ALB.

Use this when your Lambda function is a target of an Application Load Balancer with OIDC (Cognito) authentication configured. The ALB forwards OIDC headers in the Lambda event, and this class verifies them.

Example

from cognito_auth.lambda_auth import LambdaAuth from cognito_auth import Authoriser

authoriser = Authoriser.from_lists(allowed_groups=["developers"]) auth = LambdaAuth(authoriser=authoriser)

def handler(event, context): try: user = auth.get_auth_user(event) except Exception: return { "statusCode": 302, "headers": {"Location": auth.redirect_url}, "body": "", }

return {
    "statusCode": 200,
    "body": f"Hello {user.email}!",
}
Source code in src/cognito_auth/lambda_auth.py
class LambdaAuth(BaseAuth):
    """
    Authentication for AWS Lambda functions behind an ALB.

    Use this when your Lambda function is a target of an Application Load
    Balancer with OIDC (Cognito) authentication configured. The ALB forwards
    OIDC headers in the Lambda event, and this class verifies them.

    Example:
        from cognito_auth.lambda_auth import LambdaAuth
        from cognito_auth import Authoriser

        authoriser = Authoriser.from_lists(allowed_groups=["developers"])
        auth = LambdaAuth(authoriser=authoriser)

        def handler(event, context):
            try:
                user = auth.get_auth_user(event)
            except Exception:
                return {
                    "statusCode": 302,
                    "headers": {"Location": auth.redirect_url},
                    "body": "",
                }

            return {
                "statusCode": 200,
                "body": f"Hello {user.email}!",
            }
    """

    def get_auth_user(self, event: dict) -> User:
        """
        Get the authenticated and authorised user from an ALB Lambda event.

        Extracts OIDC headers from the event, verifies the tokens, and checks
        authorisation rules.

        Args:
            event: ALB Lambda event dictionary. Must contain a "headers" key
                with the ALB OIDC headers (x-amzn-oidc-data and
                x-amzn-oidc-accesstoken).

        Returns:
            Authenticated and authorised User

        Raises:
            MissingTokenError: If required OIDC headers are not present
            InvalidTokenError: If token verification fails
            ExpiredTokenError: If tokens have expired
            PermissionError: If user is authenticated but not authorised

        Example:
            auth = LambdaAuth()

            def handler(event, context):
                try:
                    user = auth.get_auth_user(event)
                except Exception:
                    return {
                        "statusCode": 302,
                        "headers": {"Location": auth.redirect_url},
                        "body": "",
                    }

                return {
                    "statusCode": 200,
                    "body": f"Hello {user.email}!",
                }
        """
        headers = event.get("headers", {})
        user = self._get_user_from_headers(headers)

        if not self._is_authorised(user):
            logger.warning(
                "User not authorised: email=%s, groups=%s",
                user.email,
                user.groups,
            )
            raise PermissionError(
                "Access denied. You don't have permission to access this resource."
            )

        return user

Functions

get_auth_user(event)

Get the authenticated and authorised user from an ALB Lambda event.

Extracts OIDC headers from the event, verifies the tokens, and checks authorisation rules.

Parameters:

Name Type Description Default
event dict

ALB Lambda event dictionary. Must contain a "headers" key with the ALB OIDC headers (x-amzn-oidc-data and x-amzn-oidc-accesstoken).

required

Returns:

Type Description
User

Authenticated and authorised User

Raises:

Type Description
MissingTokenError

If required OIDC headers are not present

InvalidTokenError

If token verification fails

ExpiredTokenError

If tokens have expired

PermissionError

If user is authenticated but not authorised

Example

auth = LambdaAuth()

def handler(event, context): try: user = auth.get_auth_user(event) except Exception: return { "statusCode": 302, "headers": {"Location": auth.redirect_url}, "body": "", }

return {
    "statusCode": 200,
    "body": f"Hello {user.email}!",
}
Source code in src/cognito_auth/lambda_auth.py
def get_auth_user(self, event: dict) -> User:
    """
    Get the authenticated and authorised user from an ALB Lambda event.

    Extracts OIDC headers from the event, verifies the tokens, and checks
    authorisation rules.

    Args:
        event: ALB Lambda event dictionary. Must contain a "headers" key
            with the ALB OIDC headers (x-amzn-oidc-data and
            x-amzn-oidc-accesstoken).

    Returns:
        Authenticated and authorised User

    Raises:
        MissingTokenError: If required OIDC headers are not present
        InvalidTokenError: If token verification fails
        ExpiredTokenError: If tokens have expired
        PermissionError: If user is authenticated but not authorised

    Example:
        auth = LambdaAuth()

        def handler(event, context):
            try:
                user = auth.get_auth_user(event)
            except Exception:
                return {
                    "statusCode": 302,
                    "headers": {"Location": auth.redirect_url},
                    "body": "",
                }

            return {
                "statusCode": 200,
                "body": f"Hello {user.email}!",
            }
    """
    headers = event.get("headers", {})
    user = self._get_user_from_headers(headers)

    if not self._is_authorised(user):
        logger.warning(
            "User not authorised: email=%s, groups=%s",
            user.email,
            user.groups,
        )
        raise PermissionError(
            "Access denied. You don't have permission to access this resource."
        )

    return user

Quick Start

from cognito_auth.lambda_auth import LambdaAuth
from cognito_auth import Authoriser

authoriser = Authoriser.from_lists(allowed_groups=["developers"])
auth = LambdaAuth(authoriser=authoriser)

def handler(event, context):
    try:
        user = auth.get_auth_user(event)
    except Exception:
        return {
            "statusCode": 302,
            "headers": {"Location": auth.redirect_url},
            "body": "",
        }

    return {
        "statusCode": 200,
        "body": f"Hello {user.email}!",
    }

Configuration

LambdaAuth inherits from BaseAuth and accepts these parameters:

  • authoriser (optional): Pre-configured Authoriser instance. If not provided, auto-loads from environment variables
  • redirect_url (optional): Where to redirect unauthorised users (default: "https://public.gds-idea.io/401.html"). Available as auth.redirect_url for building redirect responses.
  • region (optional): AWS region (default: "eu-west-2")
from cognito_auth import Authoriser
from cognito_auth.lambda_auth import LambdaAuth

# Custom configuration
authoriser = Authoriser.from_lists(allowed_groups=["developers"])
auth = LambdaAuth(
    authoriser=authoriser,
    redirect_url="https://myapp.com/unauthorised",
    region="us-east-1"
)

Behavior

LambdaAuth extracts OIDC headers from the ALB Lambda event and verifies them. On failure:

  • MissingTokenError: Required OIDC headers not present in the event
  • InvalidTokenError: Token verification failed
  • ExpiredTokenError: Token has expired
  • PermissionError: User authenticated but not authorised

The caller is responsible for building the appropriate Lambda response (redirect, JSON error, etc.).

Development Mode

Enable dev mode for local development without ALB. See Development Mode for full details.

export COGNITO_AUTH_DEV_MODE=true

Complete Example

from cognito_auth.lambda_auth import LambdaAuth
from cognito_auth import Authoriser

authoriser = Authoriser.from_lists(allowed_groups=["developers"])
auth = LambdaAuth(authoriser=authoriser)

def handler(event, context):
    try:
        user = auth.get_auth_user(event)
    except Exception:
        return {
            "statusCode": 302,
            "headers": {"Location": auth.redirect_url},
            "body": "",
        }

    return {
        "statusCode": 200,
        "headers": {"Content-Type": "text/html"},
        "body": f"""
            <h1>Welcome {user.name}!</h1>
            <p>Email: {user.email}</p>
            <p>Groups: {', '.join(user.groups)}</p>
        """,
    }

No Authorisation (Authentication Only)

from cognito_auth.lambda_auth import LambdaAuth

# Pass authoriser=None to skip authorisation checks
auth = LambdaAuth(authoriser=None)

def handler(event, context):
    try:
        user = auth.get_auth_user(event)
    except Exception:
        return {
            "statusCode": 302,
            "headers": {"Location": auth.redirect_url},
            "body": "",
        }

    return {
        "statusCode": 200,
        "body": f"Authenticated as {user.email}",
    }