Standalone OIDC identity microservice and client library for DjangoPlay and Python applications.
Project description
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). Fill in the rest of.env: database credentials,INTERNAL_SERVICE_TOKEN,CORS_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]"
Add the required settings to your Django project (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
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 = "djangoplay"
AUTHX_JWT_ISSUER = "https://auth.djangoplay.org"
Only AUTHX_BASE_URL and AUTHX_SERVICE_TOKEN are required; the rest fall back to the defaults shown above.
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.
- Only call
/userinfofor server-to-server lookups when you don't already have a JWT in hand.
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.
Development
pip install -e ".[dev]"
pytest
ruff check .
See CHANGELOG.md for release history.
License
MIT — see LICENSE.
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 authx_identity-1.0.0.tar.gz.
File metadata
- Download URL: authx_identity-1.0.0.tar.gz
- Upload date:
- Size: 22.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32ad63b71b3b8aef5dd19853c4e3553b921243e3bf3dd1a8bd9ecb4c808ec5ec
|
|
| MD5 |
e26ace5eee58efb845030108ce19c63a
|
|
| BLAKE2b-256 |
c1b9cbe51ccef0b80be6a5cb8f905242bcd05e23753201efa968cbf056dbabdc
|
File details
Details for the file authx_identity-1.0.0-py3-none-any.whl.
File metadata
- Download URL: authx_identity-1.0.0-py3-none-any.whl
- Upload date:
- Size: 24.0 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 |
e90b2a57cc1f16511e1f741b16e5cd3f9c957928a6f225153b9b6557febda1ac
|
|
| MD5 |
eeeb7f0ad4b78a2e5ab2eb77efbef86e
|
|
| BLAKE2b-256 |
9f9c869b8f462a781f6b38434f34359d9427654bc9f4d8d3faf48ee45a2171af
|