Verify and decrypt 3rd party OIDC ID tokens to protect your fastapi endpoints.
Documentation: ReadTheDocs
Source code: Github
- Features
- Installation
- Quick Start
- Supported Providers
- Configuration
- Usage Examples
- Troubleshooting
- Contributing
- Security
- License
- ✅ Verify JWT tokens from any OIDC-compliant provider
- ✅ Automatic discovery of provider configuration via
.well-knownendpoints - ✅ Caching of signing keys and configuration for performance
- ✅ Type-safe token validation with Pydantic
- ✅ Support for custom token models with additional fields
- ✅ FastAPI-native dependency injection
- ✅ Python 3.10+ with modern type hints
- ✅ Comprehensive test coverage
- ✅ Production-ready and actively maintained
pip install fastapi-oidcOr with Poetry:
poetry add fastapi-oidcHere's a minimal example to get you started:
from fastapi import Depends, FastAPI
from fastapi_oidc import IDToken, get_auth
# Configure OIDC authentication
authenticate_user = get_auth(
client_id="your-client-id",
base_authorization_server_uri="https://your-auth-server.com",
issuer="your-auth-server.com",
signature_cache_ttl=3600,
)
app = FastAPI()
@app.get("/")
def public():
return {"message": "This endpoint is public"}
@app.get("/protected")
def protected(token: IDToken = Depends(authenticate_user)):
return {"message": f"Hello {token.email}!"}fastapi-oidc works with any OIDC-compliant authentication provider:
- ✅ Okta - Enterprise identity management
- ✅ Auth0 - Authentication and authorization platform
- ✅ Google OAuth 2.0 - Google identity services
- ✅ Microsoft Azure AD / Entra ID - Microsoft identity platform
- ✅ Keycloak - Open-source identity and access management
- ✅ AWS Cognito - Amazon's user identity and data synchronization
- ✅ Any OIDC-compliant provider - Supports OpenID Connect Discovery
See the examples directory for provider-specific configurations (coming soon).
| Parameter | Type | Description |
|---|---|---|
client_id |
str |
OAuth client ID from your provider |
base_authorization_server_uri |
str |
Base URL of your auth server (e.g., https://dev-123456.okta.com) |
issuer |
str |
Token issuer identifier (usually matches base URI domain) |
signature_cache_ttl |
int |
Cache duration for signing keys in seconds (recommended: 3600) |
| Parameter | Type | Default | Description |
|---|---|---|---|
audience |
str |
client_id |
Token audience claim to validate |
token_type |
Type[IDToken] |
IDToken |
Custom token model (must inherit from IDToken) |
Basic Configuration:
authenticate_user = get_auth(
client_id="0oa1e3pv9opbyq2Gm4x7",
base_authorization_server_uri="https://dev-126594.okta.com",
issuer="dev-126594.okta.com",
signature_cache_ttl=3600,
)With Custom Audience:
authenticate_user = get_auth(
client_id="your-client-id",
audience="https://yourapi.url.com/api",
base_authorization_server_uri="https://auth.example.com",
issuer="auth.example.com",
signature_cache_ttl=3600,
)Using Environment Variables (Recommended):
import os
authenticate_user = get_auth(
client_id=os.getenv("OIDC_CLIENT_ID"),
audience=os.getenv("OIDC_AUDIENCE"),
base_authorization_server_uri=os.getenv("OIDC_BASE_URI"),
issuer=os.getenv("OIDC_ISSUER"),
signature_cache_ttl=int(os.getenv("OIDC_CACHE_TTL", "3600")),
)- ✅ Always use HTTPS in production
- ✅ Store credentials in environment variables, not in code
- ✅ Set cache TTL between 3600-7200 seconds for optimal balance
- ✅ Validate the issuer matches your authentication server
- ✅ Use short token expiration times (5-15 minutes recommended)
- ✅ Implement application-level rate limiting
- ✅ Monitor authentication logs for suspicious activity
See SECURITY.md for comprehensive security guidelines.
This is great if you just want to use something like Okta or google to handle your auth. All you need to do is verify the token and then you can extract user ID info from it.
from fastapi import Depends
from fastapi import FastAPI
# Set up our OIDC
from fastapi_oidc import IDToken
from fastapi_oidc import get_auth
OIDC_config = {
"client_id": "0oa1e3pv9opbyq2Gm4x7",
# Audience can be omitted in which case the aud value defaults to client_id
"audience": "https://yourapi.url.com/api",
"base_authorization_server_uri": "https://dev-126594.okta.com",
"issuer": "dev-126594.okta.com",
"signature_cache_ttl": 3600,
}
authenticate_user: Callable = get_auth(**OIDC_config)
app = FastAPI()
@app.get("/protected")
def protected(id_token: IDToken = Depends(authenticate_user)):
return {"Hello": "World", "user_email": id_token.email}The IDToken class will accept any number of extra field but if you want to craft your own token class and validation that's accounted for too.
class CustomIDToken(fastapi_oidc.IDToken):
custom_field: str
custom_default: float = 3.14
authenticate_user: Callable = get_auth(**OIDC_config, token_type=CustomIDToken)
app = FastAPI()
@app.get("/protected")
def protected(id_token: CustomIDToken = Depends(authenticate_user)):
return {"Hello": "World", "user_email": id_token.custom_default}Causes:
- Incorrect
client_idorissuerconfiguration - Token is expired
- Authentication server is unreachable
- Token signed with different key than expected
Solutions:
# Verify your configuration
print(f"Client ID: {client_id}")
print(f"Issuer: {issuer}")
print(f"Base URI: {base_authorization_server_uri}")
# Check token expiration
import jwt
decoded = jwt.decode(token, options={"verify_signature": False})
print(f"Token expires at: {decoded['exp']}")
# Verify network connectivity
import requests
response = requests.get(f"{base_authorization_server_uri}/.well-known/openid-configuration")
print(f"OIDC Discovery Status: {response.status_code}")Cause: The token's aud claim doesn't match your configuration.
Solution:
# Set the audience parameter explicitly
authenticate_user = get_auth(
client_id="your-client-id",
audience="your-expected-audience", # Must match token's 'aud' claim
base_authorization_server_uri="https://auth.example.com",
issuer="auth.example.com",
signature_cache_ttl=3600,
)
# Or check what audience your token contains
import jwt
decoded = jwt.decode(token, options={"verify_signature": False})
print(f"Token audience: {decoded['aud']}")Causes:
- Network connectivity issues
- Firewall blocking outbound HTTPS
- Incorrect base URI
Solutions:
# Test connectivity
curl https://your-auth-server.com/.well-known/openid-configuration
# Check firewall rules allow HTTPS to your auth server
# Verify the base URI is correct (no trailing slash)Solution:
# Ensure fastapi-oidc is installed
pip install fastapi-oidc
# Verify installation
python -c "import fastapi_oidc; print(fastapi_oidc.__version__)"
# Reinstall if needed
pip install --force-reinstall fastapi-oidcEnable detailed logging:
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("fastapi_oidc")
logger.setLevel(logging.DEBUG)- 📖 Check the documentation
- 🔍 Search existing issues
- 💬 Open a new issue
- 📧 See SECURITY.md for security-related concerns
Contributions are welcome! We appreciate bug fixes, documentation improvements, and new features.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests (
poetry run pytest) - Run code quality checks (
poetry run pre-commit run --all-files) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
See CONTRIBUTING.md for detailed guidelines.
# Clone the repository
git clone https://github.com/HarryMWinters/fastapi-oidc.git
cd fastapi-oidc
# Install dependencies
poetry install
# Set up pre-commit hooks
poetry run pre-commit install
# Run tests
poetry run pytest
# Run with coverage
poetry run pytest --cov=fastapi_oidcSecurity is a top priority. Please report security vulnerabilities privately to harrymcwinters@gmail.com.
See SECURITY.md for:
- Security best practices
- Supported versions
- Vulnerability reporting process
- Known security considerations
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with FastAPI
- Token validation via python-jose
- Type validation with Pydantic
Made with ❤️ by Harry M. Winters