Skip to content

altissimo-hq/auth-providers-python

altissimo-auth

CI Python License Code style: ruff

Reusable, framework-agnostic authentication library for Python applications.

Features

  • πŸ”‘ API Key authentication with pluggable storage backends
  • πŸ”₯ Firebase ID token verification and user record resolution
  • 🌐 Google OAuth2 ID token verification
  • πŸ›‘οΈ Google IAP header extraction
  • πŸ”’ OIDC service-to-service authentication with configurable caller policies
  • πŸͺ Webhook signature verification (Stripe included, extensible via protocol)
  • ⚑ FastAPI adapter with Depends() wrappers and structured telemetry
  • πŸ₯· Django Ninja adapter with native auth classes
  • πŸ§ͺ Flask adapter for server-side Google OAuth2 Authorization Code flow
  • 🌊 Auth Cascade for multi-method authentication fallback
  • πŸ“ Typed β€” full type annotations with py.typed PEP 561 marker

Requirements

  • Python 3.11+

Installation

pip install altissimo-auth[fastapi]        # core + FastAPI adapter
pip install altissimo-auth[ninja]          # core + Django Ninja adapter
pip install altissimo-auth[flask]          # core + Flask OAuth2 adapter
pip install altissimo-auth[firebase]       # core + Firebase provider
pip install altissimo-auth[google]         # core + Google OAuth2/OIDC providers
pip install altissimo-auth[all]            # everything

Quick Start

FastAPI

from typing import Annotated

from fastapi import Depends, FastAPI

from altissimo.auth import FirebaseUser
from altissimo.auth.fastapi import Auth, configure
from altissimo.auth.service import AuthService

app = FastAPI()

# Configure once at startup
configure(AuthService(api_key_backend=my_backend))

# Route-level guard
@app.get("/items", dependencies=[Depends(Auth.validate_api_key)])
async def list_items():
    ...

# Parameter injection
@app.get("/me")
async def get_me(
    user: Annotated[FirebaseUser, Depends(Auth.validate_firebase_user)],
):
    return {"uid": user.uid, "email": user.email}

Django Ninja

from ninja import NinjaAPI

from altissimo.auth.cascade import AuthCascade
from altissimo.auth.ninja import ApiKeyAuth, FirebaseAuth, OIDCAuth, configure
from altissimo.auth.service import AuthService

api = NinjaAPI()

# Configure once at startup
configure(AuthService(api_key_backend=my_backend))

# Route-level guard
@api.get("/items", auth=ApiKeyAuth())
def list_items(request):
    ...

# Cascading Auth (Multi-Method Fallback)
cascade = AuthCascade([FirebaseAuth(), ApiKeyAuth()])

@api.get("/me", auth=cascade)
def get_me(request):
    user = request.auth  # Will be FirebaseUser or APIKeyRecord
    ...

Flask (Server-Side Google OAuth2)

from flask import Flask, g
from altissimo.auth.flask import OAuth2FlowManager

app = Flask(__name__)
app.secret_key = "super-secret"

auth = OAuth2FlowManager(
    client_secrets_file="client_secret.json",
    scopes=["openid", "email", "profile"],
    redirect_uri_endpoint="oauth2_callback",
    hosted_domain="mycompany.com"  # Optional: restrict to domain
)

@app.route("/login")
def login():
    return auth.login_redirect()

@app.route("/oauth2_callback")
def oauth2_callback():
    user_info = auth.handle_callback()
    return f"Logged in as {user_info['email']}"

@app.route("/logout")
def logout():
    auth.logout()
    return "Logged out"

@app.route("/protected")
@auth.require_auth
def protected():
    return f"Hello, {g.user.email}"  # g.user is populated by require_auth

OIDC Service-to-Service

from altissimo.auth.fastapi import Auth
from altissimo.auth.providers.oidc import OIDCPolicy

policy = OIDCPolicy(
    allowed_callers={
        "dev": ["sa@my-project-dev.iam.gserviceaccount.com"],
        "prod": ["sa@my-project-prod.iam.gserviceaccount.com"],
    },
    valid_audiences={
        "dev": ["https://api-dev.run.app"],
        "prod": ["https://api-prod.run.app"],
    },
    project_sa_suffix="@my-project-{env}.iam.gserviceaccount.com",
    team_domains=["mycompany.com"],
)

verify_caller = Auth.create_oidc_dependency(policy)

@app.post("/callback", dependencies=[Depends(verify_caller)])
async def handle_callback():
    ...

Architecture

altissimo.auth
β”œβ”€β”€ core/           # Framework-agnostic models, exceptions, policies
β”œβ”€β”€ providers/      # Token/key verification (API key, Firebase, Google, OIDC, IAP, webhooks)
β”œβ”€β”€ service.py      # AuthService orchestration layer
β”œβ”€β”€ fastapi/        # FastAPI Depends() wrappers
└── ninja/          # Django Ninja auth classes

Provider β†’ Service β†’ Adapter Pattern

  • Providers contain the actual auth logic (token verification, key lookup)
  • AuthService wires providers + policies together
  • Adapters are thin framework-specific wrappers that delegate to AuthService

Pluggable Backends

API key storage and webhook verification are abstracted via Python Protocols:

from altissimo.auth.providers import APIKeyBackend

class MyKeyBackend:
    """Implement the APIKeyBackend protocol with your storage."""

    def get_key(self, key_id: str) -> APIKeyRecord | None:
        return db.query(APIKey).filter_by(id=key_id).first()

Development

# Install all dependencies
poetry sync

# Run tests
poetry run pytest

# Run tests with coverage
poetry run pytest --cov=altissimo --cov-report=term-missing

# Run linters
poetry run ruff check .
poetry run ruff format --check .

See CONTRIBUTING.md for detailed development guidelines.

Changelog

See CHANGELOG.md for version history.

Security

For reporting security vulnerabilities, see SECURITY.md.

License

Apache License 2.0 β€” see LICENSE for details.

About

Abstracted authentication providers for IAP, OIDC, and API keys

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages