canonical_id: "auth_oauth2"
display_name: "Auth Primitive — OAuth2"
category: "auth"
version: "v1"
wasi_target: "preview1"
stability: "floating"
runtime_compat:
  - "cf-workers"
  - "workerd"
  - "wazero"
constraints:
  max_size_kb: 2048
  max_cold_start_ms: 200
  no_network_syscall: false
  no_filesystem_syscall: true
  io_model: "stdin_stdout_json"
input_schema:
  type: object
  required: [action, api_key, service]
  properties:
    action:
      type: string
      enum: [authenticate, needs_refresh, refresh]
      description: |
        authenticate — 用 refresh_token 換 access_token，展開 inject 模板
        needs_refresh — 檢查 token 是否需要 refresh（expires_at < now+300s）
        refresh — 強制重新 refresh，更新 CREDENTIALS_KV 中的 access_token/expires_at
    api_key:
      type: string
      description: 租戶識別（ak_ 前綴），用來組 {api_key}:cred:{name} KV key
    service:
      type: string
      description: auth recipe 名稱，對應 auth_recipe:{service} 的 KV 記錄
    request:
      type: object
      description: 下游零件的 HTTP request 上下文（保留，auth_oauth2 當前不使用）
output_schema:
  type: object
  properties:
    success:
      type: boolean
    needs_refresh:
      type: boolean
      description: action=needs_refresh 時有效
    auth_headers:
      type: object
      additionalProperties:
        type: string
    auth_query:
      type: object
      additionalProperties:
        type: string
    auth_body:
      type: object
      additionalProperties:
        type: string
    runtime:
      type: object
      description: 含 access_token（action=authenticate/refresh 時有效）
gherkin_tests:
  - scenario: "缺少 api_key"
    given: '{"action":"authenticate","service":"google"}'
    then_contains: '{"success":false'
  - scenario: "找不到 auth recipe"
    given: '{"action":"authenticate","api_key":"ak_test","service":"nonexistent_oauth2_svc"}'
    then_contains: '{"success":false'
  - scenario: "needs_refresh 無 expires_at"
    given: '{"action":"needs_refresh","api_key":"ak_test","service":"google"}'
    then_contains: '"needs_refresh":true'
tags: [auth, credential, primitive, oauth2]
description: |
  OAuth2 auth primitive。讀取 auth_recipe（含 token_endpoint、client_id、client_secret）
  + 解密 refresh_token + 呼叫 token endpoint 換 access_token + 展開 {{runtime.access_token}}。
  支援 authenticate / needs_refresh / refresh 三個 action。
  透過 host function kv_get + crypto_decrypt + http_request，plaintext 永不離開 WASM。
config_example: |
  auth_step:
    component: "auth_oauth2"
    action: "authenticate"
    service: "google_drive"   # 對應 auth_recipe:google_drive 的 KV 記錄
