Shared AWS Cognito authentication library for FastAPI + Jinja2 web applications.
# Basic installation
pip install -e .
# With JWT verification support (recommended)
pip install -e ".[auth]"
# With development dependencies
pip install -e ".[dev,auth]"from daylily_cognito import CognitoConfig, CognitoAuth
config = CognitoConfig(
name="myapp",
region="us-west-2",
user_pool_id="us-west-2_XXXXXXXXX",
app_client_id="XXXXXXXXXXXXXXXXXXXXXXXXXX",
aws_profile="my-profile", # optional
)
config.validate() # raises ValueError if invalid
auth = CognitoAuth(
region=config.region,
user_pool_id=config.user_pool_id,
app_client_id=config.app_client_id,
app_client_secret=config.app_client_secret, # optional, for clients with secrets
profile=config.aws_profile,
)When a Cognito app client has a client secret enabled, all authentication API calls
require a SECRET_HASH parameter. The library automatically computes this when
app_client_secret is provided:
# For app clients WITH a secret
auth = CognitoAuth(
region="us-west-2",
user_pool_id="us-west-2_pUqKyIM1N",
app_client_id="your-client-id",
app_client_secret="your-client-secret", # Required for clients with secrets
)
# The SECRET_HASH is automatically computed as:
# base64(hmac_sha256(client_secret, username + client_id))Note: If your Cognito app client was created with GenerateSecret=True, you MUST
provide the app_client_secret parameter, otherwise authentication will fail with
"Unable to verify secret hash for client".
For multi-tenant or multi-environment setups:
export DAYCOG_PROD_REGION=us-west-2
export DAYCOG_PROD_USER_POOL_ID=us-west-2_abc123
export DAYCOG_PROD_APP_CLIENT_ID=client123
export DAYCOG_PROD_AWS_PROFILE=prod-profile # optionalfrom daylily_cognito import CognitoConfig
config = CognitoConfig.from_env("PROD")For backward compatibility with existing deployments:
export COGNITO_REGION=us-west-2 # or AWS_REGION, defaults to us-west-2
export COGNITO_USER_POOL_ID=us-west-2_abc123
export COGNITO_APP_CLIENT_ID=client123 # or COGNITO_CLIENT_ID
export AWS_PROFILE=my-profile # optionalfrom daylily_cognito import CognitoConfig
config = CognitoConfig.from_legacy_env()The daycog CLI provides commands for managing Cognito resources:
# Check configuration status
daycog status
# Create user pool and app client
daycog setup --name my-pool --port 8001
# List users
daycog list-users
# Add a user
daycog add-user user@example.com
# Set user password
daycog set-password --email user@example.com --password NewPass123
# Delete a user
daycog delete-user --email user@example.com
# Delete all users (use with caution!)
daycog delete-all-users --force
# Delete the entire pool
daycog teardown --forceUse --config NAME to select a named configuration:
export DAYCOG_PROD_REGION=us-west-2
export DAYCOG_PROD_USER_POOL_ID=us-west-2_prod
export DAYCOG_PROD_APP_CLIENT_ID=client_prod
export DAYCOG_DEV_REGION=us-east-1
export DAYCOG_DEV_USER_POOL_ID=us-east-1_dev
export DAYCOG_DEV_APP_CLIENT_ID=client_dev
daycog --config PROD status
daycog --config DEV list-usersfrom fastapi import Depends, FastAPI
from daylily_cognito import CognitoAuth, CognitoConfig, create_auth_dependency
app = FastAPI()
# Load config and create auth handler
config = CognitoConfig.from_legacy_env()
auth = CognitoAuth(
region=config.region,
user_pool_id=config.user_pool_id,
app_client_id=config.app_client_id,
)
# Create dependencies
get_current_user = create_auth_dependency(auth)
get_optional_user = create_auth_dependency(auth, optional=True)
@app.get("/protected")
def protected_route(user: dict = Depends(get_current_user)):
return {"user": user}
@app.get("/public")
def public_route(user: dict | None = Depends(get_optional_user)):
return {"user": user}from daylily_cognito import (
build_authorization_url,
build_logout_url,
exchange_authorization_code,
)
# Build authorization URL for login redirect
auth_url = build_authorization_url(
domain="myapp.auth.us-west-2.amazoncognito.com",
client_id="abc123",
redirect_uri="http://localhost:8000/auth/callback",
state="csrf-token",
)
# Exchange authorization code for tokens
tokens = exchange_authorization_code(
domain="myapp.auth.us-west-2.amazoncognito.com",
client_id="abc123",
code="auth-code-from-callback",
redirect_uri="http://localhost:8000/auth/callback",
)
# Build logout URL
logout_url = build_logout_url(
domain="myapp.auth.us-west-2.amazoncognito.com",
client_id="abc123",
logout_uri="http://localhost:8000/",
)daylily-cognito supports standalone Google OAuth2 authentication that auto-creates
users in your Cognito user pool. This hybrid approach lets users sign in with Google
while keeping Cognito as the single user directory.
- Create a Google Cloud project and enable the OAuth consent screen
- Create OAuth 2.0 credentials in the Google Cloud Console
- Register
http://localhost:8000/auth/google/callbackas an authorized redirect URI
Namespaced:
export DAYCOG_PROD_GOOGLE_CLIENT_ID="your-google-client-id"
export DAYCOG_PROD_GOOGLE_CLIENT_SECRET="your-google-client-secret"Legacy:
export GOOGLE_CLIENT_ID="your-google-client-id"
export GOOGLE_CLIENT_SECRET="your-google-client-secret"Or use the CLI helper:
daycog setup-google --client-id YOUR_ID --client-secret YOUR_SECRETfrom daylily_cognito import (
build_google_authorization_url,
exchange_google_code_for_tokens,
fetch_google_userinfo,
auto_create_cognito_user_from_google,
generate_state_token,
CognitoAuth,
CognitoConfig,
)
# 1. Build authorization URL and redirect the user
state = generate_state_token()
auth_url = build_google_authorization_url(
client_id="your-google-client-id",
redirect_uri="http://localhost:8000/auth/google/callback",
state=state,
)
# 2. In your callback handler, exchange the code for tokens
tokens = exchange_google_code_for_tokens(
client_id="your-google-client-id",
client_secret="your-google-client-secret",
code=request.query_params["code"],
redirect_uri="http://localhost:8000/auth/google/callback",
)
# 3. Fetch the user's Google profile
userinfo = fetch_google_userinfo(tokens["access_token"])
# userinfo contains: sub, email, email_verified, name, given_name,
# family_name, picture, locale, hd (if Google Workspace)
# 4. Auto-create or retrieve the Cognito user
config = CognitoConfig.from_legacy_env()
auth = CognitoAuth(
region=config.region,
user_pool_id=config.user_pool_id,
app_client_id=config.app_client_id,
)
result = auto_create_cognito_user_from_google(auth, userinfo)
# result = {"user": {...}, "created": True/False, "google_sub": "...", "email": "..."}All attributes available with standard scopes (openid email profile) — no extra
permissions required:
| Claim | Description |
|---|---|
sub |
Unique Google user ID |
email |
Email address |
email_verified |
Whether email is verified by Google |
name |
Full display name |
given_name |
First name |
family_name |
Last name |
picture |
Profile photo URL |
locale |
User locale (BCP 47) |
hd |
Hosted domain (Google Workspace only, absent for personal accounts) |
The user pool must have these custom attributes configured:
custom:customer_id— defaults to Googlesubcustom:google_sub— Google unique user IDcustom:google_hd— hosted domain (optional, populated when present)
# Install with dev dependencies
pip install -e ".[dev,auth]"
# Run tests
pytest -q
# Run tests with coverage
pytest --cov=daylily_cognito
# Lint and format
ruff check daylily_cognito tests
ruff format daylily_cognito testsMIT