Metadata-Version: 2.4
Name: keycloak-mockup
Version: 0.1.0.post1.dev0
Summary: A minimal Keycloak-compatible OIDC/OAuth2 mock server
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Framework :: FastAPI
Requires-Python: >=3.13
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi>=0.111.3
Requires-Dist: uvicorn[standard]>=0.29.0
Requires-Dist: pyjwt[crypto]>=2.8.0
Requires-Dist: cryptography>=42.0.0
Requires-Dist: pyyaml>=6.0.0
Requires-Dist: jinja2>=3.1.0
Requires-Dist: python-multipart>=0.0.9
Provides-Extra: dev
Requires-Dist: pytest>=9.0.3; extra == "dev"
Requires-Dist: requests>=2.34.2; extra == "dev"
Requires-Dist: python-keycloak>=7.1.1; extra == "dev"
Dynamic: license-file

# Keycloak Mockup

[![CI](https://img.shields.io/github/actions/workflow/status/calcite/keycloak-mockup/ci.yml?branch=main)](https://github.com/calcite/keycloak-mockup/actions/workflows/ci.yml)
[![Release](https://img.shields.io/github/actions/workflow/status/calcite/keycloak-mockup/release.yml?label=release)](https://github.com/calcite/keycloak-mockup/actions/workflows/release.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

![Keycloak Mockup demo client](image.png)

Keycloak Mockup is a lightweight **Keycloak-compatible OIDC/OAuth2 mock server** for local development, integration testing, and CI pipelines.

It provides a fake identity provider with configurable users, roles, JWT claims, JWKS, login page, token endpoint, userinfo endpoint, logout endpoint, and a built-in browser demo client for testing authentication flows.

By default, no runtime options are required:

- the built-in `config.yaml` is used automatically
- the demo client is served at `/`
- the server listens on port `5151`

> ⚠️ Keycloak Mockup is intended for development and testing only. Do not use it in production.

## Main use cases

- Run a local Keycloak-like identity provider without installing Keycloak.
- Test backend services that expect OIDC/OAuth2 and JWT tokens.
- Test browser clients using the Keycloak JavaScript adapter.
- Provide deterministic users and roles in CI/integration tests.
- Inspect issued tokens and userinfo data through the built-in demo client.

## Quick start with Docker Compose

Create `docker-compose.yml`:

```yaml
services:
  keycloak-mockup:
    image: ghcr.io/calcite/keycloak-mockup:latest
    ports:
      - "5151:5151"
```

Start the service:

```bash
docker compose up
```

Open:

- Demo client: <http://localhost:5151/>
- OIDC discovery: <http://localhost:5151/realms/myrealm/.well-known/openid-configuration>

The demo client lets you click **Login**, authenticate against Keycloak Mockup, and inspect the data returned to the browser client: parsed token claims, raw tokens, adapter state, and userinfo response.

Default users are defined in the packaged `config.yaml`, for example:

- `admin` / `admin123`
- `operator` / `operator123`
- `viewer` / `viewer123`

### Docker Compose with a custom config

To customize users, roles, realm, client ID, token TTLs, or advertised base URL, mount your own config file:

```bash
cp src/keycloak_mockup/config.example.yaml ./config.yaml
```

```yaml
services:
  keycloak-mockup:
    image: ghcr.io/calcite/keycloak-mockup:latest
    ports:
      - "5151:5151"
    volumes:
      - ./config.yaml:/config.yaml:ro
    command: ["keycloak-mockup", "--config", "/config.yaml"]
```

### Docker Compose with `/demo-client/` path

The demo client is mounted at `/` by default. To move it to `/demo-client/`:

```yaml
services:
  keycloak-mockup:
    image: ghcr.io/calcite/keycloak-mockup:latest
    ports:
      - "5151:5151"
    environment:
      DEMO_CLIENT_PATH: "/demo-client/"
```

Then open:

- <http://localhost:5151/demo-client/>

## Install with pip

Install from PyPI:

```bash
pip install keycloak-mockup
```

Run with the packaged default config:

```bash
keycloak-mockup
```

Open:

- <http://localhost:5151/>

Install from a local checkout:

```bash
pip install .
keycloak-mockup
```

Run as a Python module:

```bash
python -m keycloak_mockup
```

Use a custom config when needed:

```bash
keycloak-mockup --config ./config.yaml
```

## Configuration

Runtime behavior is controlled by a YAML or JSON config file. If `--config` is omitted, the packaged `config.yaml` is used.

Minimal example:

```yaml
realm: myrealm
client_id: myclient
host: "0.0.0.0"
port: 5151
base_url: "http://localhost:5151"
insecure_cookies: true

users:
  - username: admin
    password: admin123
    email: admin@example.com
    name: Admin User
    enabled: true
    autologin: true
    realm_roles:
      - ADMIN
    client_roles:
      myclient:
        - admin
```

See the full reference in [docs/configuration.md](docs/configuration.md).

## Built-in demo client

The app includes a static browser demo client based on the Keycloak JavaScript adapter.

Default URL:

- `http://localhost:5151/`

Optional path override:

```bash
DEMO_CLIENT_PATH=/demo-client/ keycloak-mockup
```

The page supports:

- Login redirect through Keycloak Mockup
- Logout
- Token refresh
- Userinfo reload
- Displaying parsed token claims
- Displaying raw access/refresh/ID tokens when available
- Displaying adapter state and selected client configuration

> The demo client displays sensitive token data in the browser. Use it only for local development and testing.

## Main endpoints

- OIDC discovery: `/realms/{realm}/.well-known/openid-configuration`
- Realm info: `/realms/{realm}`
- Authorization: `/realms/{realm}/protocol/openid-connect/auth`
- Token: `/realms/{realm}/protocol/openid-connect/token`
- Userinfo: `/realms/{realm}/protocol/openid-connect/userinfo`
- Logout: `/realms/{realm}/protocol/openid-connect/logout`
- JWKS: `/realms/{realm}/protocol/openid-connect/certs`

## Supported flows

- Authorization Code flow
- Refresh token flow with rotation
- Client Credentials flow
- Browser login with session cookie
- Silent SSO support for Keycloak JS testing

## Example access token payload

```json
{
  "iss": "http://localhost:5151/realms/myrealm",
  "sub": "a1b2c3d4-...",
  "aud": "myclient",
  "email": "admin@example.com",
  "name": "Admin User",
  "preferred_username": "admin",
  "realm_access": { "roles": ["ADMIN"] },
  "resource_access": {
    "myclient": { "roles": ["admin"] }
  }
}
```

## Client examples

- [Python client example](docs/python-keycloak.md)
- [Browser client example](docs/keycloak-js.md)
- [Configuration reference](docs/configuration.md)

## Development with pip

The project uses standard Python packaging and plain `pip`; PDM is not required.

Create a virtual environment and install the package with development dependencies:

```bash
python -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
```

Run tests:

```bash
pytest -q
```

Build the Python package and Docker image locally:

```bash
python -m pip install build
python -m build
docker build -t keycloak-mockup .
```

## Release

A release is created by pushing a version tag such as `v0.1.0`:

```bash
git tag v0.1.0
git push origin v0.1.0
```

The GitHub Actions release workflow will:

1. build the Python source distribution and wheel
2. upload the Python package to PyPI
3. build a Docker image from the generated wheel
4. push the Docker image to this repository's GitHub Container Registry package (`ghcr.io/<owner>/<repo>`)
5. create a GitHub Release with the Python artifacts attached

Required GitHub/PyPI setup:

- Configure PyPI Trusted Publishing for this project on `pypi.org`.
- In PyPI, add this GitHub repository as a trusted publisher for workflow `.github/workflows/release.yml`.
- No `PYPI_API_TOKEN` secret is required; the workflow publishes with GitHub OIDC via `pypa/gh-action-pypi-publish`.
- Ensure workflow permissions allow package publishing. In GitHub repository settings, enable **Actions → General → Workflow permissions → Read and write permissions** if needed.
- The workflow uses `GITHUB_TOKEN` for GitHub Releases and GHCR publishing; no extra GHCR token should be needed for the same repository.

## License

MIT License — see [LICENSE](LICENSE).
