magento2-api-wrapper
Version:
Minimal Magento 2 API library. Both node and browser compatible
156 lines (155 loc) • 5.42 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.OAuth = void 0;
/**
* A OAuth 1.0a implementation, planned for Magento 2 usage.
*
* @see https://oauth.net/core/1.0a/
*/
class OAuth {
constructor(opts) {
Object.defineProperty(this, "consumer", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "version", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "realm", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "signatureMethod", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "hashMethods", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "nonceLength", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.consumer = opts.consumer;
this.version = opts.version || "1.0";
this.realm = opts.realm;
this.signatureMethod = opts.signatureMethod || "PLAINTEXT";
this.nonceLength = opts.nonceLength || 32;
this.hashMethods = {
PLAINTEXT: (key) => Promise.resolve(key),
...opts.hashMethods,
};
}
/**
* Returns signed Authorization header value
*/
async authorize(request, token) {
const oauthData = {
oauth_consumer_key: this.consumer.key,
oauth_nonce: this.nonce(),
oauth_signature_method: this.signatureMethod,
oauth_timestamp: this.timestamp(),
oauth_token: token.key,
oauth_version: this.version,
};
const signatureMethod = oauthData.oauth_signature_method;
if (!signatureMethod || !this.hashMethods[signatureMethod]) {
throw new Error(`Unknown signing method: ${signatureMethod}`);
}
const signatureBaseString = await this.getSignatureBaseString(request, oauthData);
const oauth = Object.entries(oauthData);
oauth.push([
"oauth_signature",
await this.hashMethods[signatureMethod](this.hashKey(token), signatureBaseString),
]);
oauth.sort((a, b) => a[0].localeCompare(b[0]));
const oauthStr = oauth.map((h) => `${h[0]}="${encodeRFC3986URIComponent(h[1])}"`).join(",");
const realm = this.realm ? `realm="${this.realm}",` : ``;
return `OAuth ${realm}${oauthStr}`;
}
/**
* Signs Request and adds Authorize header
*/
async authRequest(request, token) {
const auth = await this.authorize(request, token);
request.headers.set("authorization", auth);
return auth;
}
async getSignatureBaseString(request, oauthData) {
const signingParams = [
...Object.entries(oauthData),
...(await this.collectRequestParams(request)),
]
.sort((a, b) => a[0].localeCompare(b[0]));
return [
request.method,
this.constructRequestUrl(request),
signingParams.map(p => `${encodeURIComponent(p[0])}=${encodeURIComponent(p[1])}`).join('&'),
]
.filter((p) => p)
.map(encodeRFC3986URIComponent)
.join("&");
}
/**
* Collects request params used for signing in a entries array format
*/
async collectRequestParams(request) {
const params = [];
const url = new URL(request.url);
params.push(...url.searchParams.entries());
if (request.headers?.get("content-type") ===
"application/x-www-form-urlencoded" && request.body) {
const reader = request.body.getReader();
let body = "";
let part;
do {
part = await reader.read();
body += part.value;
} while (part.value !== undefined);
reader.releaseLock();
params.push(...(new URLSearchParams(body)).entries());
}
return params
.filter((p) => p[1] !== undefined);
}
/**
* Constructs url for signing according to RFC section 9.1.2 Construct Request URL
*/
constructRequestUrl(request) {
const url = new URL(request.url);
url.search = "";
url.hash = "";
return url.toString();
}
hashKey(token) {
return `${encodeRFC3986URIComponent(this.consumer.secret)}&${encodeRFC3986URIComponent(token.secret)}`;
}
nonce() {
const array = new Uint8Array((this.nonceLength || 32) / 2);
crypto.getRandomValues(array);
return Array.from(array, (dec) => dec.toString(16).padStart(2, "0"))
.join("");
}
timestamp() {
return Math.floor(Date.now() / 1000).toString();
}
}
exports.OAuth = OAuth;
function encodeRFC3986URIComponent(str) {
return encodeURIComponent(str)
.replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
}