Node.js 中的 OAuth1.0 标头

Posted

技术标签:

【中文标题】Node.js 中的 OAuth1.0 标头【英文标题】:OAuth1.0 header in Node.js 【发布时间】:2019-10-17 08:00:31 【问题描述】:

我通过邮递员成功使用了API,它使用了 OAuth1.0。 现在我正在构建一个调用此 API 的 API,但是在尝试在 OAuth1.0 的 javascript 中设置等效项时遇到了麻烦。 标题如下所示:

'Authorization': 'OAuth oauth_consumer_key="XXX",oauth_token="XXX",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1559312415",oauth_nonce="XXX",oauth_version="1.0",oauth_signature="XXX"'

我的问题与 oauth_nonceoauth_signature 有关。 我可以用来生成这两个参数的哈希函数是什么。 另外,我正在使用AXios 进行请求。 感谢您的宝贵时间。

【问题讨论】:

【参考方案1】:

我能够通过 Axios 找到解决方案。我创建了一个 OauthHelper 类来生成 Authorization 标头:

const crypto = require('crypto');
const oauth1a = require('oauth-1.0a');

const CONSUMERKEY = '<consumerKey>';
const CONSUMERSECRET = '<consumerSecret>';
const TOKENKEY = '<tokenKey>';
const TOKENSECRET = '<tokenSecret>';

class Oauth1Helper 
    static getAuthHeaderForRequest(request) 
        const oauth = oauth1a(
            consumer:  key: CONSUMERKEY, secret: CONSUMERSECRET ,
            signature_method: 'HMAC-SHA1',
            hash_function(base_string, key) 
                return crypto
                    .createHmac('sha1', key)
                    .update(base_string)
                    .digest('base64')
            ,
        )

        const authorization = oauth.authorize(request, 
            key: TOKENKEY,
            secret: TOKENSECRET,
        );

        return oauth.toHeader(authorization);
    


module.exports = Oauth1Helper;

然后我就可以通过 Axios 从任何我需要的地方发帖:

const request = 
    url: 'https://api-domain.com',
    method: 'POST',
    body: 
        "uniqueId": 1234
    
;

const authHeader = Oauth1Helper.getAuthHeaderForRequest(request);

return await axios.post(
    request.url,
    request.body,
     headers: authHeader );

【讨论】:

get请求是不是也一样? 应该是,虽然我没有明确测试过。 你能看看我的问题吗***.com/questions/61458960/… 我收到此代码错误。 TypeError: Cannot read property 'createHmac' of undefined。如果可能的话,你能帮忙更新一下吗?这是用 RSA-SHA1 签名吗 @GregVanGorp 自从我用这个敲打我的头以来已经有 5 个小时了。你是一个救生员,希望我能投票 100 次,谢谢 TON!【参考方案2】:

这是一个不需要包裹的。

您需要 makeHeader(consumer, token, request),它适用于 Node 的 https.request,但也适用于 Axios。

const crypto = require('crypto');
const  stringify: qStringify  = require('querystring');
const  httpOptions, fetch  = require('./fetch');

function nonce() 
  return crypto.randomBytes(16).toString('hex');


function sign(baseStr, key) 
  return crypto.createHmac('sha1', key).update(baseStr).digest('base64');


function percentEncode(str) 
  const notEscapedRe = /[!'()*]/g;
  return encodeURIComponent(str).replace(notEscapedRe, (c) => `%$c.charCodeAt(0).toString(16)`);


function makeObjStr(parameters, quote = '"', split = ',') 
  const ordered = Object.fromEntries(Object.entries(parameters).sort());
  return Object.entries(ordered).map(([key, value]) => `$percentEncode(key)=$quote$percentEncode(value)$quote`).join(split);


function authHeader(parameters) 
  return  Authorization: `OAuth $makeObjStr(parameters)` ;


function makeHeader(consumer, token, request) 
  const oauthData = 
    oauth_consumer_key: consumer.key,
    oauth_token: token.key,
    oauth_nonce: nonce(),
    oauth_signature_method: 'HMAC-SHA1',
    oauth_timestamp: Math.floor(Date.now() / 1000),
    oauth_version: '1.0',
  ;
  const baseStr = [
    request.method.toUpperCase(),
    percentEncode(request.url),
    percentEncode(makeObjStr( ...request.data, ...oauthData , '', '&')),
  ].join('&');
  const signingKey = [percentEncode(consumer.secret), percentEncode(token.secret)].join('&');
  return authHeader(
    ...oauthData,
    oauth_signature: sign(baseStr, signingKey),
  );


function oAuth1Fetch(
  consumer, token, hostname, path, query = ,
) 
  const request = 
    method: 'GET',
    url: `https://$hostname$path`,
    data: query,
  ;
  return fetch(
    ...httpOptions,
    headers: makeHeader(consumer, token, request),
    hostname,
    path: `$path?$qStringify(query)`,
  );


module.exports = 
  oAuth1Fetch,
;

这是我的fetch

const  Agent, request  = require('https');

const httpOptions = 
  agent: new Agent( keepAlive: true ),
  'User-Agent': `AWS Lambda Node/$process.version surflog.app`,
  // Accept: 'application/json',
;

function fetch(options) 
  return new Promise((resolve, reject) => 
    const req = request(options, (res) => 
      const data = [];
      res.on('data', (chunk) => data.push(chunk));
      res.on('end', () => 
        const result = Buffer.concat(data).toString();
        console.log(res.statusCode);
        if (res.statusCode >= 200 && res.statusCode < 300) 
          resolve(result);
         else 
          reject(result);
        
      );
    );
    req.setTimeout(6000, (err) => 
      console.warn(err);
      console.warn('Timeout', options.hostname);
    );
    req.on('error', reject);
    req.end();
  );


module.exports = 
  httpOptions,
  fetch,
;

一个例子:

oAuth1Fetch(
  consumer: 
    key: 'xyz',
    secret: 'xyz',
  ,
  token: 
    key: 'xyz',
    secret: 'xyz',
  ,
  hostname: 'apis.garmin.com',
  path: '/wellness-api/rest/backfill/activities',
  query: 
    summaryStartTimeInSeconds: 1609459200,
    summaryEndTimeInSeconds: 1609459200 + 7776000,
  ,
).then(console.log).catch(console.error);

【讨论】:

【参考方案3】:

这是对 Greg Van Gorp 之前提出的解决方案的更新,但使用了 Crypto-JS。由于不推荐使用加密货币。

const crypto = require('crypto-js');
const oauth1a = require('oauth-1.0a');

const CONSUMERKEY = '<consumerKey>';
const CONSUMERSECRET = '<consumerSecret>';

class Oauth1Helper 
  static getAuthHeaderForRequest(request) 
    const oauth = oauth1a(
      consumer: key: CONSUMERKEY, secret: CONSUMERSECRET,
      signature_method: 'HMAC-SHA1',
      hash_function(base_string, key) 
        return crypto.algo.HMAC
          .create(crypto.algo.SHA1, key)
          .update(base_string)
          .finalize()
          .toString(crypto.enc.Base64);
      ,
    );

    const authorization = oauth.authorize(request);

    return oauth.toHeader(authorization);
  


module.exports = Oauth1Helper;

我提出的解决方案略有不同,因为它使用的 API 不需要令牌,但我知道只传递令牌应该没有问题,这些在 oauth1a.authorize 方法中是可选的。

请求:

const request = 
         url: 'https://api-domain.com',
         method: 'POST',
         body: 
           someData: '1234',
         ,
      ;

const authHeader = Oauth1Helper.getAuthHeaderForRequest(request);

axios
  .post(request.url, request.body, headers: authHeader)
  .then(res => 
     console.log(res);
  )
  .catch(err => 
     console.log(err);
  );

我希望它有效!

【讨论】:

以上是关于Node.js 中的 OAuth1.0 标头的主要内容,如果未能解决你的问题,请参考以下文章

如何将生成的 Oauth 1.0 标头用于 GET 请求?

如何使用 node.js 和 Request 包禁用 HTTP 标头中的“withcredentials”?

未设置 Node.js 响应标头

将 set-cookie 标头发送到 node.js 中的重定向 url

node.js 中的标头 - 套接字 io 资源解释为脚本,但使用 MIME 类型 text/plain 传输:

解析 If-Modified-Since 标头 (node.js)