Skip to main content

Standalone OIDC identity microservice and client library for DjangoPlay and Python applications.

Project description

AuthX-Identity

Python PyPI License

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

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

  1. Clone this repo and copy the environment template:

    cp env.example .env
    
  2. 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
    
  3. Inline both keys into .env as JWT_PRIVATE_KEY / JWT_PUBLIC_KEY (single line, literal \n between lines — see the comment in env.example for the exact awk command). Fill in the rest of .env: database credentials, INTERNAL_SERVICE_TOKEN, CORS_ORIGINS.

  4. Start everything:

    docker compose up
    

    Migrations run automatically on startup. The service listens on http://localhost:8100 by default.

  5. 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:

  • ApplicationAPP_ENV, APP_HOST, APP_PORT, APP_BASE_URL
  • DatabaseDATABASE_URL (async, for the app), DATABASE_URL_SYNC (for Alembic)
  • JWT signingJWT_PRIVATE_KEY, JWT_PUBLIC_KEY, JWT_ALGORITHM, JWT_ACCESS_TOKEN_EXPIRE_MINUTES, JWT_REFRESH_TOKEN_EXPIRE_DAYS, JWT_ISSUER, JWT_AUDIENCE
  • Internal authINTERNAL_SERVICE_TOKEN, shared with consumers that call /internal/*
  • CORSCORS_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:

  1. Fetch public keys from /jwks once and cache them (or set AUTHX_PUBLIC_KEY directly to skip the fetch).
  2. Verify JWT signatures locally — no call to AuthX needed per request.
  3. Only call /userinfo for 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

authx_identity-1.0.0.tar.gz (22.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

authx_identity-1.0.0-py3-none-any.whl (24.0 kB view details)

Uploaded Python 3

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

Hashes for authx_identity-1.0.0.tar.gz
Algorithm Hash digest
SHA256 32ad63b71b3b8aef5dd19853c4e3553b921243e3bf3dd1a8bd9ecb4c808ec5ec
MD5 e26ace5eee58efb845030108ce19c63a
BLAKE2b-256 c1b9cbe51ccef0b80be6a5cb8f905242bcd05e23753201efa968cbf056dbabdc

See more details on using hashes here.

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

Hashes for authx_identity-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e90b2a57cc1f16511e1f741b16e5cd3f9c957928a6f225153b9b6557febda1ac
MD5 eeeb7f0ad4b78a2e5ab2eb77efbef86e
BLAKE2b-256 9f9c869b8f462a781f6b38434f34359d9427654bc9f4d8d3faf48ee45a2171af

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page