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_nonce 和 oauth_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 标头的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 node.js 和 Request 包禁用 HTTP 标头中的“withcredentials”?
将 set-cookie 标头发送到 node.js 中的重定向 url