Skip to main content

Python SDK for Guardhouse authentication - supports both M2M client and resource server validation

Project description

Guardhouse Python SDK

PyPI version Python License

Python SDK for Guardhouse authentication, supporting both Machine-to-Machine (M2M) client and resource server token validation.

Beta Release: This is the first beta release (v0.1.0b1). The API is stable but may undergo minor changes before the final v1.0.0 release. Feedback welcome!

Features

  • Dual Mode Support: M2M client for backend services and resource server validation
  • Sync & Async: Full support for both synchronous and asynchronous operations
  • Automatic Token Management: Token caching, auto-refresh, and thread-safe operations
  • Strict Security: Algorithm enforcement (RS256 only), JWKS caching, introspection
  • Framework Integrations: Batteries-included helpers for FastAPI and Flask
  • Type Hints: Fully typed for Python 3.9+ (passes mypy --strict)
  • Production Ready: HTTP resilience with retry, comprehensive error handling

Installation

pip install guardhouse

Optional Dependencies

For framework integrations:

# FastAPI
pip install guardhouse[fastapi]

# Flask
pip install guardhouse[flask]

Quick Start

M2M Client (Backend Service)

Authenticate your backend service to call Guardhouse APIs:

from guardhouse import GuardhouseClient

client = GuardhouseClient(
    authority="https://auth.example.com",
    client_id="my-service",
    client_secret="your-secret-here",
    scope="api"
)

# Get access token automatically (cached)
token = client.get_access_token()

# Make authenticated requests
response = client.get("https://api.example.com/users")
users = response.json()

# Async support
async def fetch_users():
    token = await client.get_access_token_async()
    response = await client.get_async("https://api.example.com/users")
    return response.json()

Resource Server (Token Validation)

Validate incoming Bearer tokens to protect your API:

from guardhouse import TokenVerifier, TokenExpiredError, TokenValidationError

verifier = TokenVerifier(
    authority="https://auth.example.com",
    audience="my-api"
)

def protected_route(request):
    # Extract token from Authorization header
    token = request.headers.get("Authorization", "").replace("Bearer ", "")

    try:
        claims = verifier.verify(token)
        print(f"Authenticated user: {claims['sub']}")

        # Check scopes
        TokenVerifier.has_scope("read:users", claims.get("scope"))

    except TokenExpiredError:
        return {"error": "Token expired"}, 401
    except TokenValidationError as e:
        return {"error": str(e)}, 401

Framework Integrations

FastAPI

from fastapi import FastAPI, Depends, HTTPException
from guardhouse.middleware import requires_auth, User

app = FastAPI()

# Create auth dependency
get_user = requires_auth(
    authority="https://auth.example.com",
    audience="my-api"
)

@app.get("/protected")
async def protected_route(user: User = Depends(get_user)):
    return {"message": f"Hello, {user.sub}!"}

@app.get("/admin")
async def admin_route(user: User = Depends(get_user)):
    if not user.has_scope("admin"):
        raise HTTPException(status_code=403, detail="Admin scope required")
    return {"message": "Welcome admin!"}

Flask

from flask import Flask, request
from guardhouse.middleware import FlaskAuthExtension

app = Flask(__name__)

# Initialize extension
auth = FlaskAuthExtension(
    app=app,
    authority="https://auth.example.com",
    audience="my-api"
)

@app.route("/protected")
@auth.requires_auth
def protected(user):
    return {"message": f"Hello, {user['sub']}!"}

@app.route("/admin")
@auth.requires_auth(required_scope="admin")
def admin(user):
    return {"message": "Welcome admin!"}

Advanced Configuration

Client Configuration

from guardhouse import GuardhouseClient

client = GuardhouseClient(
    authority="https://auth.example.com",
    client_id="my-service",
    client_secret="your-secret",
    scope="api read write",

    # Token caching
    cache_expiration_buffer=60,  # Refresh 60s before expiry

    # Retry logic
    max_retry_attempts=3,
    request_timeout=30,
    enable_http_resilience=True,

    # Introspection (optional)
    introspection_client_id="introspection-client",
    introspection_client_secret="introspection-secret",
    introspection_credential_transmission="basic_auth",
)

Verifier Configuration

from guardhouse import TokenVerifier

verifier = TokenVerifier(
    authority="https://auth.example.com",
    audience="my-api",

    # Validation mode
    validation_mode="jwt_signature",  # or "introspection"

    # Introspection (if using introspection mode)
    introspection_client_id="introspection-client",
    introspection_client_secret="introspection-secret",

    # JWKS caching
    jwks_cache_duration_hours=24,
    jwks_refresh_interval_minutes=5,

    # Security
    validate_issuer=True,
    validate_audience=True,
    validate_lifetime=True,
    valid_algorithms=["RS256"],
    clock_skew_minutes=5.0,
)

Security Features

The SDK implements comprehensive security protections:

  • Algorithm Enforcement: Strict RS256 only, rejects "none" algorithm
  • Issuer Validation: Verifies token issuer matches configured authority
  • Audience Validation: Ensures token is intended for your API
  • Expiration Validation: Rejects expired tokens (with configurable clock skew)
  • JWKS Caching: Prevents DDoS on auth server with rate-limited refreshes
  • Scope Checking: Helper method to validate user permissions
  • Safe Logging: Secrets redacted from all logs and error messages
  • Thread Safety: Token cache is protected with RLock
  • Token Type Validation: Ensures token type is "JWT"

Error Handling

from guardhouse import (
    GuardhouseError,
    GuardhouseConfigError,
    GuardhouseAuthError,
    GuardhouseNetworkError,
    TokenValidationError,
    TokenExpiredError,
    InvalidAlgorithmError,
    ScopeError,
)

try:
    claims = verifier.verify(token)
except TokenExpiredError:
    # Token has expired
    pass
except InvalidAlgorithmError as e:
    # Invalid algorithm (e.g., "none")
    print(f"Algorithm: {e.algorithm}")
    pass
except TokenValidationError:
    # Generic validation error
    pass
except GuardhouseAuthError as e:
    # Authentication failed
    print(f"Error: {e.error_code}")
    print(f"Description: {e.error_description}")
    pass
except GuardhouseNetworkError as e:
    # Network request failed
    print(f"Status: {e.status_code}")
    print(f"URL: {e.url}")
    pass

Development

Setup Development Environment

# Install with development dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run type checking
mypy --strict src/

# Run linting
ruff check src/

Running Tests

# Run all tests
pytest

# Run with coverage
pytest --cov=src/guardhouse --cov-report=html

# Run specific test file
pytest tests/test_verifier.py

Project Structure

guardhouse-sdk-python/
├── src/
│   └── guardhouse/
│       ├── __init__.py          # Public API
│       ├── client.py            # M2M client
│       ├── verifier.py          # Token verifier
│       ├── config.py            # Configuration models
│       ├── constants.py         # Constants
│       ├── exceptions.py        # Custom exceptions
│       └── middleware/        # Framework integrations
│           ├── __init__.py
│           ├── fastapi.py
│           └── flask.py
├── tests/
│   └── test_verifier.py       # Unit tests
├── pyproject.toml             # Project configuration
└── README.md

License

Apache-2.0 License - see LICENSE file for details.

Contributing

Contributions are welcome! Please read our contributing guidelines before submitting PRs.

Support

For issues and questions:

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

guardhouse-0.1.0b1.tar.gz (29.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

guardhouse-0.1.0b1-py3-none-any.whl (28.4 kB view details)

Uploaded Python 3

File details

Details for the file guardhouse-0.1.0b1.tar.gz.

File metadata

  • Download URL: guardhouse-0.1.0b1.tar.gz
  • Upload date:
  • Size: 29.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for guardhouse-0.1.0b1.tar.gz
Algorithm Hash digest
SHA256 5fad4219a4b433a91554f078497494d6595cada480d5968103cf1efa3238aa54
MD5 8db17bd8413fbec598ff2cb7b1f13d79
BLAKE2b-256 fa57e7452ae8800f46383eb92f1e30e55461640fb4abfdff423b83ad678587e9

See more details on using hashes here.

Provenance

The following attestation bundles were made for guardhouse-0.1.0b1.tar.gz:

Publisher: python-publish.yml on legiosoft/guardhouse-sdk-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file guardhouse-0.1.0b1-py3-none-any.whl.

File metadata

  • Download URL: guardhouse-0.1.0b1-py3-none-any.whl
  • Upload date:
  • Size: 28.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for guardhouse-0.1.0b1-py3-none-any.whl
Algorithm Hash digest
SHA256 7ab08fd3e589750bde88dfa85216d96dc91bd0a7462a4a023ad831063efdf721
MD5 0c1165b3c8fdd02d5e47aa42d69b1249
BLAKE2b-256 5fd450ed7c74252f6ecd3666ebb3a5190bf96bd3dce05b3079afd740b5eb3205

See more details on using hashes here.

Provenance

The following attestation bundles were made for guardhouse-0.1.0b1-py3-none-any.whl:

Publisher: python-publish.yml on legiosoft/guardhouse-sdk-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page