wmt-marketplace-auth-canada
Version:
Generate the authentication headers required by Walmart Marketplace API.
264 lines (247 loc) • 7.13 kB
text/typescript
import * as Util from './util';
import * as uuid from 'uuid';
import { createSign, Signer } from 'crypto';
/**
* WM_SVC Authentication header namespace.
*/
export interface WMService {
/**
* Walmart Service Name.
*
* Header WM_SVC.NAME.
*/
Name: string;
}
/**
* WM_QOS Authentication header namespace.
*/
export interface WMQOS {
/**
* A unique ID to correlate a vendor's calls with the Walmart system.
*
* Header WM_QOS.CORRELATION_ID.
*/
CorrelationId: string;
}
/**
* WM_SEC Authentication header namespace.
*/
export interface WMSecurity {
/**
* The Epoch timestamp.
*
* Header WM_SEC.TIMESTAMP.
*/
Timestamp: number;
/**
* The vendor's digital signature.
*
* Header WM_SEC.AUTH_SIGNATURE.
*/
AuthSignature: string;
}
/**
* WM_CONSUMER.CHANNEL Authentication header namespace.
*/
export interface Channel {
/**
* A unique ID to track the consumer request by channel. Use the Consumer Channel
* Type received during onboarding.
*
* Header WM_CONSUMER.CHANNEL.TYPE.
*/
Type: string;
}
/**
* WM_CONSUMER Authentication header namespace.
*/
export interface WMConsumer {
/**
* WM_CONSUMER.CHANNEL Authentication header namespace.
*/
Channel: Channel;
/**
* The Walmart Consumer ID from Developer Center required to access the API.
*
* Header WM_CONSUMER.ID.
*/
ConsumerId: string;
}
/**
* Walmart authentication headers. Use the custom class to set custom values to the
* headers before adding to the request.
*
* @see {@link https://developer.walmart.com/#/apicenter/marketPlace/latest#apiAuthentication}
*/
export class Custom {
/**
* WM_SVC Authentication headers.
*/
WMService: WMService;
/**
* WM_QOS Authentication headers.
*/
WMQOS: WMQOS;
/**
* WM_SEC Authentication headers.
*/
WMSecurity: WMSecurity;
/**
* WM_CONSUMER Authentication headers.
*/
WMConsumer: WMConsumer;
/**
* The desired returned data format of the response. May override the default to
* 'application/xml' if an XML response is preferred instead of JSON.
*/
Accept: string;
/**
* The data format used in the request. May override the default to
* 'application/xml' if an XML response is preferred instead of JSON.
*/
ContentType: string;
constructor() {
this.WMQOS = { CorrelationId: uuid()};
this.WMService = { Name: 'Walmart Marketplace' };
this.Accept = 'application/json';
this.ContentType = 'application/json';
}
/**
* Set the vendor's Correlation ID.
*
* @param correlationId A unique ID to correlate a vendor's calls with the Walmart
* system.
*/
setCorrelationId(correlationId: string): void {
this.WMQOS = { CorrelationId: correlationId };
}
/**
* Set the Consumer ID.
*
* @param consumer The Walmart Consumer ID from Developer Center required to access
* the API.
*/
setConsumer(consumer: WMConsumer): void {
this.WMConsumer = consumer;
}
/**
* Set the request Timestamp. Note: Resets the AuthSignature to empty string. This
* method is primarily used by tests. Timestamp is automatically set by sign()
* method if not otherwise set.
*
* @param timestamp Epoch timestamp in milliseconds.
*/
setTimestamp(timestamp: number): void {
this.WMSecurity = {
Timestamp: timestamp,
AuthSignature: ''
}
}
}
/**
* The API request headers compiled according to the API spec, including the required
* digital signature.
*/
export interface Signed {
'WM_SVC.NAME': string;
'WM_QOS.CORRELATION_ID': string;
'WM_SEC.TIMESTAMP': number;
'WM_SEC.AUTH_SIGNATURE': string;
'WM_CONSUMER.CHANNEL.TYPE': string;
'WM_CONSUMER.ID': string;
'Accept': string;
'Content-Type': string;
}
/**
* The request properties required to generate the digital signature.
*/
export interface DigitalSignatureRequest {
/**
* The full URL to call, including path and query parameters.
*/
RequestUrl: string;
/**
* The vendor's Base-64-encoded, PKCS#8 stored Private Key.
*/
PrivateKey: string;
/**
* The HTTP request method used, in capital letters (i.e. GET, POST).
*/
RequestMethod: string;
}
/**
* Returns the signed headers required for the API request.
*
* @param custom Walmart authentication headers. Use the custom class to set custom
* values to the headers before adding to the request.
* @param request The request properties required to generate the digital signature.
*/
export function sign(custom: Custom, request: DigitalSignatureRequest): Signed {
/**
* An Epoch timestamp is required for the digital signature.
*/
let epoch: number = Util.epochInMilliseconds();
/**
* Override the generated Epoch timestamp if a timestamp was included in the
* custom headers.
*/
if (custom.WMSecurity && custom.WMSecurity.Timestamp) {
epoch = custom.WMSecurity.Timestamp;
}
/**
* The generated digital signature.
*/
let signature: string = digitalSignature(custom, request, epoch);
/**
* Override the generated digital signature if a signature was included in the
* custom headers.
*/
if (custom.WMSecurity && custom.WMSecurity.AuthSignature) {
signature = custom.WMSecurity.AuthSignature;
}
/**
* The signed request headers according to Walmart API spec.
*/
let signedHeaders: Signed = {
'WM_SVC.NAME': custom.WMService.Name,
'WM_QOS.CORRELATION_ID': custom.WMQOS.CorrelationId,
'WM_SEC.TIMESTAMP': epoch,
'WM_SEC.AUTH_SIGNATURE': signature,
'WM_CONSUMER.CHANNEL.TYPE': custom.WMConsumer.Channel.Type,
'WM_CONSUMER.ID': custom.WMConsumer.ConsumerId,
'Accept': custom.Accept,
'Content-Type': custom.Accept
}
return signedHeaders;
}
/**
* Generates the digital signature required for the API request.
*
* @param custom Walmart authentication headers. Use the custom class to set custom
* values to the headers before adding to the request.
* @param request The request properties required to generate the digital signature.
* @param epoch An Epoch timestamp is required for the digital signature.
*/
function digitalSignature(custom: Custom, request: DigitalSignatureRequest, epoch: number): string {
/**
* Node Crypto Sign object that uses the given algorithm.
*
* @see {@link https://nodejs.org/api/crypto.html#crypto_class_sign}
*/
const signer: Signer = createSign('RSA-SHA256');
/**
* Walmart API request string to be signed.
*/
let stringToSign: string = custom.WMConsumer.ConsumerId + "\n" + request.RequestUrl + "\n"
+ request.RequestMethod.toUpperCase() + "\n" + epoch + "\n";
/**
* Updates the Sign content with the given data.
* @see {@link https://nodejs.org/api/crypto.html#crypto_sign_update_data_inputencoding}
*/
signer.update(stringToSign);
/**
* Private key wrapped in key header and footer.
*/
let privateKey: string = "-----BEGIN PRIVATE KEY-----\n" + request.PrivateKey + "\n-----END PRIVATE KEY-----";
return signer.sign(privateKey, 'base64');
}