Metadata-Version: 2.4
Name: blade-auth-client
Version: 0.4.15
Summary: Shared Casdoor authentication client for Blade ecosystem services.
Project-URL: Homepage, https://pypi.org/project/blade-auth-client/
Author-email: yangliu35 <so.2liu@gmail.com>
License-Expression: MIT
Requires-Python: >=3.12
Requires-Dist: httpx
Requires-Dist: pydantic-settings
Requires-Dist: pydantic>=2
Requires-Dist: pyjwt[crypto]
Requires-Dist: pyyaml
Requires-Dist: sqlalchemy
Provides-Extra: dev
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: pytest-asyncio; extra == 'dev'
Requires-Dist: respx; extra == 'dev'
Requires-Dist: ruff<0.12,>=0.11; extra == 'dev'
Provides-Extra: fastapi
Requires-Dist: fastapi; extra == 'fastapi'
Requires-Dist: python-multipart; extra == 'fastapi'
Requires-Dist: starlette; extra == 'fastapi'
Provides-Extra: socketio
Requires-Dist: python-socketio; extra == 'socketio'
Description-Content-Type: text/markdown

# blade-auth-client

`blade-auth-client` 是 Blade 生态的共享 Casdoor 认证客户端包。它抽离了统一的 token 校验、FastAPI 装配和 Socket.IO 握手接口，供各个服务复用。

## 安装

```bash
pip install blade-auth-client[fastapi,socketio]
```

## Casdoor 配置要求

Casdoor 的 application、scope 和 redirect URI 约束见 [docs/casdoor-setup.md](docs/casdoor-setup.md)。

## 快速上手(0.4.0+)

```python
from fastapi import FastAPI, Depends
from blade_auth_client import AuthConfig, BladeAuth, CasdoorClaims, LazyProvisioner

class MyProvisioner(LazyProvisioner["MyUser"]):
    async def provision_user(self, claims: CasdoorClaims) -> "MyUser":
        # 首次登录:建用户并绑定 claims.sub;非首次:查出来刷字段
        ...

auth = BladeAuth(
    AuthConfig.from_yaml("configs/oauth_config.yaml"),
    provisioner=MyProvisioner(),
)

app = FastAPI()
app.include_router(auth.router, prefix="/api/v1/auth")

@app.get("/me")
def me(user = auth.require()):
    return user
```

详细流程见 [`docs/接入指南.md`](docs/接入指南.md);多 app 共享鉴权 / BladeAuth 设计背景见 [`docs/设计/设计-API暴露面收敛.md`](docs/设计/设计-API暴露面收敛.md)。

### 从 0.3.x 升级

- 推荐迁到 `BladeAuth` facade(上面那段)。老零件(`OidcClient` / `TokenVerifier` / `create_auth_router` / `make_require_auth_dep` / 等)0.4.0 仍可从顶层 import,但会 `DeprecationWarning`,0.5.0 移除。
- `LazyProvisioner.ensure_user` → **`provision_user`**。老名字继续可用且 SDK 内部自动回落,调用时 warn 一次。

## Web 会话与组织

系统里有两类长期凭据，生命周期不同，别混用：

| | web 令牌 | PAT |
|---|---|---|
| 是什么 | 密码登录换来的 RS256 JWT | 不透明的 `sk-blade-v3-…` |
| 代表 | *某人这次登录* | *某人这个身份* |
| `logout` 后 | 立即失效 | 不受影响 |
| 适合 | 网页 / 客户端会话 | 脚本、服务间调用 |

会话用 web 令牌、自动化用 PAT。拿 PAT 当会话使，用户点了登出也退不掉；拿 web
令牌做自动化，用户一登出你的定时任务就断了。

```python
from blade_auth_client import SessionClient, OrgClient
from blade_auth_client.session import AccountLockedError
from blade_auth_client.errors import InvalidTokenError

sc = SessionClient("http://blade-oauth:19000")

try:
    token = await sc.login("alice", "secret")
except AccountLockedError:      # 429：连续失败被冻结
    ...
except InvalidTokenError:       # 401：账号或密码不对
    ...

me = await sc.me(token.access_token)     # SessionUser（含 source / source_id）
await sc.logout(token.access_token)      # 服务端拉黑，之后一律 401
```

`logout` 只作用于传入的这一枚令牌：同一用户在别处的会话、以及他的 PAT 都不受
影响。需要「所有设备下线」用管理端的 `DELETE /api/v1/users/{id}/sessions`。
重复登出不报错——登出表达的是意图，令牌本来就无效同样满足这个意图。

本地口令校验不过时，blade-oauth 会转发给管理员配置的外部认证源，认证通过即自动
建号；对调用方没有区别，拿到的始终是 blade-oauth 自己签的令牌。`SessionUser.source`
告诉你这个账号从哪来（`admin_api` / `bootstrap` / `external` / `legacy`）。

```python
oc = OrgClient("http://blade-oauth:19000")

roster = await oc.my_members(token)              # 我同组织的人，普通用户可调
orgs = await oc.list(admin_token)                # 需要 admin
org = await oc.get(admin_token, "org-a")
members = await oc.members(admin_token, "org-a")
```

`my_members` 返回**脱敏视图**——`OrgMember` 刻意没有邮箱、手机、额度等字段，
避免花名册被当成全公司通讯录来爬。注意判断 `OrgRoster.truncated`：花名册上限
500 人，服务端会如实告诉你名单是否被截断。

## 版本策略

`0.0.x` 版本仅用于占位发布和联调验证，不承诺可用性。`0.2.0` 起采用仅校验 `iss` + 签名 + `exp` 的简化策略。

## 发布

仓库已经预留了 GitHub Actions 发布流水线：

- PR 校验：`.github/workflows/python-sdk-ci.yml`
- PyPI 发布：`.github/workflows/python-sdk-publish.yml`

首次配置 Trusted Publishing 时，在 PyPI 项目的 Publishing 页面添加 GitHub publisher，填写：

- Owner: `blade-hq`
- Repository name: `blade-oauth`
- Workflow name: `python-sdk-publish.yml`

日常发版流程：

```bash
# 1. 修改 sdk/python/pyproject.toml 里的 version

# 2. 推送代码到默认分支后，创建并推送同版本 tag
git tag blade-auth-client-vX.Y.Z
git push origin blade-auth-client-vX.Y.Z
```

发布 workflow 会校验 tag 版本与 `pyproject.toml` 一致，然后自动构建并发布到 PyPI。
