You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

X平台OAuth 1.0a请求签名问题及/oauth/request_token端点401错误排查求助

X平台OAuth 1.0a请求签名问题及/oauth/request_token端点401错误排查求助

我正在开发“用X登录”的功能,目前处于OAuth 1.0a验证流程的第一步,调用/oauth/request_token端点发送POST请求时,始终收到401状态码错误。以下是我的实现代码和已经尝试过的排查措施,想请教大家还有哪些排查方向?同时也想确认下我的签名生成步骤(createHMAC、update、digest)是否正确?

import crypto from 'crypto';
import axios from 'axios';
import { NextResponse } from 'next/server';

const X_CLIENT_ID = process.env.X_CLIENT_ID as string;
const X_CLIENT_SECRET = process.env.X_CLIENT_SECRET as string;
const X_CALLBACK_URL = process.env.X_CALLBACK_URL as string;

if (!X_CLIENT_ID || !X_CLIENT_SECRET || !X_CALLBACK_URL) {
  throw 'Missing env vars';
}

const BASE_URL = 'https://api.x.com/oauth/request_token';

export async function GET() {
  try {
    const params = createSignedRequestParams();
    const authorizationHeader = `Oauth oauth_callback="${params.oauth_callback}",oauth_consumer_key="${params.oauth_consumer_key}",oauth_nonce="${params.oauth_nonce}",oauth_signature="${params.oauth_signature}",oauth_signature_method="${params.oauth_signature_method}",oauth_timestamp="${params.oauth_timestamp}",oauth_version="${params.oauth_version}"`
    const response = await axios.post(BASE_URL, null, {
      headers: {
        'User-Agent': 'Cutcosts',
        'Host': 'api.x.com',
        'Accept': '*/*',
        'Authorization': authorizationHeader
      }
    });
    console.log(response);
    return NextResponse.json({ success: true })
  } catch (error: any) {
    console.log(JSON.stringify(error, null, 2));
    return NextResponse.json({ message: 'Internal server error' }, { status: 500 });
  }
}

function enc(str: string) {
  return encodeURIComponent(str);
}

function createSignedRequestParams() {
  // RFC 5849 Section 3.4.1
  // Encoding method is only necessary for custom methods
  const method = 'POST';

  // Params
  const params: Record<string, string> = {
    'oauth_callback': X_CALLBACK_URL,
    'oauth_consumer_key': X_CLIENT_ID, // "API Key" in X Developer Portal, per docs
    'oauth_nonce': Math.random().toString(36).substring(2) + Math.random().toString(36).substring(2),
    'oauth_signature_method': 'HMAC-SHA1',
    'oauth_timestamp': String(Math.floor(Date.now() / 1000)),
    'oauth_version': '1.0'
  };

  // Encode params
  const encodedParams: Record<string, string> = {};

  Object.keys(params).forEach((key) => {
    encodedParams[key] = enc(params[key]);
  });

  // Normalize encoded params
  const normalizedParams = Object.keys(encodedParams).sort().map(key => `${key}=${encodedParams[key]}`).join('&');

  // Encode normalize params
  const encodedNormalizedParams = normalizedParams; // enc(normalizedParams);

  // Encode base url
  const encodedBaseUrl = enc(BASE_URL);

  // Construct base string
  const baseString = method + '&' + encodedBaseUrl + '&' + enc(encodedNormalizedParams);
  console.log('Example:', 'POST&https%3A%2F%2Fapi.x.com%2F1.1%2Fstatuses%2Fupdate.json&include_entities%3Dtrue%26oauth_consumer_key%3Dxvz1evFS4wEEPTGEFPHBog%26oauth_nonce%3DkYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1318622958%26oauth_token%3D370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb%26oauth_version%3D1.0%26status%3DHello%2520Ladies%2520%252B%2520Gentlemen%252C%2520a%2520signed%2520OAuth%2520request%2521');
  console.log('Actual:', baseString);

  // Construct encoded signature
  const signingKey = enc(X_CLIENT_SECRET) + '&';
  const signature = crypto.createHmac('sha1', signingKey)
    .update(baseString)
    .digest('base64');
  const encodedSignature = enc(signature);

  // Update params to include encoded signature
  encodedParams.oauth_signature = encodedSignature;
  return encodedParams;
}

已尝试的排查步骤

  • 尝试对Authorization头中"Oauth "之后的整个字符串进行编码,结果返回400错误
  • 尝试不对base64后的签名进行编码,结果返回400错误
  • 尝试把oauth_callback参数放在POST请求体中而非Authorization头里,结果返回401错误
  • 尝试在生成签名时不对标准化后的参数进行编码,结果返回401错误
  • 验证了X开发者后台配置的回调URI和请求中的回调URI完全一致
  • 验证了oauth_consumer_key的值和X开发者后台的Client ID一致
  • 验证了签名过程中使用的密钥和X开发者后台的Client Secret一致
  • 尝试用开发者后台的“API Key”和“API Secret”分别作为oauth_consumer_key和签名密钥(根据OAuth 1.0a文档,这才是正确的),但仍然返回401错误

大家还有什么其他的排查建议吗?另外我的签名生成流程(createHmac初始化、update传入baseString、digest输出base64)是否正确?

备注:内容来源于stack exchange,提问作者viktorhex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 09:08:03