AuthX-Identity
A standalone OpenID Connect (OIDC) identity microservice, built with FastAPI, plus a small Django client for services that need to talk to it.
Maintained by DjangoPlay.
Contents
- What is AuthX
- What's in this package
- Architecture
- Quickstart: running the microservice
- Quickstart: using the Django client
- API reference
- Configuration
- JWT verification
- Running in production
- Development
- License
What is AuthX
AuthX is a standards-compliant OIDC identity provider. It:
- Issues signed JWT access tokens and refresh tokens
- Exposes public OIDC endpoints (
/token,/userinfo,/jwks,/.well-known/openid-configuration) - Exposes an internal API (
/internal/identities) for trusted services to create and look up identities - Supports email/password and SSO (Google, Apple) identity providers
- Is stateless and horizontally scalable — any instance can validate any token, no shared session state
What's in this package
pip install authx-identity gives you two things bundled into one distribution:
| Package | Purpose | You need it if... |
|---|---|---|
authx |
The FastAPI microservice itself — DB models, Alembic migrations, API routes | You're running AuthX as its own deployed service |
authx_client |
A small Django client: create/look up identities over the internal API, verify AuthX JWTs locally | You have a Django app that consumes a running AuthX instance |
These ship together so a single pip install covers both sides, but each has its own dependencies:
pip install authx-identity # microservice only
pip install "authx-identity[django]" # adds Django, for the client
If you only need the client, you don't need to run the microservice's stack (Postgres, Alembic, etc.) yourself — just point it at wherever AuthX is already deployed.
Architecture
┌───────────────────────────────────┐
│ Client (browser, mobile, CLI) │
│ → POST /token (login) │
│ → GET /userinfo (who am I?) │
└────────────────┬──────────────────┘
│ JWT
┌───────────▼──────────────────┐
│ AuthX │
│ FastAPI + PostgreSQL │
│ Issues & validates JWTs │
└───────────┬──────────────────┘
│ Internal API (X-Service-Token)
┌───────────▼──────────────────┐
│ DjangoPlay │
│ Trusts AuthX JWTs │
│ Calls authx_client for │
│ identity create/lookup │
└────────────────────────────────┘
AuthX owns identity (email, password, SSO links). The consuming app (e.g. DjangoPlay) owns its own domain data and links to an identity by ID.
Quickstart: running the microservice
-
Clone this repo and copy the environment template:
cp env.example .env
-
Generate an RSA keypair for signing JWTs — do not reuse any keypair that has ever been committed to source control:
openssl genrsa -out private.pem 2048 openssl rsa -in private.pem -pubout -out public.pem
-
Inline both keys into
.envasJWT_PRIVATE_KEY/JWT_PUBLIC_KEY(single line, literal\nbetween lines — see the comment inenv.examplefor the exactawkcommand). -
Configure all required JWT settings:
JWT_ISSUER=https://auth.example.com JWT_AUDIENCE=your-application
JWT_ISSUERmust identify the actual AuthX deployment.JWT_AUDIENCEmust identify the application or API for which the token is intended. Neither value has a DjangoPlay-specific default. -
Fill in the remaining
.envvalues: database credentials,INTERNAL_SERVICE_TOKEN, andCORS_ORIGINS. -
Start everything:
docker compose up
Migrations run automatically on startup. The service listens on
http://localhost:8100by default. -
Confirm it's up:
curl http://localhost:8100/.well-known/openid-configuration
Quickstart: using the Django client
Install with the django extra so Django itself comes along:
pip install "authx-identity[django]"
Configure the required AuthX settings in your Django project's settings.py (see Configuration below), then:
from authx_client import AuthXClient, AuthXJWT, AuthXConflictError
# Server-to-server: create or look up identities
client = AuthXClient()
try:
identity = client.create_identity(
email="user@example.com",
username="user",
password="...",
)
except AuthXConflictError:
identity = client.get_by_email("user@example.com")
# Verify a JWT locally — no network call needed per request
payload = AuthXJWT.decode(token)
identity_id = AuthXJWT.get_identity_id(token)
AuthXClient is synchronous, since Django views and services typically are. It talks to AuthX's /internal/* endpoints using a shared service token — never expose that token to end users or client-side code.
API reference
Public OIDC endpoints
| Method | Path | Description |
|---|---|---|
| GET | /.well-known/openid-configuration |
OIDC discovery document |
| GET | /jwks |
Public keys for JWT verification |
| POST | /token |
Issue access + refresh token |
| POST | /token/refresh |
Refresh access token |
| GET | /userinfo |
Get identity info from a token |
Internal endpoints (require X-Service-Token)
| Method | Path | Description |
|---|---|---|
| POST | /internal/identities |
Create identity |
| GET | /internal/identities/{id} |
Get identity by ID |
| GET | /internal/identities/by-email/{email} |
Look up by email |
| GET | /internal/identities/by-sso/lookup |
Look up by SSO provider + ID |
| PATCH | /internal/identities/{id} |
Update identity fields |
| DELETE | /internal/identities/{id} |
Soft delete identity |
Internal endpoints are meant for trusted backend services only — never expose them publicly without the service token check in front.
Configuration
Microservice (.env)
All variables the FastAPI service reads are listed in env.example, grouped by purpose:
- Application —
APP_ENV,APP_HOST,APP_PORT,APP_BASE_URL - Database —
DATABASE_URL(async, for the app),DATABASE_URL_SYNC(for Alembic) - JWT signing —
JWT_PRIVATE_KEY,JWT_PUBLIC_KEY,JWT_ALGORITHM,JWT_ACCESS_TOKEN_EXPIRE_MINUTES,JWT_REFRESH_TOKEN_EXPIRE_DAYS,JWT_ISSUER,JWT_AUDIENCE - Internal auth —
INTERNAL_SERVICE_TOKEN, shared with consumers that call/internal/* - CORS —
CORS_ORIGINS, comma-separated
JWT_ISSUER and JWT_AUDIENCE are required settings. There are no DjangoPlay-specific defaults for either value.
JWT_ISSUER must be an HTTP(S) URL without a query string or fragment. HTTPS is required when APP_ENV=production.
JWT_AUDIENCE is an application/API identifier and does not need to be a URL.
Copy env.example to .env and fill in real values; never commit .env or any key material.
Django client settings
Set these in the consuming Django project's settings.py:
AUTHX_BASE_URL = "http://authx:8100"
AUTHX_SERVICE_TOKEN = "..." # must match the microservice's INTERNAL_SERVICE_TOKEN
AUTHX_PUBLIC_KEY = "..." # optional — omit to auto-fetch from /jwks on first use
AUTHX_JWT_ALGORITHM = "RS256"
AUTHX_JWT_AUDIENCE = "your-application"
AUTHX_JWT_ISSUER = "https://auth.example.com"
AUTHX_JWT_AUDIENCE and AUTHX_JWT_ISSUER must exactly match the corresponding JWT_AUDIENCE and JWT_ISSUER configured in the AuthX service.
There is no DjangoPlay-specific fallback for either value. If either setting is missing from the consuming Django application, JWT verification will fail instead of silently using an incorrect issuer or audience.
AUTHX_BASE_URL and AUTHX_SERVICE_TOKEN remain required for the internal API client. AUTHX_PUBLIC_KEY and AUTHX_JWT_ALGORITHM retain their existing optional/default behavior.
JWT verification
Consumers should:
- Fetch public keys from
/jwksonce and cache them (or setAUTHX_PUBLIC_KEYdirectly to skip the fetch). - Verify JWT signatures locally — no call to AuthX needed per request.
- Validate both the
issandaudclaims against the explicitly configuredAUTHX_JWT_ISSUERandAUTHX_JWT_AUDIENCE. - Only call
/userinfofor server-to-server lookups when you don't already have a JWT in hand.
The issuer identifies the AuthX deployment that issued the token. The audience identifies the application or API for which the token is intended.
Running in production
docker compose -f docker-compose.yml -f docker-compose.fullstack.yml up -d
Make sure .env has production-grade secrets (fresh JWT keypair, strong INTERNAL_SERVICE_TOKEN) and that /internal/* is not reachable from outside your private network.
In production, JWT_ISSUER must use HTTPS and must identify the actual public issuer URL for the AuthX deployment.
Development
pip install -e ".[dev]"
pytest
ruff check .
See CHANGELOG.md for release history.
License
MIT — see LICENSE.
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 authx_identity-1.0.1.tar.gz.
File metadata
- Download URL: authx_identity-1.0.1.tar.gz
- Upload date:
- Size: 23.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
68f0f49e3a69ec67eaaf3c9526726f7280e49186ae8c81ddfe7e18c629a04639
|
|
| MD5 |
8194104b0dee7ad6181232b57ce4e8a5
|
|
| BLAKE2b-256 |
97a11965ddb521a2e754ed63deb66342562a5e058c256fbf181465f0ea00f07f
|
File details
Details for the file authx_identity-1.0.1-py3-none-any.whl.
File metadata
- Download URL: authx_identity-1.0.1-py3-none-any.whl
- Upload date:
- Size: 24.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d70a15c9c7c22e48cc9a5118b658e45f40e85b31d7a88b09f324e867816f4dc3
|
|
| MD5 |
246e22a2d5178a75b1c8620e29d668b7
|
|
| BLAKE2b-256 |
18e99bb0794223451a4ac0b2b57d7e6b337423c69a5839c6cbee54ff4d58b4ba
|