papir
Version:
Rest API Modelling library
217 lines (196 loc) • 5.77 kB
JavaScript
import crypto from 'crypto'
import util from './util'
import encode from './encode'
class Sign {
constructor () {
let scope = this
this.gen = (opt) => {
let conf = {
authentication: 'oauth',
version: '1.0a',
type: 'one_legged',
algorithm: 'HMAC-SHA1',
url: '',
method: 'GET',
key: '',
secret: '',
token: { key: '', secret: '' },
nonce: '',
nonceLength: 6,
timestampLength: 10,
keepEmpty: true,
requester: null,
base64: true,
ampersand: true,
sort: true,
protocol: 'rfc3986',
encodeNull: true,
encodeNames: true,
encodeValues: true
}
Object.keys(conf).forEach((key) => {
if (typeof opt[key] !== 'undefined') {
conf[key] = opt[key]
}
})
let baseString = conf.method + '&' + encode.encode(util.stripUri(conf.url)) + '&'
let hash = ''
let mergedParams = []
util.getParams(conf.url).forEach(param => {
mergedParams.push({
key: param.key,
value: param.value
})
})
if ((typeof conf.authentication === 'function' || conf.authentication === 'oauth') && conf.version === '1.0a') {
mergedParams = mergedParams.concat([
{
key: 'oauth_consumer_key',
value: conf.key
},
{
key: 'oauth_signature_method',
value: conf.algorithm
},
{
key: 'oauth_token',
value: conf.token.key
},
{
key: 'oauth_timestamp',
value: util.timestamp(conf.timestampLength)
},
{
key: 'oauth_nonce',
value: (conf.nonce === '' && conf.nonceLength > 0) ? util.nonce(conf.nonceLength) : conf.nonce
},
{
key: 'oauth_version',
value: '1.0'
}
])
if (conf.requester !== null) {
mergedParams.push({
key: 'xoauth_requester_id',
value: conf.requester
})
}
let paramString = scope.paramString(mergedParams, conf.keepEmpty, conf.sort)
mergedParams = paramString.decoded
baseString += encode.encode(paramString.string)
let signKey = scope.signKey(conf.secret, conf.token.secret, conf.ampersand)
if (conf.base64 && conf.algorithm === 'HMAC-SHA1') {
// baseString = baseString.replace(/%00/g, '%2500').replace(/%0A/g, '%250A').replace(/%0D/g, '%250D')
// @note At this point %00 = %252500, %0A = %25250A, %0D = %25250D
hash = crypto.createHmac('sha1', signKey).update(baseString).digest('base64')
}
}
// Convert params to html-form type (change 'key' to 'name')
let params = []
mergedParams.forEach((param) => {
params.push({
name: param.key,
value: param.value
})
if (param.key === 'oauth_nonce') {
params.push({
name: 'oauth_signature',
value: hash
})
}
})
// Generate OAuth header
let header = 'OAuth '
params.forEach((param) => {
let key = param.name
let value = param.value
if (conf.encodeNames) {
key = encode.encode(key, conf.protocol, conf.encodeNull)
}
if (conf.encodeValues) {
value = encode.encode(value, conf.protocol, conf.encodeNull)
}
if (value !== '') {
header += key + '="' + value + '",'
} else {
header += key + '",'
}
})
let queryString = ''
let i = 0
params.forEach((param) => {
let key = param.name
let value = param.value
if (conf.encodeNames) {
key = encode.encode(key, conf.protocol, conf.encodeNull)
}
if (conf.encodeValues) {
value = encode.encode(value, conf.protocol, conf.encodeNull)
}
if (value !== '') {
queryString += key + '=' + value
} else {
queryString += key
}
if (i !== (params.length - 1)) {
queryString += '&'
}
i++
})
return {
params: params,
header: header.slice(0, -1),
string: queryString
}
}
this.signKey = (secret, tokenSecret, ampersand = true) => {
if (ampersand || tokenSecret !== '') {
return encode.encode(secret) + '&' + encode.encode(tokenSecret)
} else {
return encode.encode(secret)
}
}
this.paramString = (params, keepEmpty = true, sort = true) => {
let paramString = ''
let enc = []
params.forEach((param) => {
if (param.value !== '') {
enc.push(param.key + '=' + param.value + '&')
} else if (param.value === '' && param.key !== 'oauth_token' && keepEmpty) {
enc.push(param.key + '=&')
}
})
if (sort) {
enc.sort()
}
// Decode encoded to get equal sorting as encoded
let dec = []
enc.forEach((param) => {
let p = param.split('=')
if (p.length === 2) {
dec.push({
key: encode.decode(p[0]),
value: encode.decode(p[1]).slice(0, -1)
})
} else {
dec.push({
key: encode.decode(p[0]),
value: ''
})
}
})
enc.forEach(param => {
paramString += param
})
if (enc.length > 0) {
paramString = paramString.slice(0, -1)
}
return {
string: paramString,
encoded: enc,
decoded: dec
}
}
}
}
export default new Sign()