Return (False, None) instead of (True, None) when the OAuth token endpoint returns 200 but the response has no access_token field. Also moved Awaitable/Callable imports behind TYPE_CHECKING guard since they are only used for type annotations.
Stack Auth Python SDK
Python SDK for Stack Auth — the open-source authentication platform. Provides StackServerApp for server-side user management, team operations, JWT verification, and more.
Installation
pip install stack-auth
Quick Start
from stack_auth import StackServerApp
app = StackServerApp(
project_id="your-project-id",
secret_server_key="your-secret-server-key",
)
# List users
users = app.list_users(limit=10)
for user in users.items:
print(f"{user.display_name} ({user.primary_email})")
# Create a user
user = app.create_user(
primary_email="[email protected]",
password="securepassword",
display_name="Jane Doe",
)
# Get a user
user = app.get_user("user-id")
# Update a user
updated = app.update_user("user-id", display_name="New Name")
# Delete a user
app.delete_user("user-id")
Async Support
Every method has an async equivalent via AsyncStackServerApp:
from stack_auth import AsyncStackServerApp
app = AsyncStackServerApp(
project_id="your-project-id",
secret_server_key="your-secret-server-key",
)
user = await app.get_user("user-id")
users = await app.list_users(limit=10)
team = await app.create_team(display_name="Engineering")
Authentication
Verify incoming request tokens without framework-specific middleware:
from stack_auth._auth import sync_authenticate_request
from stack_auth._jwt import SyncJWKSFetcher
fetcher = SyncJWKSFetcher(
project_id="your-project-id",
base_url="https://api.stack-auth.com",
)
# Works with any object that has a .headers mapping
result = sync_authenticate_request(request, fetcher=fetcher)
if result.status == "authenticated":
print(f"User: {result.user_id}")
else:
print("Not authenticated")
FastAPI Example
from fastapi import Depends, HTTPException, Request
from stack_auth._auth import sync_authenticate_request
from stack_auth._jwt import SyncJWKSFetcher
fetcher = SyncJWKSFetcher(project_id="...", base_url="https://api.stack-auth.com")
def get_user_id(request: Request) -> str:
result = sync_authenticate_request(request, fetcher=fetcher)
if result.status != "authenticated":
raise HTTPException(status_code=401)
return result.user_id
@app.get("/protected")
def protected_route(user_id: str = Depends(get_user_id)):
return {"user_id": user_id}
Error Handling
All API errors raise typed exceptions:
from stack_auth.errors import (
StackAuthError,
AuthenticationError,
NotFoundError,
ValidationError,
)
try:
user = app.get_user("invalid-id")
except NotFoundError as e:
print(f"Not found: {e.message}")
except AuthenticationError as e:
print(f"Auth failed: {e.code}")
except StackAuthError as e:
print(f"API error: {e.code} - {e.message}")
Teams
# Create a team
team = app.create_team(
display_name="Engineering",
creator_user_id="user-id",
)
# List teams
teams = app.list_teams(user_id="user-id")
# Manage members
app.add_team_member(team_id=team.id, user_id="another-user-id")
profiles = app.list_team_member_profiles(team_id=team.id)
# Permissions
app.grant_team_permission(
team_id=team.id,
user_id="user-id",
permission_id="admin",
)
Self-Hosted
Point to your self-hosted Stack Auth instance:
app = StackServerApp(
project_id="your-project-id",
secret_server_key="your-secret-key",
base_url="https://your-stack-auth.example.com",
)
API Reference
StackServerApp / AsyncStackServerApp
Constructor:
project_id(str) — Your Stack Auth project IDsecret_server_key(str) — Server secret key from the dashboardbase_url(str, optional) — API base URL (default:https://api.stack-auth.com)publishable_client_key(str, optional) — Required for projects withrequirePublishableClientKeytoken_store(optional) — Token storage strategy
User methods: get_user, list_users, create_user, update_user, delete_user, get_user_by_api_key, get_partial_user
Team methods: get_team, list_teams, create_team, update_team, delete_team, get_team_by_api_key, add_team_member, remove_team_member, list_team_member_profiles, get_team_member_profile, send_team_invitation, list_team_invitations, revoke_team_invitation
Permission methods: grant_team_permission, revoke_team_permission, list_team_permissions, grant_user_permission, revoke_user_permission, list_user_permissions
Session methods: list_sessions, get_session, revoke_session
Contact channel methods: send_contact_channel_verification, verify_contact_channel, check_contact_channel_verification
API key methods: create_user_api_key, list_user_api_keys, revoke_user_api_key, create_team_api_key, list_team_api_keys, revoke_team_api_key, check_api_key
OAuth methods: list_oauth_providers, create_oauth_provider, get_oauth_provider, list_connected_accounts
Payment methods: list_products, get_item, grant_product, cancel_subscription
Email methods: send_email, get_email_delivery_stats
Data vault: get_data_vault_store → returns DataVaultStore with get, set, delete, list_keys
Development
# Clone the repo
git clone https://github.com/stack-auth/stack-auth.git
cd stack-auth/sdks/implementations/python
# Install in dev mode
pip install -e ".[dev]"
# Run unit tests (no server needed)
python3 -m pytest tests/ -v
# Run E2E tests (requires running Stack Auth)
# First: pnpm start-deps && pnpm dev (from monorepo root)
STACK_E2E=1 python3 -m pytest tests/test_e2e.py -v -s
Requirements
- Python >= 3.10
- httpx
- PyJWT[crypto]
- pydantic >= 2.7
License
MIT — same as Stack Auth.