Metadata-Version: 2.4
Name: easecation-iam-client
Version: 1.4.4
Summary: IAM V2 client library for EaseCation services (Python port of @easecation/iam-client)
License-Expression: LicenseRef-Proprietary
Keywords: iam,oauth,jwt,rbac,easecation
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"

# easecation-iam-client

Python port of [@easecation/iam-client](https://www.npmjs.com/package/@easecation/iam-client). IAM V2 API wrapper for EaseCation services.

## Install

```bash
pip install easecation-iam-client
```

Or from repo:

```bash
pip install -e packages/iam-client-py
```

## Usage

```python
from easecation_iam_client import IamClient, create_iam_client_from_env

# From env (IAM_BASE_URL, IAM_CLIENT_ID, IAM_CLIENT_SECRET)
client = create_iam_client_from_env()

# Or explicit config
client = IamClient(
    iam_base_url="https://iamapi.easecation.net",
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
)

# API-key-only Open V2 Data calls do not require client_id/client_secret.
api_key_only_client = IamClient(
    iam_base_url="https://iamapi.easecation.net",
    api_key="ec_xxx",
)
feishu_users = api_key_only_client.request_open_v2_data(
    "/feishu/batch",
    body={"uids": [123]},
)

# Or override the API key for one request.
permission_users = client.request_open_v2_data(
    "/permission/user",
    api_key="ec_xxx",
    body={"permission_code": "5#sen.admin"},
)

# OAuth code exchange
tokens = client.exchange_oauth_code(code, redirect_uri)

# Verify access token
verify = client.verify_token(access_token)

# API key verification
api_key_data = client.verify_api_key(key_value)

# App session verification
valid = client.verify_app_session_token(app_session_token)

# Cross-app callback with IAM-managed API key refresh/retry.
player = client.with_app_api_key(
    lambda api_key: ecapi.player.get_info(
        {"displayName": "Steve"},
        auth={"type": "apiKey", "apiKey": api_key},
    ),
    name="service -> ECAPI",
)

# Business delegation: app A calls app B as an authorized user without seeing
# the user's own API key. Requirements:
# 1. User OAuth-authorized app A with scope "<target_app_id>.permission".
# 2. App B granted app A scope "iam.business.delegate" in App Grants.
me_response = client.request_with_delegated_user_api_key(
    "https://ecapi.example.test/users/me",
    api_key="ec_app_key_for_app_a",  # optional; omit to use config api_key/get_app_api_key()
    user_id=123,
    target_application_id=5,
)
me = me_response.json()
```

## API

- `get_app_session_token()`, `invalidate_app_token()`
- `exchange_oauth_code(code, redirect_uri)`
- `verify_token(access_token)`, `evict_from_verify_cache(access_token)`
- `refresh_token(refresh_token)`, `revoke_token(refresh_token)`
- `get_jwks()`, `get_user_profile(access_token)`, `get_user_bootstrap(...)`, `get_hr_bootstrap_data(...)`
- `verify_iam_callback_token(iam_token, ...)`, `get_user_permissions(user_id, ...)`
- `verify_api_key(key_value)`, `evict_api_key_from_cache(key_value)`, `clear_api_key_cache()`
- `verify_app_session_token(app_session_token)`, `clear_app_session_verify_cache()`
- `get_app_api_key(...)`, `with_app_api_key(callback, ...)`
- `get_delegated_user_api_key(...)`, `get_delegated_user_api_key_data(...)`
- `request_with_delegated_user_api_key(url, api_key=..., user_id=..., target_application_id=..., ...)`
- `request_open_v2_data(path, api_key=..., body=..., params=..., method=...)`

`ApiKeyRotationHelper` for zero-downtime key rotation: `create_new_key(...)`, `revoke_key(key_id, ...)`, `list_active_keys(owner_id?)`.
