0

我正在尝试为一个项目连接 eTrade 的 API。他们使用 OAuth v1 流程。我在流程的第一步 - 获取请求令牌时遇到了错误。可以在此处找到他们文档中的信息。

我得到的错误是 401-无效签名。

我一直在这里阅读 OAuth 流程并熟悉该流程。过去,我使用 PassportJS 进行 3rd 方集成。

所以 eTrade 为我提供了 4 个密钥——PROD_KEY、PROD_SECRET、SANDBOX_KEY、SANDBOX_SECRET。我现在正在使用沙盒密钥,因为我的生产密钥仍在等待中。

现在我的 API 上有一个端点,我将从客户端调用它来获取请求令牌。它的结构是这样的:

router.route('/auth/request').get(controller.request);

下面的控制器是我生成签名并尝试从 eTrade 请求令牌的地方。该控制器如下所示:

// controllers/auth.js

const axios = require('axios');
const { v1 } = require('uuid');
const crypto = require('crypto');

function generateSignature() {
  const method = 'GET',
        url = 'http://localhost/',
        encodedUrl = encodeURIComponent(url),
        paramaters = {
          oauth_consumer_key: process.env.ETRADE_SANDBOX_API_KEY,
          oauth_signature_method: 'HMAC-SHA1',
          oauth_timestamp: Math.floor(Date.now() / 1000),
          oauth_nonce: v1(),
          oauth_callback: 'oob'
        }

  var ordered = {};
  Object.keys(paramaters).sort().forEach((key) => {
    ordered[key] = paramaters[key];
  });

  var encodedParameters = '';

  for(k in ordered) {
    const encodedValue = escape(ordered[k]);
    const encodedKey = encodeURIComponent(k);

    if(encodedParameters === '') {
      encodedParameters += encodeURIComponent(`${encodedKey}=${encodedValue}`);
    } else {
      encodedParameters += encodeURIComponent(`&${encodedKey}=${encodedValue}`);
    }
  }

  encodedParameters = encodeURIComponent(encodedParameters);

  const signature_base_string = `${method}&${encodedUrl}&${encodedParameters}`;
  const signing_key = `${process.env.ETRADE_SANDBOX_SECRET_KEY}`;
  const signature = crypto.createHmac("SHA1", signing_key).update(signature_base_string).digest().toString('base64');
  const encodedSignature = encodeURIComponent(signature);

  return encodedSignature;
}

module.exports = {
  request: async (req, res) => {
    console.log('Fetching request token from etrade...');

    try {
      var response = await axios.get(`https://api.etrade.com/oauth/request_token`, {
        params: {
          oauth_consumer_key: process.env.ETRADE_SANDBOX_API_KEY,
          oauth_signature_method: 'HMAC-SHA1',
          oauth_signature: generateSignature(),
          oauth_timestamp: Math.floor(Date.now() / 1000),
          oauth_nonce: v1(),
          oauth_callback: 'oob'
        }
      });

      console.log('Fetched request token...', response.data);
    } catch (error) {
      console.log('Could not fetch request token...', error.response.data);
    }
  }
}

我已经按照互联网上的一些指南来帮助我了解签署请求的过程,但我似乎无法得到有效的回复。

4

2 回答 2

0

您的代码以 etrade 生产 URL 为目标。尝试调用沙盒 URL。

Production  https://api.etrade.com/v1/{module}/{endpoint}
Sandbox     https://apisb.etrade.com/v1/{module}/{endpoint}
于 2020-09-01T03:38:28.047 回答
0

我不知道您是否仍在寻找有关此问题的答案,但我认为以下内容可能会有所帮助。我在尝试与他们的 API 集成时遇到了类似的问题(虽然我使用的是 java)。查看他们的示例代码,我意识到签署 baseString 的密钥在 request_token API 的末尾需要一个额外的“&”。从他们的示例应用程序中查看以下代码 -

       if( token != null){
            // not applicable for request_token API call
             key = StringUtils.isEmpty(token.getOauth_token_secret()) ? context.getResouces().getSharedSecret() +"&" : 
                 context.getResouces().getSharedSecret()+"&" + OAuth1Template.encode(token.getOauth_token_secret());
        }else{
            // applicable for request_token API call
            key =  context.getResouces().getSharedSecret() +"&";
        }

AFAIK,这不是 oAuth 标准或任何东西。这似乎非常具体到他们的实施。无论如何,我更新了我的代码,在最后包含了额外的“&”,它开始工作了。

  private String signBaseString(String httpMethod, String url) {
    StringBuilder baseString = new StringBuilder();
    baseString.append(encode(httpMethod)).append("&").append(encode(url)).append("&");
    baseString.append(encode(normalizeParams(url)));
    // the extra & is added here.
    SecretKeySpec signingKey = new SecretKeySpec((this.apiSecret + "&").getBytes(), SIGN_ALG);
    Mac mac = null;
    try {
      mac = Mac.getInstance(SIGN_ALG);
      mac.init(signingKey);
      return new String(Base64.encodeBase64(
          mac.doFinal(baseString.toString().getBytes(
              StandardCharsets.UTF_8))));
    } catch (NoSuchAlgorithmException | InvalidKeyException e) {
      logger.error("Error during signing headers", e);
      return null;
    }
  }

让我知道这是否适合您。

于 2021-01-19T23:51:49.460 回答