Metadata-Version: 2.4
Name: authx-identity
Version: 1.0.0
Summary: Standalone OIDC identity microservice and client library for DjangoPlay and Python applications.
Author: CodeFleet Labs
License-Expression: MIT
Project-URL: Homepage, https://github.com/codefleetx/authx
Project-URL: Repository, https://github.com/codefleetx/authx
Project-URL: Issues, https://github.com/codefleetx/authx/issues
Keywords: authx,oidc,oauth2,identity,authentication,jwt,fastapi,microservice,djangoplay
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Framework :: FastAPI
Classifier: Environment :: Web Environment
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi>=0.111.0
Requires-Dist: uvicorn[standard]>=0.29.0
Requires-Dist: sqlalchemy>=2.0.0
Requires-Dist: alembic>=1.13.0
Requires-Dist: asyncpg>=0.29.0
Requires-Dist: psycopg2-binary>=2.9.0
Requires-Dist: passlib[bcrypt]>=1.7.4
Requires-Dist: python-jose[cryptography]>=3.3.0
Requires-Dist: cryptography>=42.0.0
Requires-Dist: python-multipart>=0.0.9
Requires-Dist: pydantic>=2.7.0
Requires-Dist: pydantic-settings>=2.2.0
Requires-Dist: email-validator>=2.0.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: structlog>=24.1.0
Provides-Extra: django
Requires-Dist: django>=4.2; extra == "django"
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
Requires-Dist: aiosqlite>=0.20.0; extra == "dev"
Requires-Dist: ruff>=0.4.0; extra == "dev"
Requires-Dist: build>=1.2.0; extra == "dev"
Requires-Dist: twine>=5.0.0; extra == "dev"
Dynamic: license-file

# AuthX-Identity

[![Python](https://img.shields.io/pypi/pyversions/authx-identity)](https://pypi.org/project/authx-identity/)
[![PyPI](https://img.shields.io/pypi/v/authx-identity)](https://pypi.org/project/authx-identity/)
[![License](https://img.shields.io/badge/license-MIT-green)](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](https://djangoplay.org).

---

## Contents

- [What is AuthX](#what-is-authx)
- [What's in this package](#whats-in-this-package)
- [Architecture](#architecture)
- [Quickstart: running the microservice](#quickstart-running-the-microservice)
- [Quickstart: using the Django client](#quickstart-using-the-django-client)
- [API reference](#api-reference)
- [Configuration](#configuration)
- [JWT verification](#jwt-verification)
- [Running in production](#running-in-production)
- [Development](#development)
- [License](#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:

```bash
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:

   ```bash
   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:

   ```bash
   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:

   ```bash
   docker compose up
   ```

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

5. Confirm it's up:

   ```bash
   curl http://localhost:8100/.well-known/openid-configuration
   ```

## Quickstart: using the Django client

Install with the `django` extra so Django itself comes along:

```bash
pip install "authx-identity[django]"
```

Add the required settings to your Django project (see [Configuration](#configuration) below), then:

```python
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`](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`:

```python
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

```bash
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

```bash
pip install -e ".[dev]"
pytest
ruff check .
```

See [CHANGELOG.md](CHANGELOG.md) for release history.

## License

MIT — see [LICENSE](LICENSE).
