Python SDK for Guardhouse authentication - supports both M2M client and resource server validation
Project description
Guardhouse Python SDK
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:
- GitHub Issues: https://github.com/legiosoft/guardhouse-sdk-python/issues
- Documentation: https://docs.guardhouse.cloud
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5fad4219a4b433a91554f078497494d6595cada480d5968103cf1efa3238aa54
|
|
| MD5 |
8db17bd8413fbec598ff2cb7b1f13d79
|
|
| BLAKE2b-256 |
fa57e7452ae8800f46383eb92f1e30e55461640fb4abfdff423b83ad678587e9
|
Provenance
The following attestation bundles were made for guardhouse-0.1.0b1.tar.gz:
Publisher:
python-publish.yml on legiosoft/guardhouse-sdk-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
guardhouse-0.1.0b1.tar.gz -
Subject digest:
5fad4219a4b433a91554f078497494d6595cada480d5968103cf1efa3238aa54 - Sigstore transparency entry: 1005612207
- Sigstore integration time:
-
Permalink:
legiosoft/guardhouse-sdk-python@7875f359090835ab3926c4e8b64ffed216bfde43 -
Branch / Tag:
refs/tags/0.1.0b1 - Owner: https://github.com/legiosoft
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@7875f359090835ab3926c4e8b64ffed216bfde43 -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7ab08fd3e589750bde88dfa85216d96dc91bd0a7462a4a023ad831063efdf721
|
|
| MD5 |
0c1165b3c8fdd02d5e47aa42d69b1249
|
|
| BLAKE2b-256 |
5fd450ed7c74252f6ecd3666ebb3a5190bf96bd3dce05b3079afd740b5eb3205
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
guardhouse-0.1.0b1-py3-none-any.whl -
Subject digest:
7ab08fd3e589750bde88dfa85216d96dc91bd0a7462a4a023ad831063efdf721 - Sigstore transparency entry: 1005612210
- Sigstore integration time:
-
Permalink:
legiosoft/guardhouse-sdk-python@7875f359090835ab3926c4e8b64ffed216bfde43 -
Branch / Tag:
refs/tags/0.1.0b1 - Owner: https://github.com/legiosoft
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@7875f359090835ab3926c4e8b64ffed216bfde43 -
Trigger Event:
release
-
Statement type: