Metadata-Version: 2.4
Name: py-oidc-auth-client
Version: 2603.0.0
Summary: Client library for the OIDC Auth server.
Author-email: "DKRZ, Clint" <freva@dkrz.de>
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx
Requires-Dist: platformdirs
Requires-Dist: rich
Requires-Dist: typing_extensions
Requires-Dist: rich-argparse ; extra == "cli"
Requires-Dist: tox ; extra == "dev"
Requires-Dist: cryptography ; extra == "dev"
Requires-Dist: uvicorn ; extra == "dev"
Requires-Dist: py-oidc-auth[fastapi, tests] ; extra == "dev"
Requires-Dist: sphinx-execute-code-python3 ; extra == "docs"
Requires-Dist: sphinx-code-tabs ; extra == "docs"
Requires-Dist: sphinx-copybutton ; extra == "docs"
Requires-Dist: sphinx-sitemap ; extra == "docs"
Requires-Dist: sphinx-togglebutton ; extra == "docs"
Requires-Dist: sphinxcontrib-httpdomain ; extra == "docs"
Requires-Dist: sphinxext-opengraph ; extra == "docs"
Requires-Dist: sphinx-design ; extra == "docs"
Requires-Dist: pydata-sphinx-theme ; extra == "docs"
Requires-Dist: pytest ; extra == "tests"
Requires-Dist: pytest-env ; extra == "tests"
Requires-Dist: mock ; extra == "tests"
Requires-Dist: pytest-mock ; extra == "tests"
Requires-Dist: pytest-cov ; extra == "tests"
Requires-Dist: pytest-asyncio ; extra == "tests"
Requires-Dist: requests ; extra == "tests"
Requires-Dist: uvicorn ; extra == "tests"
Requires-Dist: fastapi ; extra == "tests"
Requires-Dist: python-multipart ; extra == "tests"
Requires-Dist: PyJWT[crypto] ; extra == "tests"
Project-URL: Documentation, https://py-oidc-auth-client.readthedocs.io
Project-URL: Issues, https://github.com/freva-org/py-oidc-auth-client/issues
Project-URL: Source, https://github.com/freva-org/py-oidc-auth-client/
Provides-Extra: cli
Provides-Extra: dev
Provides-Extra: docs
Provides-Extra: tests

<p align="center">
  <img src="https://raw.githubusercontent.com/freva-org/py-oidc-auth-client/main/docs/source/_static/logo.png" alt="py-oidc-auth-client logo" width="560">
</p>
<p align="center">
<em>Typed client library for the authentication routes exposed by py-oidc-auth.</em>
</p>

[![License](https://img.shields.io/badge/License-BSD-purple.svg)](LICENSE)
[![codecov](https://codecov.io/gh/freva-org/py-oidc-auth-client/graph/badge.svg?token=9JP9UWixaf)](https://codecov.io/gh/freva-org/py-oidc-auth-client)
[![docs](https://readthedocs.org/projects/py-oidc-auth-client/badge/?version=latest)](https://py-oidc-auth-client.readthedocs.io/en/latest/?badge=latest)
[![PyPI](https://img.shields.io/pypi/v/py-oidc-auth-client)](https://pypi.org/project/py-oidc-auth-client/)
[![Python Versions](https://img.shields.io/pypi/pyversions/py-oidc-auth-client)](https://pypi.org/project/py-oidc-auth-client/)



py-oidc-auth-client is the counterpart of the server-side library **py-oidc-auth**.

While **py-oidc-auth** helps you add OpenID Connect login, token, and device endpoints to web frameworks,
**py-oidc-auth-client** consumes those routes and gives you ready-to-use bearer tokens for calling protected APIs.

## Features

* One high level helper: `authenticate()`
* Device flow for headless sessions
* Authorization code flow for interactive logins
* Token caching and refresh token support via a token file
* Fully typed public API

## Install

```console
python -m pip install py-oidc-auth-client
```

Import name is `py_oidc_auth_client`:

```python
from py_oidc_auth_client import authenticate
```

## Relationship to py-oidc-auth

A typical **py-oidc-auth** server exposes endpoints similar to:

* `GET  /auth/v2/login`
* `GET  /auth/v2/callback`
* `POST /auth/v2/token`
* `POST /auth/v2/device`
* `GET  /auth/v2/logout`
* `GET  /auth/v2/userinfo`

This client calls the relevant routes (token and device, and possibly login/callback) and returns a `Token`
object that contains a ready-made `Authorization` header.

## Quick start

```python
from py_oidc_auth_client import authenticate

token = authenticate(host="https://auth.example.org")

# Use with any HTTP client
headers = token["headers"]
print(headers["Authorization"])
```

### Use with httpx

```python
import httpx
from py_oidc_auth_client import authenticate

token = authenticate(host="https://auth.example.org")

with httpx.Client() as client:
    r = client.get("https://service.example.org/protected", headers=token["headers"])
    r.raise_for_status()
    print(r.json())
```

## Token persistence

By default, the client stores tokens in a cache file so you do not have to re-authenticate on every run.
You can control where tokens are stored with `token_file`:

```python
from py_oidc_auth_client import authenticate, TokenStore

token = authenticate(
    host="https://auth.example.org",
    store=TokenStore(path="~/.cache/py-oidc-auth-client/token.json"),
)
```

You can also point to a token file via environment variable:

* `OIDC_TOKEN_FILE`

## Interactive and non-interactive environments

The client tries to select a suitable strategy:

1. Use a valid cached access token.
2. Refresh using the refresh token.
3. If interactive authentication is possible, fall back to an interactive login.
4. If running in a non-interactive session without a usable token, raise an error telling you how to provide a token file.

For headless sessions, the device flow is the recommended approach.

## Advanced usage

If you need more control than `authenticate()`, use the flow helpers from `py_oidc_auth_client.auth`.

### Device flow

```python
import asyncio
from py_oidc_auth_client import Config, DeviceFlow

async def main() -> None:
    cfg = Config(host="https://auth.example.org")
    flow = DeviceFlow(config=cfg, token=None, timeout=600)

    device = await flow.get_device_code()
    print("Open:", device.uri)
    print("Code:", device.user_code)

    token = await flow.poll(device["device_code"], int(device["interval"]))
    print(token["headers"])

asyncio.run(main())
```

### Authorization code flow

```python
import asyncio
from py_oidc_auth_client import Config, CodeFlow

async def main() -> None:
    cfg = Config(host="https://auth.example.org")
    flow = CodeFlow(config=cfg, token=None, timeout=120)
    token = await flow.authenticate()
    print(token["headers"])

asyncio.run(main())
```

## Contributing

Contributions are welcome. Please open an issue to discuss larger changes before submitting a pull request.

