OA 安全会话与凭据
当前版本:0.5.2(以 VERSION 文件为准)
版本自报(P2-2):首次执行 OA 动作前,在回复中回显 [oa-auth-session v0.5.2](以包内 VERSION 文件为准);排障时先核对版本再排查,不凭记忆猜测。
把 Token 视为宿主运行时秘密。本 Skill 先选择唯一 OA 运输,再为独立宿主解析管理员控制、工作区外的受信环境,管理当前操作系统用户的凭据,并执行只读人员状态预检;不调用 OA 业务写接口。支持明确标记的 test 与 production 环境、同一 profile 内的多环境精确绑定、分发模板首次可信浏览器登录自动账号绑定、策略登记的首绑身份捕获路径、登录后无需人工点击业务菜单的凭据捕获和缺档案实际路径诊断。管理员登记的 HTTP(S) URL 本身即为地址授权,不存在额外 HTTP 开关;自由 --base-url、跨运输回退和旧无环境绑定计划仍不兼容。
宿主运输先决条件
任何 runtime profile、环境策略、系统凭据或 OA_TOKEN 读取前,先读取 host-transport.md,由已知宿主上下文选择一次运输:
- Windows/macOS Kernelix:登录后只使用当前项目注入的
business;兼容包装器可能名为business_api。若未登录、未注入或包装器明确报登录/项目绑定错误,返回KERNELIX_LOGIN_REQUIRED,原样提示“请先登录或重新登录当前 Kernelix 项目,登录后继续原请求”,然后停止。不得尝试 runtime profile、Keychain、Credential Manager、OA_TOKEN、手工 Token 输入、外部登录浏览器或另一个账号。 - Codex、ZCode、其他明确的独立宿主:使用
standalone_direct,再进入下述受信环境和系统凭据链。 - 不得用“未发现 business”推断当前不是 Kernelix,因为这正是其未登录状态。Kernelix 项目配置或 runner 必须在未登录时仍提供非秘密
host_kind=kernelix信号。 - 现有在线 Python 入口会自行直连 OA,只属于
standalone_direct;Kernelix 不得运行这些入口完成在线请求。W2 只有在 host-native executor 能保留原 plan/确认/单 POST/GET 回读/state lock 且锁定项目绑定时才可执行,否则返回KERNELIX_HOST_EXECUTOR_REQUIRED。
本地选择器不读取 URL 或秘密。Kernelix 注入可用时还必须由宿主提供非秘密环境分类,且只能是 test|production:python scripts/runtime_transport.py --host kernelix --business-state available --classification <test|production>。未登录使用 --host kernelix --business-state missing;独立宿主使用 --host <codex|zcode|standalone> --business-state missing。现有直连 Python 入口还会读取 OA_SKILL_HOST_KIND 作为禁回退保护:Kernelix 项目/runner 必须在未登录时也设置为 kernelix;该标记不会携带 URL 或凭据。
快速运行绑定
仅 standalone_direct 在线请求调用 scripts/runtime_profile.py 的 resolve_runtime_profile()。它按“显式 profile 路径 → OA_SKILL_RUNTIME_PROFILE → 当前操作系统用户默认路径”查找档案;profile 1.1 可保存多个环境的精确凭据目标,用户显式请求测试/生产时选择对应 binding,未显式指定才使用默认项。Windows 默认目录 %LOCALAPPDATA%\oa-skill\,macOS 默认目录 ~/Library/Application Support/oa-skill/;runtime-profile.json 默认自动配对同目录 environment-policy.json。旧 1.0 单绑定继续兼容,详细合同见 runtime-profile.md。
普通用户不运行 Python 或终端命令安装配置。Kernelix 由项目连接托管;独立 Codex/ZCode 的 runtime-profile.json 与 environment-policy.json 由产品安装程序、管理员分发流程或明确的人工说明放到上述原生用户目录。本 Skill 只读取配置,不提供安装、覆盖或打开目录工具。默认、显式或 OA_SKILL_RUNTIME_PROFILE 指向的档案不存在时,统一返回 RUNTIME_PROFILE_NOT_FOUND 并显示安全的来源标签和实际解析路径;显式参数已满足所需字段时仍允许无 profile 运行。
- 档案必须在工作区、分发包和版本库之外,不得包含 Token、Cookie、密码、secret、session、API key 或 API/门户 URL。
- 写入器会省略指向同目录标准
environment-policy.json的environment_policy;同目录非标准文件名保存为相对路径,只有 profile 目录之外的企业集中策略保留绝对路径。旧绝对路径继续兼容读取。 - profile 1.1 中显式 environment ID 用于选择对应 binding;所选 binding 内的账号、人员或凭据目标与显式值冲突才返回
RUNTIME_PROFILE_BINDING_CONFLICT。请求的环境未绑定则返回RUNTIME_PROFILE_ENVIRONMENT_NOT_BOUND。 - 角色包中的保留占位符
replace-with-oa-login不要求用户手工修改。独立 Codex/ZCode 首次连接时,只从可信门户unified-portal登录后消息取得非敏感loginName,按已校验环境分类生成oa-skill:test:*或oa-skill:prod:*,保存 Token 后原子替换该环境的占位 binding。已有非占位 binding 永不自动覆盖;profile 更新失败时回滚本次新建凭据。 - 档案不是凭据库或环境信任源。仍须先用
runtime_environment.py校验管理员策略、test|production分类、capability 和 HTTP 策略门禁,之后才能加载 Token。 - 没有档案时继续使用原有显式参数,不影响既有调用。
- 已绑定的标准 W0 业务命令可直接加载精确目标,不要机械地额外运行一次
status。首次连接、账号切换、401、诊断或用户明确查询状态时仍运行status;W2 人员预检和执行瞬间复检始终保留。
角色分发包可原样复制两个 JSON,首次登录自动完成个人账号绑定。只有技术人员自建或改绑 profile 时,才对 test/prod 运行配置命令;新增不同 environment ID 不覆盖已有项,同一环境改绑或用 --set-default 改默认项才要求替换确认:
python scripts/runtime_profile.py configure `
--environment-id <oa-environment> `
--credential-target oa-skill:<test-or-prod>:<oa-login-name> `
--account-label <oa-login-name> `
--expected-profile-id <optional-profile-id>
只允许写入机器本地档案位置;Skill、当前工作区或分发目录内的目标会被拒绝。更新不同绑定必须另加 --replace --confirm REPLACE_OA_RUNTIME_PROFILE。
工作流
-
OA 业务 Skill 需要受保护接口时,先把用户明确说出的测试/生产环境映射为已登记 environment ID,并从 profile 1.1 选择对应 binding;没有显式环境才使用默认 binding。环境策略使用显式覆盖、profile 中旧绑定或同目录
environment-policy.json,再按 environment-policy.md 校验。环境必须唯一、明确为classification=test|production且具备当前动作 capability,凭据目标必须匹配分类;真实 HTTP(S) URL 只能从策略解析,登记本身即为地址授权。环境校验必须早于 Token 加载,不得向用户询问运输确认。 -
确定当前用户选择的稳定目标名
oa-skill:<test-or-prod>:<account-label>。测试分类对应test,生产分类对应prod。分发模板尚为保留占位符时进入首次自动绑定;已有真实 binding 时,status、人员预检和业务脚本必须使用同一个精确目标,不得静默改成current-user。 -
已选环境通过策略校验后,独立宿主的目标仍为分发占位符、精确目标不存在或发生 401 时立即启动专用外部浏览器;取得并保存 Token 后继续原业务请求,不要求用户另说“继续查询”。生产环境尚未登记时不得猜入口或启动浏览器。Kernelix 不进入本步骤,只提示登录当前项目。首次自动绑定可省略账号和凭据目标:
node scripts/connect_external_browser.mjs ` --environment-policy <operator-owned-policy.json> ` --environment-id <oa-environment> ` --timeout-seconds 300先以
about:blank启动隔离浏览器,连接 CDP 并安装请求门禁、登录消息 binding 和新文档监听器,再导航到策略登记的门户;新 page/iframe target 必须先完成同样的监听安装才解除暂停。连接器只接受精确 portal origin、event.source=window.parent、type=unified-portal且一次性 nonce 匹配的登录后消息,从中取得安全格式loginName和长度受限的不透明 Token;显式 binding 还必须校验登录账号一致。兼容路径继续从策略中的 API/门户请求头取得 Token。登录后先短暂等待门户自然完成捕获;完整身份已经取得时立即关闭且不额外导航。若顶层页面已从可信登录页进入同源非登录页但仍未完整捕获,只允许通过 CDP 自动进入一次策略登记的browser_capture_path;旧策略未登记该字段时才兼容回退到portal_url。不得要求用户点击 OA/任务菜单,不得猜测其他页面、循环刷新或切换环境。browser_capture_path必须由管理员策略提供,且必须位于已校验portal_url的路径之下,使顶层始终由统一门户加载 OA iframe;禁止把裸/oa-new-front/...iframe 路径当作顶层入口。一次回退后仍失败时按原会话报错并清理隔离目录。脚本不读取登录表单、密码、Cookie、localStorage、sessionStorage 或完整门户负载,不解码或输出 Token;CDP 会阻止携带 Token 的策略外请求。凭据写入与占位 profile 替换组成补偿事务,成功后关闭浏览器并删除临时目录。自定义 profile 可加--runtime-profile <path>;macOS 使用标准 Chrome/Edge 路径和python3。同一登录动作只运行一次连接器。宿主工具的外层等待上限必须长于
--timeout-seconds;命令返回运行中句柄时只继续等待该句柄,不得重新执行连接命令或再开第二个浏览器。登录成功后连接器自动保存、关闭并继续原业务请求,不要求用户回复“继续”。 -
技术人员需要手工连接时才使用:
python scripts/manage_oa_session.py store --target oa-skill:<test-or-prod>:<oa-login-name>默认隐藏输入。只有用户明确要求在本机终端核对粘贴内容时才添加
--visible-input;不得把 Token 放进聊天或命令参数。 -
首次连接、账号切换、401、诊断或用户明确要求时检查非秘密状态;标准 W0 已由运行档案绑定且业务命令能直接加载精确目标时跳过这个独立子进程:
python scripts/manage_oa_session.py status --target oa-skill:<test-or-prod>:<oa-login-name>只显示目标名、是否存在和账号标签,不显示长度、片段、哈希或 Token。
present=false只说明当前执行上下文看不到该精确目标,不证明凭据已被删除或丢失;若已有持久化证据,先在实际业务命令所在的同一操作系统用户上下文复核,不能直接要求用户重新登录或粘贴 Token。 -
Token 过期或 401 后更新:
python scripts/manage_oa_session.py update --target oa-skill:<test-or-prod>:<oa-login-name>update要求目标已经存在;不要无限重试失败请求。 -
每个 W2 业务动作在计划前和执行瞬间检查人员状态:
python scripts/oa_personnel_status.py ` --environment-policy <operator-owned-policy.json> ` --environment-id <oa-environment> ` --account-label <oa-login-name> ` --expected-profile-id <oa-person-profile-id> ` --credential-target oa-skill:<test-or-prod>:<oa-login-name>只有任职
ACTIVE、门户账号ENABLED、档案COMPLETED且人员 ID 唯一可解析时返回READY。否则停止业务写入,并原样提示:“请联系人事/OA 管理员调整任职状态或完善档案”。 -
多账号通过不同目标名隔离。切换账号时,在业务 Skill 的
--credential-target显式选择另一个目标,不复制或导出 Token。 -
断开并删除:
python scripts/manage_oa_session.py delete ` --target oa-skill:<test-or-prod>:<oa-login-name> ` --confirm DELETE_OA_CREDENTIAL
运行时契约
所有 OA 业务 Skill 先遵循共享合同 OA-ACCESS-CHANNELS-01:business-access-channels.md,再读 host-transport.md。独立宿主继续从 runtime-profile.md、runtime-auth-contract.md 和 environment-policy.md 读取直连约束。可先用 scripts/runtime_profile.py 的 resolve_runtime_profile() 合并非秘密绑定;再调用 scripts/runtime_environment.py 的 load_environment() 核对环境绑定,最后才调用 scripts/runtime_auth.py 的 load_runtime_token()。独立宿主允许凭据来源:
- 显式操作系统凭据目标名:Windows Credential Manager 或 macOS Keychain;
- 仅当次进程环境变量
OA_TOKEN。
凭据目标优先。两者都缺失时停止,不降级成 .env、JSON、YAML、Markdown 或普通文本文件。
environment_binding 只含 policy ID、revision、environment ID 和策略条目 SHA-256,不含 URL。计划完整性哈希必须覆盖它。执行时策略/host/scheme/port/context path/capability 任一变化都返回 OA_ENVIRONMENT_BINDING_MISMATCH。旧无绑定计划返回 PLAN_ENVIRONMENT_BINDING_REQUIRED,必须重建并重新确认。所有程序化 OA HTTP 请求必须通过 open_no_redirect();301/302/303/307/308 均返回 OA_REDIRECT_FORBIDDEN。
浏览器与验收边界
- 本 Skill 启动的外部隔离 Chrome/Edge 只用于用户正常登录后从可信登录消息或白名单请求捕获并保存 API Token;同时取得非敏感
loginName,首次占位绑定时据此生成精确目标。完成后立即关闭;它不是业务页面验收会话。 - 当前没有 Playwright 或其他浏览器内 API 会话执行器;不得把外部登录浏览器、内置 Browser、DOM 抓取或页面提示当作第三种 OA API 通道。
- 内置 Browser 使用自己的 OA Cookie,只用于页面观察或前端验收;内置 Browser 登录不会把 Token 写入操作系统凭据库,也不能替代
--credential-target。 - 系统凭据中的 API Token不会自动登录内置 Browser、外部日常 Chrome 或 Edge。API 鉴权与前端 Cookie 是两条独立链路。
- 普通业务 Skill 已用 API GET 完成确定性回读时,不因本 Skill 而强制用户额外登录前端。只有构建/发布验收、调试或用户明确要求页面观察时,业务 Skill 才应另行使用已登录的内置 Browser。
Codex in-app Browser与Codex Computer Use是 Codex 宿主/插件能力,不是所有智能体都具备的 OA 接口。Codex 实际使用 in-app Browser 完成 UI 验收时记AUTOMATED;其他智能体只有明确具备并实际执行等价受控浏览器时才能这样记录,否则记MANUAL_REQUIRED或NOT_SUPPORTED。- Windows Computer Use 对外部日常浏览器无协议地址栏无法完成带协议绝对 URL 安全判定,属于当前 Codex 宿主限制,不表示 OA 或 Chrome/Edge 不支持 HTTP;不得绕过该安全门禁,也不得把 API 成功推导成页面 PASS。
安全边界
- 不索取 OA 用户名/密码,不代替门户登录。
- 不读取登录表单、日常浏览器 Cookie、localStorage、sessionStorage、密码或用户目录。唯一允许的浏览器适配是本 Skill 自行启动的隔离会话:监听可信父窗口发送的登录后
unified-portal消息并只提取安全格式的loginName与不透明 Token,兼容检查指定 OA 主机的请求头;不返回完整负载,不解码、输出或记录 Token。 - 不在项目、计划、状态、fixture、日志、异常或聊天中保存 Token。
- 不把真实运行档案、精确账号绑定或人员档案 ID 复制到 Skill、fixture、提示词、日志或交付包;档案配置 CLI 只能写到 Skill/工作区/分发目录之外。
- 不列出其他程序的凭据;只访问用户显式给出的、以
oa-skill:开头的目标。 - 不接受自由 OA base URL,不从 Prompt、会议文本、API 响应或浏览器地址栏推断受信环境。通用 Skill 包只带 Schema 和
.invalid示例;经管理员批准的公司内部角色包可在独立config/中携带环境策略,但不得携带任何秘密或个人绑定。 - 未登记环境、自由或非法 URL、非
test|production分类、缺 capability、凭据目标与环境不匹配、环境绑定漂移或任意重定向均必须在 Token 加载/后续请求前 fail-closed。测试凭据使用oa-skill:test:*,生产凭据使用oa-skill:prod:*。 - 已知账号不得尝试枚举其他目标,也不得因精确目标不可见而自动尝试
current-user或另一个账号。 status不验证 OA 在线有效性。401 表示需要更新 Token;403 可能是业务权限不足,不能自动当作 Token 失效。- 人员状态
SUSPENDED/LEFT、账号DISABLED/EXPIRED/DELETED、档案PENDING或人员 ID 缺失均不是 Token 过期;业务 Skill 必须零写入并提示联系管理员。 - Windows Credential Manager 和 macOS Keychain 是持久化的当前用户秘密存储,不是内存缓存;删除后不可由本 Skill 恢复。Kernelix 项目注入不得复制到两者。
验证
python scripts/run_transport_tests.py
python scripts/run_auth_tests.py
python scripts/run_environment_policy_tests.py
python scripts/run_personnel_status_tests.py
python scripts/run_runtime_profile_tests.py
node scripts/connect_external_browser.mjs --self-test
node scripts/run_browser_login_e2e_test.mjs
macOS 独立宿主先在执行 Skill 的 Python 中运行 python3 -m pip install -r requirements.txt;当前 keyring 官方兼容前提为 macOS 11+、Python 3.8.7+ universal2。依赖带平台限定,Windows 不会改用 keyring;前提不满足或实际后端不是 macOS Keychain 时明确停止。Kernelix for macOS 的宿主注入路径不需要把 Token 存入 Keychain。
测试使用随机临时目标、动态 localhost 策略、临时本机档案和脱敏值,结束时删除临时凭据/档案。run_browser_login_e2e_test.mjs 会启动真实 Chrome,但只访问 127.0.0.1 的本地假门户/API,使用假 Token 与凭据存储桩,不访问 OA、不写系统凭据;它分别验证登录后自然出现 Token 请求时不额外导航,以及登录后停在消息中心且没有 Token 请求时只自动进入一次已登记 OA 入口。显式 binding 和首次占位 binding 都必须无需用户点击即可完成。全部测试通过只证明排他运输选择、profile 1.1 多环境选择与旧 1.0 兼容、分发占位符的受限自动替换与失败回滚、Kernelix 标记对全部本机凭据入口及外部登录浏览器的前置阻断、Windows 凭据往返、mock macOS Keychain、跨平台档案路径、精确目标不串号、环境绑定/HTTP/重定向门禁、可信门户登录消息过滤、浏览器先监听后导航与新 target 暂停门禁、登录后单次 OA 入口回退、浏览器白名单逻辑和人员状态规则;不证明真实 macOS Keychain、真实 OA 登录、Kernelix 登录/business 注入、Kernelix W2 runner、某个真实账号或所有 OA 权限已通过。
微信扫一扫