"""Microsoft 365 厂商专属凭证 Pydantic 模型。 定义两个 ``AuthCredential`` 子类,分别对应应用权限(client_credentials)与 委托权限(authorization_code + PKCE)两种认证方式。 字段与 ``token_refresh.py`` 中刷新策略函数读取的 ``auth_config`` 字段一一对齐: - ``Microsoft365ClientCredential`` ↔ ``_refresh_microsoft365_client_credentials`` - ``Microsoft365AuthCodeCredential`` ↔ ``_refresh_microsoft365_authorization_code`` ``cloud`` 字段不在凭证中——它属于 ``connection_config``(见 ``cloud_endpoints.py`` 模块注释),由刷新策略从 ``auth_config["connection_config"]["cloud"]`` 读取。 ``scope`` 也不在凭证中——它由刷新策略根据 ``cloud`` 推导为 ``{graph_host}/.default``。 """ from __future__ import annotations from pydantic import ConfigDict from yuxi.external_systems.framework.auth_plugins.credentials import AuthCredential class Microsoft365ClientCredential(AuthCredential): """Microsoft 365 应用权限凭证(client_credentials 流)。 scope 由 ``cloud_endpoints`` 推导为 ``{graph_host}/.default``,不暴露为凭证字段。 cloud 从 ``connection_config`` 读取,不放在凭证中(见模块注释)。 """ model_config = ConfigDict(populate_by_name=True, extra="ignore") auth_type: str = "microsoft365_client_credentials" tenant_id: str client_id: str client_secret: str class Microsoft365AuthCodeCredential(AuthCredential): """Microsoft 365 委托权限凭证(authorization_code + PKCE)。 凭证存储的是首次授权码交换后回填的 ``refresh_token``,用于后续刷新。 ``code_verifier`` 仅在首次 token 交换时使用,刷新阶段不携带。 首次授权码交换由前端 PKCE 流程完成,交换成功后 ``refresh_token`` 持久化到本凭证。 """ model_config = ConfigDict(populate_by_name=True, extra="ignore") auth_type: str = "microsoft365_authorization_code" tenant_id: str client_id: str client_secret: str = "" # 公开客户端(SPA/移动端)可为空 refresh_token: str # 首次授权码交换后回填,后续刷新使用 code_verifier: str | None = None # 仅首次 token 交换使用,刷新阶段不携带