【问题标题】:OAuth1.0 header in Node.jsNode.js 中的 OAuth1.0 标头
【发布时间】: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 进行请求。
感谢您的宝贵时间。

【问题讨论】:

    标签: javascript node.js api oauth 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请求是不是也一样?
    • 应该是,虽然我没有明确测试过。
    • 你能看看我的问题吗stackoverflow.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);
        });
      

      我希望它有效!

      【讨论】:

        猜你喜欢
        • 2021-02-24
        • 1970-01-01
        • 2013-08-10
        • 1970-01-01
        • 2015-08-18
        • 2013-08-29
        • 1970-01-01
        • 1970-01-01
        • 2013-03-23
        相关资源
        最近更新 更多