Skip to content

DashAuth

Authentication for Dash and Flask applications.

DashAuth

Bases: BaseAuth

Authentication for Dash and Flask apps.

Since Dash runs on Flask, this class works for both frameworks.

RECOMMENDED: Use protect_app() to protect your entire application with one line.

Example (Dash - RECOMMENDED): from dash import Dash from cognito_auth.dash import DashAuth

app = Dash(__name__)
auth = DashAuth()
auth.protect_app(app)  # Protects entire app!

@app.callback(...)
def my_callback(...):
    user = auth.get_auth_user()
    return f"Welcome {user.email}!"

Example (Flask - RECOMMENDED): from flask import Flask from cognito_auth.dash import DashAuth

app = Flask(__name__)
auth = DashAuth()
auth.protect_app(app)  # Protects entire app!

@app.route("/")
def index():
    user = auth.get_auth_user()
    return f"Welcome {user.email}!"

Alternative: Protect individual routes with @require_auth decorator.

Source code in src/cognito_auth/dash.py
class DashAuth(BaseAuth):
    """
    Authentication for Dash and Flask apps.

    Since Dash runs on Flask, this class works for both frameworks.

    RECOMMENDED: Use protect_app() to protect your entire application with one line.

    Example (Dash - RECOMMENDED):
        from dash import Dash
        from cognito_auth.dash import DashAuth

        app = Dash(__name__)
        auth = DashAuth()
        auth.protect_app(app)  # Protects entire app!

        @app.callback(...)
        def my_callback(...):
            user = auth.get_auth_user()
            return f"Welcome {user.email}!"

    Example (Flask - RECOMMENDED):
        from flask import Flask
        from cognito_auth.dash import DashAuth

        app = Flask(__name__)
        auth = DashAuth()
        auth.protect_app(app)  # Protects entire app!

        @app.route("/")
        def index():
            user = auth.get_auth_user()
            return f"Welcome {user.email}!"

    Alternative: Protect individual routes with @require_auth decorator.
    """

    def protect_app(self, app: Dash | Flask) -> None:
        """
        Protect the entire application with authentication.

        This is the RECOMMENDED approach. Call this once after creating your app,
        and all routes/callbacks will require authentication. Use get_auth_user()
        anywhere to access the authenticated user.

        Args:
            app: Dash or Flask application instance

        Example:
            app = Dash(__name__)
            auth = DashAuth()
            auth.protect_app(app)  # One line protects everything!

            @app.callback(...)
            def my_callback(...):
                user = auth.get_auth_user()
                return f"Hello {user.email}"
        """
        # Get the underlying Flask app (Dash.server or Flask app itself)
        flask_app = app.server if isinstance(app, Dash) else app

        @flask_app.before_request
        def _check_auth():
            """Validate authentication before every request."""
            try:
                headers = dict(request.headers)
                user = self._get_user_from_headers(headers)

                if not self._is_authorised(user):
                    return redirect(self.redirect_url)

                # Store user in request-scoped g object
                g.user = user

            except Exception:
                return redirect(self.redirect_url)

    def get_auth_user(self) -> User:
        """
        Get the authenticated and authorised user for this request.

        When using protect_app() (RECOMMENDED), this retrieves the user that was
        validated during the before_request hook.

        When using @require_auth decorator, this validates the user on-demand.

        Returns:
            Authenticated and authorised User

        Raises:
            RuntimeError: If called outside request context or before protect_app()

        Example:
            auth.protect_app(app)

            @app.callback(...)
            def my_callback(...):
                user = auth.get_auth_user()
                return f"Hello {user.email}"
        """
        # If protect_app() was used, user is stored in g
        if hasattr(g, "user"):
            return g.user

        # Otherwise, validate on-demand (for @require_auth decorator usage)
        if not request:
            raise RuntimeError(
                "get_auth_user() must be called within a request context"
            )

        headers = dict(request.headers)
        user = self._get_user_from_headers(headers)

        if not self._is_authorised(user):
            raise PermissionError(
                "Access denied. You don't have permission to access this resource."
            )

        return user

    def require_auth(self, func: Callable[..., Any]) -> Callable[..., Any]:
        """
        Decorator to protect individual Flask routes.

        NOTE: Using protect_app() is RECOMMENDED instead of this decorator.
        This is provided for cases where you need route-specific protection.

        Args:
            func: The function to protect

        Returns:
            Wrapped function that requires authentication

        Example:
            @app.route("/protected")
            @auth.require_auth
            def protected_route():
                user = auth.get_auth_user()
                return f"Hello {user.email}"
        """

        @wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            try:
                headers = dict(request.headers)
                user = self._get_user_from_headers(headers)

                if not self._is_authorised(user):
                    return redirect(self.redirect_url)

                # Store user in g for get_auth_user() to retrieve
                g.user = user

                return func(*args, **kwargs)

            except Exception:
                return redirect(self.redirect_url)

        return wrapper

Functions

protect_app(app)

Protect the entire application with authentication.

This is the RECOMMENDED approach. Call this once after creating your app, and all routes/callbacks will require authentication. Use get_auth_user() anywhere to access the authenticated user.

Parameters:

Name Type Description Default
app Dash | Flask

Dash or Flask application instance

required
Example

app = Dash(name) auth = DashAuth() auth.protect_app(app) # One line protects everything!

@app.callback(...) def my_callback(...): user = auth.get_auth_user() return f"Hello {user.email}"

Source code in src/cognito_auth/dash.py
def protect_app(self, app: Dash | Flask) -> None:
    """
    Protect the entire application with authentication.

    This is the RECOMMENDED approach. Call this once after creating your app,
    and all routes/callbacks will require authentication. Use get_auth_user()
    anywhere to access the authenticated user.

    Args:
        app: Dash or Flask application instance

    Example:
        app = Dash(__name__)
        auth = DashAuth()
        auth.protect_app(app)  # One line protects everything!

        @app.callback(...)
        def my_callback(...):
            user = auth.get_auth_user()
            return f"Hello {user.email}"
    """
    # Get the underlying Flask app (Dash.server or Flask app itself)
    flask_app = app.server if isinstance(app, Dash) else app

    @flask_app.before_request
    def _check_auth():
        """Validate authentication before every request."""
        try:
            headers = dict(request.headers)
            user = self._get_user_from_headers(headers)

            if not self._is_authorised(user):
                return redirect(self.redirect_url)

            # Store user in request-scoped g object
            g.user = user

        except Exception:
            return redirect(self.redirect_url)

get_auth_user()

Get the authenticated and authorised user for this request.

When using protect_app() (RECOMMENDED), this retrieves the user that was validated during the before_request hook.

When using @require_auth decorator, this validates the user on-demand.

Returns:

Type Description
User

Authenticated and authorised User

Raises:

Type Description
RuntimeError

If called outside request context or before protect_app()

Example

auth.protect_app(app)

@app.callback(...) def my_callback(...): user = auth.get_auth_user() return f"Hello {user.email}"

Source code in src/cognito_auth/dash.py
def get_auth_user(self) -> User:
    """
    Get the authenticated and authorised user for this request.

    When using protect_app() (RECOMMENDED), this retrieves the user that was
    validated during the before_request hook.

    When using @require_auth decorator, this validates the user on-demand.

    Returns:
        Authenticated and authorised User

    Raises:
        RuntimeError: If called outside request context or before protect_app()

    Example:
        auth.protect_app(app)

        @app.callback(...)
        def my_callback(...):
            user = auth.get_auth_user()
            return f"Hello {user.email}"
    """
    # If protect_app() was used, user is stored in g
    if hasattr(g, "user"):
        return g.user

    # Otherwise, validate on-demand (for @require_auth decorator usage)
    if not request:
        raise RuntimeError(
            "get_auth_user() must be called within a request context"
        )

    headers = dict(request.headers)
    user = self._get_user_from_headers(headers)

    if not self._is_authorised(user):
        raise PermissionError(
            "Access denied. You don't have permission to access this resource."
        )

    return user

require_auth(func)

Decorator to protect individual Flask routes.

NOTE: Using protect_app() is RECOMMENDED instead of this decorator. This is provided for cases where you need route-specific protection.

Parameters:

Name Type Description Default
func Callable[..., Any]

The function to protect

required

Returns:

Type Description
Callable[..., Any]

Wrapped function that requires authentication

Example

@app.route("/protected") @auth.require_auth def protected_route(): user = auth.get_auth_user() return f"Hello {user.email}"

Source code in src/cognito_auth/dash.py
def require_auth(self, func: Callable[..., Any]) -> Callable[..., Any]:
    """
    Decorator to protect individual Flask routes.

    NOTE: Using protect_app() is RECOMMENDED instead of this decorator.
    This is provided for cases where you need route-specific protection.

    Args:
        func: The function to protect

    Returns:
        Wrapped function that requires authentication

    Example:
        @app.route("/protected")
        @auth.require_auth
        def protected_route():
            user = auth.get_auth_user()
            return f"Hello {user.email}"
    """

    @wraps(func)
    def wrapper(*args: Any, **kwargs: Any) -> Any:
        try:
            headers = dict(request.headers)
            user = self._get_user_from_headers(headers)

            if not self._is_authorised(user):
                return redirect(self.redirect_url)

            # Store user in g for get_auth_user() to retrieve
            g.user = user

            return func(*args, **kwargs)

        except Exception:
            return redirect(self.redirect_url)

    return wrapper

Quick Start

from dash import Dash
from cognito_auth.dash import DashAuth

app = Dash(__name__)

# Auto-loads from environment variables
auth = DashAuth()
auth.protect_app(app)  # Protects entire app!

@app.callback(...)
def my_callback(...):
    user = auth.get_auth_user()
    return f"Welcome {user.email}!"

Configuration

DashAuth 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://gds-idea.click/401.html")
  • region (optional): AWS region (default: "eu-west-2")
from cognito_auth import Authoriser
from cognito_auth.dash import DashAuth

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

Behavior

Since Dash runs on Flask, DashAuth uses Flask's request handling. When authentication or authorisation fails:

  • With protect_app(): Automatically redirects to redirect_url before any callback executes
  • With @require_auth decorator: Redirects to redirect_url for that specific route
  • With get_auth_user() only: Raises PermissionError (useful in callbacks where you can't redirect)

The user is stored in Flask's g.user object, making it efficient to call get_auth_user() multiple times.

Development Mode

Enable dev mode for local development without ALB:

export COGNITO_AUTH_DEV_MODE=true

When dev mode is enabled and headers are missing, get_auth_user() returns a mock user instead of failing.

Customizing the Mock User

To customize the mock user returned in dev mode, create a dev-mock-user.json file in your project root:

{
  "email": "developer@example.com",
  "sub": "12345678-1234-1234-1234-123456789abc",
  "username": "12345678-1234-1234-1234-123456789abc",
  "groups": ["developers", "users"]
}

The mock user will use these values instead of the defaults. This is useful for testing different authorisation scenarios.

Available fields: - email - Mock user's email address - sub - Mock user's Cognito subject (UUID) - username - Mock user's username (usually same as sub) - groups - Mock user's Cognito groups for authorisation testing

See dev-mock-user.example.json in the repository for a complete template with comments.

Alternative config location:

You can specify a custom path via environment variable:

export COGNITO_AUTH_DEV_CONFIG=/path/to/your/mock-user.json

Complete Example

Dash Application

from dash import Dash, html, dcc
from cognito_auth.dash import DashAuth

app = Dash(__name__)

# Initialize and protect entire app
auth = DashAuth()
auth.protect_app(app)

app.layout = html.Div([
    html.H1("Protected Dashboard"),
    html.Div(id="user-info"),
])

@app.callback(
    Output("user-info", "children"),
    Input("some-input", "value")
)
def display_user_info(value):
    user = auth.get_auth_user()

    return html.Div([
        html.P(f"Logged in as: {user.email}"),
        html.P(f"Admin: {'Yes' if user.is_admin else 'No'}"),
        html.Ul([html.Li(group) for group in user.groups])
    ])

if __name__ == "__main__":
    app.run_server(debug=True)

Flask Application

from flask import Flask
from cognito_auth.dash import DashAuth

app = Flask(__name__)

# Initialize and protect entire app
auth = DashAuth()
auth.protect_app(app)

@app.route("/")
def index():
    user = auth.get_auth_user()
    return f"<h1>Welcome {user.email}!</h1>"

@app.route("/admin")
def admin():
    user = auth.get_auth_user()
    if not user.is_admin:
        return "Access denied", 403
    return "<h1>Admin Panel</h1>"

if __name__ == "__main__":
    app.run(debug=True)