# 示例：X (Twitter) OAuth1

## 1) 创建平台

```bash
curl -X POST http://127.0.0.1:8000/v1/platforms \
  -H 'content-type: application/json' \
  -d '{
    "id":"plat_x",
    "orgId":"org_demo",
    "nameId":"x_com",
    "name":"X (Twitter)",
    "code":"x"
  }'
```

## 2) 创建授权方式（直接提交 OAuth1 凭证）

```bash
curl -X POST http://127.0.0.1:8000/v1/auth-methods \
  -H 'content-type: application/json' \
  -d '{
    "id":"am_x_oauth1_manual",
    "platformId":"plat_x",
    "name":"OAuth1自维护授权",
    "type":"OAuth1",
    "description":"X平台 直接提交所有授权信息，不暴露运营平台身份，需要用户手动登录X平台界面操作后完成相关授权信息的获取。",
    "authFieldsSchema":{
      "type":"object",
      "title":"OAuth1 授权凭证",
      "properties":{
        "consumer_key":{"type":"string","title":"OAuth Token","description":"client key"},
        "consumer_secret":{"type":"string","title":"OAuth Token Secret","description":"client secret"},
        "oauth_token":{"type":"string","title":"Access Token","description":"resource owner token"},
        "oauth_token_secret":{"type":"string","title":"Access Token Secret","description":"resource owner secret"},
        "expires_at":{"type":["integer","null"],"title":"过期时间戳","description":"Unix 秒，可为空"}
      },
      "required":["consumer_key","consumer_secret","oauth_token","oauth_token_secret"]
    },
    "authFieldPlacements": {
      "metadata": {
        "oauth1": {
          "consumerKeyField": "consumer_key",
          "consumerSecretField": "consumer_secret",
          "tokenField": "oauth_token",
          "tokenSecretField": "oauth_token_secret",
          "signatureMethod": "HMAC-SHA1"
        }
      }
    }
  }'
```

## 3) 创建 Secret（直接录入 OAuth1 凭证）

```bash
curl -X POST http://127.0.0.1:8000/v1/auth-methods/am_x_oauth1_manual/secret \
  -H 'content-type: application/json' \
  -d '{
    "id":"sec_x_manual",
    "name":"x平台运营账号",
    "tags":["x-platform","prod"],
    "data":{
      "consumer_key":"<consumer_key>",
      "consumer_secret":"<consumer_secret>",r
      "oauth_token":"<access_token>",
      "oauth_token_secret":"<access_token_secret>",
      "expires_at":null
    },
    "autoLoginEnabled":false
  }'
```

# 客户端应用 `usageMapping`

X 平台使用 OAuth1。客户端应在查询 Secret 时，直接获得构建 OAuth1 会话所需的字段（来自 `Secret.data`）以及 `usageMapping` 中的 OAuth1 元数据（通常在 `metadata.oauth1`）。

建议在 `AuthMethod.authFieldPlacements` 中配置：

```json
{
  "metadata": {
    "oauth1": {
      "consumerKeyField": "consumer_key",
      "consumerSecretField": "consumer_secret",
      "tokenField": "oauth_token",
      "tokenSecretField": "oauth_token_secret",
      "signatureMethod": "HMAC-SHA1"
    }
  }
}
```

服务端返回 Secret 时会包含：
- `data.consumer_key`、`data.consumer_secret`、`data.oauth_token`、`data.oauth_token_secret`（根据你的 `authFieldsSchema`/`responseMapping` 写入）；
- `usageMapping.metadata.oauth1`（如上所示），指导客户端如何从 `data` 取对应键。

## Python 示例（requests-oauthlib OAuth1Session）

依赖安装：

```bash
pip install requests requests-oauthlib
```

示例代码：

```python
from typing import Any, Dict
import requests
from requests_oauthlib import OAuth1Session

BASE = "http://127.0.0.1:8000"
SECRET_ID = "<your-secret-id>"

resp = requests.get(f"{BASE}/v1/secrets/{SECRET_ID}")
resp.raise_for_status()
secret = resp.json()

data: Dict[str, Any] = secret.get("data", {})
oauth1_meta: Dict[str, Any] = ((secret.get("usageMapping") or {}).get("metadata") or {}).get("oauth1", {})
if secret.get("type") != "OAuth1":
  raise RuntimeError(f"unexpected secret.type={secret.get('type')}, expected 'OAuth1'")

def get_field(name: str, default: str | None = None) -> str | None:
    return data.get(name, default)

consumer_key = get_field(oauth1_meta.get("consumerKeyField", "consumer_key"))
consumer_secret = get_field(oauth1_meta.get("consumerSecretField", "consumer_secret"))
token = get_field(oauth1_meta.get("tokenField", "oauth_token"))
token_secret = get_field(oauth1_meta.get("tokenSecretField", "oauth_token_secret"))

if not all([consumer_key, consumer_secret, token, token_secret]):
    raise RuntimeError("missing required OAuth1 fields in secret.data")

client = OAuth1Session(
    client_key=consumer_key,
    client_secret=consumer_secret,
    resource_owner_key=token,
    resource_owner_secret=token_secret,
    signature_method=oauth1_meta.get("signatureMethod", "HMAC-SHA1"),
)

r = client.get("https://api.twitter.com/1.1/account/verify_credentials.json")
print(r.status_code, r.text)
```

## JavaScript 示例（oauth-1.0a）

依赖安装：

```bash
npm install oauth-1.0a node-fetch@3 crypto-js
```

示例代码（Node.js）：

```js
import OAuth from 'oauth-1.0a';
import CryptoJS from 'crypto-js';
import fetch from 'node-fetch';

const res = await fetch('http://127.0.0.1:8000/v1/secrets/<your-secret-id>');
const secret = await res.json();
const data = secret.data || {};
const meta = (secret.usageMapping?.metadata || {}).oauth1 || {};
if (secret.type !== 'OAuth1') throw new Error(`unexpected secret.type=${secret.type}`);

const consumerKey = data[meta.consumerKeyField || 'consumer_key'];
const consumerSecret = data[meta.consumerSecretField || 'consumer_secret'];
const token = data[meta.tokenField || 'oauth_token'];
const tokenSecret = data[meta.tokenSecretField || 'oauth_token_secret'];

if (!consumerKey || !consumerSecret || !token || !tokenSecret) {
  throw new Error('missing OAuth1 fields');
}

const oauth = new OAuth({
  consumer: { key: consumerKey, secret: consumerSecret },
  signature_method: meta.signatureMethod || 'HMAC-SHA1',
  hash_function(base_string, key) {
    return CryptoJS.HmacSHA1(base_string, key).toString(CryptoJS.enc.Base64);
  },
});

const requestData = { url: 'https://api.twitter.com/1.1/account/verify_credentials.json', method: 'GET' };
const authHeader = oauth.toHeader(oauth.authorize(requestData, { key: token, secret: tokenSecret }));

const r = await fetch(requestData.url, { headers: { ...authHeader } });
console.log(r.status, await r.text());
```
