ask-sdk-express-adapter
Advanced tools
Comparing version 2.1.0 to 2.10.0
# Change Log | ||
All notable changes to this project will be documented in this file. | ||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||
# [2.10.0](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/compare/v2.9.0...v2.10.0) (2020-10-08) | ||
### Bug Fixes | ||
* fix the github action workflow and compilation making relative require fail issue ([#658](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/issues/658)) ([895c88e](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/commit/895c88e8bd875488a62966680a3d9d8eb2bcd9ea)) | ||
* update rootDir to point to the right root dir in tsconfig file in tst ([66ca284](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/commit/66ca284e13ed1dc881a13d69a399035eb4725e28)) | ||
* Updated all packages .npmignore files ([de76a18](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/commit/de76a18bcd21c6a411ddd72a09064e6d8b00c6ae)) | ||
* Updated all packages package.json ([#656](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/issues/656)) ([c27c3e6](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/commit/c27c3e6842834d0fea365613da7f3598955b558f)) | ||
### Features | ||
* Add special timestamp verification for skill event requests ([#651](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/issues/651)) ([9b0dd9f](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/commit/9b0dd9fbd0169e140be09ed3dfda2e30772dd0af)) | ||
* eslint migration and fixes, github actions workflows and updates on package.json for ask-sdk-express-adapter ([e944476](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/commit/e94447697fe0b93f7dc35bd201ac40b02ec7a811)) | ||
* Update root package.json to fix doc generation ([4bf482b](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/commit/4bf482bb889fc9b93ad8d0afe8725862c5690f24)) | ||
# Change Log | ||
# 2.1.0 (2020-05-07) | ||
@@ -13,2 +39,2 @@ | ||
- Case-insensitive header value retrieval for request verification. [604](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/issues/604) | ||
- Case-insensitive header value retrieval for request verification. [604](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/issues/604) |
@@ -14,47 +14,12 @@ "use strict"; | ||
*/ | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var ask_sdk_core_1 = require("ask-sdk-core"); | ||
var body_parser_1 = require("body-parser"); | ||
var util_1 = require("../util"); | ||
var verifier_1 = require("../verifier"); | ||
exports.ExpressAdapter = void 0; | ||
const ask_sdk_core_1 = require("ask-sdk-core"); | ||
const body_parser_1 = require("body-parser"); | ||
const util_1 = require("../util"); | ||
const verifier_1 = require("../verifier"); | ||
/** | ||
* Express adapter class | ||
*/ | ||
var ExpressAdapter = /** @class */ (function () { | ||
class ExpressAdapter { | ||
/** | ||
@@ -68,6 +33,3 @@ * Constructor | ||
*/ | ||
function ExpressAdapter(skill, verifySignature, verifyTimeStamp, verifiers) { | ||
if (verifySignature === void 0) { verifySignature = true; } | ||
if (verifyTimeStamp === void 0) { verifyTimeStamp = true; } | ||
if (verifiers === void 0) { verifiers = []; } | ||
constructor(skill, verifySignature = true, verifyTimeStamp = true, verifiers = []) { | ||
this.skill = skill; | ||
@@ -94,6 +56,5 @@ this.verifiers = verifiers; | ||
*/ | ||
ExpressAdapter.prototype.getRequestHandlers = function () { | ||
var _this = this; | ||
var requestHandlers = []; | ||
requestHandlers.push(function (req, res, next) { | ||
getRequestHandlers() { | ||
const requestHandlers = []; | ||
requestHandlers.push((req, res, next) => { | ||
if (req.body) { | ||
@@ -109,27 +70,16 @@ res.statusCode = 500; | ||
})); | ||
requestHandlers.push(function (req, res) { return __awaiter(_this, void 0, void 0, function () { | ||
var responseEnvelope, err_1; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
_a.trys.push([0, 2, , 3]); | ||
return [4 /*yield*/, util_1.asyncVerifyRequestAndDispatch(req.headers, req.body, this.skill, this.verifiers)]; | ||
case 1: | ||
responseEnvelope = _a.sent(); | ||
res.json(responseEnvelope); | ||
return [3 /*break*/, 3]; | ||
case 2: | ||
err_1 = _a.sent(); | ||
res.statusCode = err_1.name === 'AskSdk.Request verification failed Error' ? 400 : 500; | ||
res.send(err_1.name + ", " + err_1.message); | ||
return [3 /*break*/, 3]; | ||
case 3: return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
requestHandlers.push(async (req, res) => { | ||
try { | ||
const responseEnvelope = await util_1.asyncVerifyRequestAndDispatch(req.headers, req.body, this.skill, this.verifiers); | ||
res.json(responseEnvelope); | ||
} | ||
catch (err) { | ||
res.statusCode = err.name === 'AskSdk.Request verification failed Error' ? 400 : 500; | ||
res.send(`${err.name}, ${err.message}`); | ||
} | ||
}); | ||
return requestHandlers; | ||
}; | ||
return ExpressAdapter; | ||
}()); | ||
} | ||
} | ||
exports.ExpressAdapter = ExpressAdapter; | ||
//# sourceMappingURL=ExpressAdapter.js.map |
export { ExpressAdapter } from './adapter/ExpressAdapter'; | ||
export { SkillRequestSignatureVerifier, TimestampVerifier, Verifier, } from './verifier'; | ||
export { SkillRequestSignatureVerifier, TimestampVerifier, Verifier } from './verifier'; |
@@ -16,6 +16,6 @@ "use strict"; | ||
var ExpressAdapter_1 = require("./adapter/ExpressAdapter"); | ||
exports.ExpressAdapter = ExpressAdapter_1.ExpressAdapter; | ||
Object.defineProperty(exports, "ExpressAdapter", { enumerable: true, get: function () { return ExpressAdapter_1.ExpressAdapter; } }); | ||
var verifier_1 = require("./verifier"); | ||
exports.SkillRequestSignatureVerifier = verifier_1.SkillRequestSignatureVerifier; | ||
exports.TimestampVerifier = verifier_1.TimestampVerifier; | ||
Object.defineProperty(exports, "SkillRequestSignatureVerifier", { enumerable: true, get: function () { return verifier_1.SkillRequestSignatureVerifier; } }); | ||
Object.defineProperty(exports, "TimestampVerifier", { enumerable: true, get: function () { return verifier_1.TimestampVerifier; } }); | ||
//# sourceMappingURL=index.js.map |
@@ -14,40 +14,5 @@ "use strict"; | ||
*/ | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var ask_sdk_core_1 = require("ask-sdk-core"); | ||
exports.asyncVerifyRequestAndDispatch = void 0; | ||
const ask_sdk_core_1 = require("ask-sdk-core"); | ||
/** | ||
@@ -63,41 +28,21 @@ * Verify request and dispatch | ||
*/ | ||
function asyncVerifyRequestAndDispatch(httpRequestHeader, httpRequestBody, skill, verifiers) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var err_1, responseEnvelope, err_2; | ||
var _this = this; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
_a.trys.push([0, 2, , 3]); | ||
return [4 /*yield*/, Promise.all(verifiers.map(function (verifier) { return __awaiter(_this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, verifier.verify(httpRequestBody, httpRequestHeader)]; | ||
case 1: | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }))]; | ||
case 1: | ||
_a.sent(); | ||
return [3 /*break*/, 3]; | ||
case 2: | ||
err_1 = _a.sent(); | ||
throw ask_sdk_core_1.createAskSdkError('Request verification failed', err_1.message); | ||
case 3: | ||
_a.trys.push([3, 5, , 6]); | ||
return [4 /*yield*/, skill.invoke(JSON.parse(httpRequestBody))]; | ||
case 4: | ||
responseEnvelope = _a.sent(); | ||
return [3 /*break*/, 6]; | ||
case 5: | ||
err_2 = _a.sent(); | ||
throw ask_sdk_core_1.createAskSdkError('Skill dispatch failed', err_2.message); | ||
case 6: return [2 /*return*/, responseEnvelope]; | ||
} | ||
}); | ||
}); | ||
async function asyncVerifyRequestAndDispatch(httpRequestHeader, httpRequestBody, skill, verifiers) { | ||
try { | ||
await Promise.all(verifiers.map(async (verifier) => { | ||
await verifier.verify(httpRequestBody, httpRequestHeader); | ||
})); | ||
} | ||
catch (err) { | ||
throw ask_sdk_core_1.createAskSdkError('Request verification failed', err.message); | ||
} | ||
let responseEnvelope; | ||
try { | ||
responseEnvelope = await skill.invoke(JSON.parse(httpRequestBody)); | ||
} | ||
catch (err) { | ||
throw ask_sdk_core_1.createAskSdkError('Skill dispatch failed', err.message); | ||
} | ||
return responseEnvelope; | ||
} | ||
exports.asyncVerifyRequestAndDispatch = asyncVerifyRequestAndDispatch; | ||
//# sourceMappingURL=index.js.map |
@@ -15,3 +15,4 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var node_forge_1 = require("node-forge"); | ||
exports.generateCAStore = exports.generateCertificatesArray = void 0; | ||
const node_forge_1 = require("node-forge"); | ||
/** | ||
@@ -22,10 +23,10 @@ * Function used to convert certificate chain string into Certificate Object Array | ||
*/ | ||
var CERT_START_KEY = '-----BEGIN CERTIFICATE-----'; | ||
var CERT_END_KEY = '-----END CERTIFICATE-----'; | ||
const CERT_START_KEY = '-----BEGIN CERTIFICATE-----'; | ||
const CERT_END_KEY = '-----END CERTIFICATE-----'; | ||
function generateCertificatesArray(certChain) { | ||
var certs = []; | ||
const certs = []; | ||
while (certChain.length > 0) { | ||
var start = certChain.indexOf(CERT_START_KEY); | ||
var end = certChain.indexOf(CERT_END_KEY) + CERT_END_KEY.length; | ||
var certString = certChain.slice(start, end); | ||
const start = certChain.indexOf(CERT_START_KEY); | ||
const end = certChain.indexOf(CERT_END_KEY) + CERT_END_KEY.length; | ||
const certString = certChain.slice(start, end); | ||
certs.push(node_forge_1.pki.certificateFromPem(certString)); | ||
@@ -42,5 +43,4 @@ certChain = certChain.slice(end).trim(); | ||
function generateCAStore(certs) { | ||
var caStore = node_forge_1.pki.createCaStore([]); | ||
for (var _i = 0, certs_1 = certs; _i < certs_1.length; _i++) { | ||
var cert = certs_1[_i]; | ||
const caStore = node_forge_1.pki.createCaStore([]); | ||
for (const cert of certs) { | ||
try { | ||
@@ -51,3 +51,3 @@ caStore.addCertificate(cert); | ||
// do nothing | ||
// node-forge doesn't support ECDSA encryped pem | ||
// node-forge doesn't support ECDSA encrypted pem | ||
} | ||
@@ -54,0 +54,0 @@ } |
@@ -17,3 +17,3 @@ /// <reference types="node" /> | ||
/** | ||
* Implemention of Verifier which provides a utility method to verify the signature of a skill request. | ||
* Implementation of Verifier which provides a utility method to verify the signature of a skill request. | ||
*/ | ||
@@ -34,3 +34,3 @@ export declare class SkillRequestSignatureVerifier implements Verifier { | ||
/** | ||
* Validate Url and retrive certificate chain | ||
* Validate Url and retrieve certificate chain | ||
* | ||
@@ -43,3 +43,3 @@ * This method validates if the URL is valid and loads | ||
*/ | ||
private _validateUrlAndRetriveCertChain; | ||
private _validateUrlAndRetrieveCertChain; | ||
/** | ||
@@ -69,3 +69,3 @@ * Validate the URL containing the certificate chain | ||
* | ||
* This method use the validated cerificate url to retrive certificate chain | ||
* This method use the validated certificate url to retrieve certificate chain | ||
* @private | ||
@@ -99,3 +99,3 @@ * @param {string} signatureCertChainUrl URL for retrieving certificate chain | ||
/** | ||
* Implemention of Verifier which provides a utility method to handle | ||
* Implementation of Verifier which provides a utility method to handle | ||
* the request timestamp verification of the input request. | ||
@@ -102,0 +102,0 @@ */ |
@@ -14,45 +14,10 @@ "use strict"; | ||
*/ | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var ask_sdk_core_1 = require("ask-sdk-core"); | ||
var crypto = require("crypto"); | ||
var client = require("https"); | ||
var node_forge_1 = require("node-forge"); | ||
var url = require("url"); | ||
var helper_1 = require("./helper"); | ||
exports.TimestampVerifier = exports.SkillRequestSignatureVerifier = void 0; | ||
const ask_sdk_core_1 = require("ask-sdk-core"); | ||
const crypto = require("crypto"); | ||
const client = require("https"); | ||
const node_forge_1 = require("node-forge"); | ||
const url = require("url"); | ||
const helper_1 = require("./helper"); | ||
/** | ||
@@ -62,18 +27,25 @@ * Provide constant value | ||
*/ | ||
var VALID_SIGNING_CERT_CHAIN_PROTOCOL = 'https:'; | ||
var VALID_SIGNING_CERT_CHAIN_URL_HOST_NAME = 's3.amazonaws.com'; | ||
var VALID_SIGNING_CERT_CHAIN_URL_PATH_PREFIX = '/echo.api/'; | ||
var SIGNATURE_CERT_CHAIN_URL_HEADER = 'SignatureCertChainUrl'; | ||
var SIGNATURE_HEADER = 'Signature'; | ||
var SIGNATURE_FORMAT = 'base64'; | ||
var CERT_CHAIN_URL_PORT = 443; | ||
var CERT_CHAIN_DOMAIN = 'echo-api.amazon.com'; | ||
var CHARACTER_ENCODING = 'utf8'; | ||
var DEFAULT_TIMESTAMP_TOLERANCE_IN_MILLIS = 150000; | ||
var MAX_TIMESTAMP_TOLERANCE_IN_MILLIS = 3600000; | ||
const VALID_SIGNING_CERT_CHAIN_PROTOCOL = 'https:'; | ||
const VALID_SIGNING_CERT_CHAIN_URL_HOST_NAME = 's3.amazonaws.com'; | ||
const VALID_SIGNING_CERT_CHAIN_URL_PATH_PREFIX = '/echo.api/'; | ||
const SIGNATURE_CERT_CHAIN_URL_HEADER = 'SignatureCertChainUrl'; | ||
const SIGNATURE_HEADER = 'Signature'; | ||
const SIGNATURE_FORMAT = 'base64'; | ||
const CERT_CHAIN_URL_PORT = 443; | ||
const CERT_CHAIN_DOMAIN = 'echo-api.amazon.com'; | ||
const CHARACTER_ENCODING = 'utf8'; | ||
const MAXIMUM_NORMAL_REQUEST_TOLERANCE_IN_MILLIS = 150000; | ||
const MAXIMUM_SKILL_EVENT_TOLERANCE_IN_MILLIS = 3600000; | ||
const ALEXA_SKILL_EVENT_LIST = new Set([ | ||
'AlexaSkillEvent.SkillEnabled', | ||
'AlexaSkillEvent.SkillDisabled', | ||
'AlexaSkillEvent.SkillPermissionChanged', | ||
'AlexaSkillEvent.SkillPermissionAccepted', | ||
'AlexaSkillEvent.SkillAccountLinked' | ||
]); | ||
/** | ||
* Implemention of Verifier which provides a utility method to verify the signature of a skill request. | ||
* Implementation of Verifier which provides a utility method to verify the signature of a skill request. | ||
*/ | ||
var SkillRequestSignatureVerifier = /** @class */ (function () { | ||
function SkillRequestSignatureVerifier() { | ||
class SkillRequestSignatureVerifier { | ||
constructor() { | ||
this.certCache = new Map(); | ||
@@ -90,42 +62,32 @@ } | ||
*/ | ||
SkillRequestSignatureVerifier.prototype.verify = function (requestEnvelope, headers) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var signatureCertChainUrl, signature, _i, _a, key, keyInLowerCase, pemCert, err_1; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
for (_i = 0, _a = Object.keys(headers); _i < _a.length; _i++) { | ||
key = _a[_i]; | ||
keyInLowerCase = key.toLocaleLowerCase(); | ||
if (keyInLowerCase === SIGNATURE_CERT_CHAIN_URL_HEADER.toLowerCase()) { | ||
signatureCertChainUrl = headers[key]; | ||
} | ||
else if (keyInLowerCase === SIGNATURE_HEADER.toLowerCase()) { | ||
signature = headers[key]; | ||
} | ||
} | ||
if (!signatureCertChainUrl) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, 'Missing Certificate for the skill request'); | ||
} | ||
if (!signature) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, 'Missing Signature for the skill request'); | ||
} | ||
_b.label = 1; | ||
case 1: | ||
_b.trys.push([1, 3, , 4]); | ||
return [4 /*yield*/, this._validateUrlAndRetriveCertChain(signatureCertChainUrl)]; | ||
case 2: | ||
pemCert = _b.sent(); | ||
this._validateRequestBody(pemCert, signature, requestEnvelope); | ||
return [3 /*break*/, 4]; | ||
case 3: | ||
err_1 = _b.sent(); | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, err_1.message); | ||
case 4: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
async verify(requestEnvelope, headers) { | ||
// throw error if signature or signatureCertChainUrl are not present | ||
let signatureCertChainUrl; | ||
let signature; | ||
for (const key of Object.keys(headers)) { | ||
const keyInLowerCase = key.toLocaleLowerCase(); | ||
if (keyInLowerCase === SIGNATURE_CERT_CHAIN_URL_HEADER.toLowerCase()) { | ||
signatureCertChainUrl = headers[key]; | ||
} | ||
else if (keyInLowerCase === SIGNATURE_HEADER.toLowerCase()) { | ||
signature = headers[key]; | ||
} | ||
} | ||
if (!signatureCertChainUrl) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, 'Missing Certificate for the skill request'); | ||
} | ||
if (!signature) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, 'Missing Signature for the skill request'); | ||
} | ||
try { | ||
// retrieve validated certification chain in pem format, then check if signature and request body are matched | ||
const pemCert = await this._validateUrlAndRetrieveCertChain(signatureCertChainUrl); | ||
this._validateRequestBody(pemCert, signature, requestEnvelope); | ||
} | ||
catch (err) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, err.message); | ||
} | ||
} | ||
/** | ||
* Validate Url and retrive certificate chain | ||
* Validate Url and retrieve certificate chain | ||
* | ||
@@ -138,17 +100,7 @@ * This method validates if the URL is valid and loads | ||
*/ | ||
SkillRequestSignatureVerifier.prototype._validateUrlAndRetriveCertChain = function (signatureCertChainUrl) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var pemCert; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
this._validateCertificateUrl(signatureCertChainUrl); | ||
return [4 /*yield*/, this._loadCertChain(signatureCertChainUrl)]; | ||
case 1: | ||
pemCert = _a.sent(); | ||
return [2 /*return*/, pemCert]; | ||
} | ||
}); | ||
}); | ||
}; | ||
async _validateUrlAndRetrieveCertChain(signatureCertChainUrl) { | ||
this._validateCertificateUrl(signatureCertChainUrl); | ||
const pemCert = await this._loadCertChain(signatureCertChainUrl); | ||
return pemCert; | ||
} | ||
/** | ||
@@ -162,29 +114,29 @@ * Validate the URL containing the certificate chain | ||
*/ | ||
SkillRequestSignatureVerifier.prototype._validateCertificateUrl = function (signatureCertChainUrl) { | ||
var urlObj = url.parse(signatureCertChainUrl); | ||
_validateCertificateUrl(signatureCertChainUrl) { | ||
const urlObj = url.parse(signatureCertChainUrl); | ||
// Validate the protocol | ||
var protocol = urlObj.protocol; | ||
const protocol = urlObj.protocol; | ||
if (protocol.toLowerCase() !== VALID_SIGNING_CERT_CHAIN_PROTOCOL) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, "SignatureCertChainUrl contains an unsupported protocol " + protocol + "." | ||
+ (" Expecting " + VALID_SIGNING_CERT_CHAIN_PROTOCOL)); | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, `SignatureCertChainUrl contains an unsupported protocol ${protocol}.` | ||
+ ` Expecting ${VALID_SIGNING_CERT_CHAIN_PROTOCOL}`); | ||
} | ||
// Validate the hostname | ||
var hostname = urlObj.hostname; | ||
const hostname = urlObj.hostname; | ||
if (hostname !== VALID_SIGNING_CERT_CHAIN_URL_HOST_NAME) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, "SignatureCertChainUrl has invalid host name: " + hostname + "." | ||
+ (" Expecting " + VALID_SIGNING_CERT_CHAIN_URL_HOST_NAME)); | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, `SignatureCertChainUrl has invalid host name: ${hostname}.` | ||
+ ` Expecting ${VALID_SIGNING_CERT_CHAIN_URL_HOST_NAME}`); | ||
} | ||
// Validate the path prefix | ||
var path = urlObj.pathname; | ||
const path = urlObj.pathname; | ||
if (!path.startsWith(VALID_SIGNING_CERT_CHAIN_URL_PATH_PREFIX)) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, "SignatureCertChainUrl has invalid path: " + path + "." | ||
+ (" Expecting the path to start with " + VALID_SIGNING_CERT_CHAIN_URL_PATH_PREFIX)); | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, `SignatureCertChainUrl has invalid path: ${path}.` | ||
+ ` Expecting the path to start with ${VALID_SIGNING_CERT_CHAIN_URL_PATH_PREFIX}`); | ||
} | ||
// Validate the port uses the default of 443 for HTTPS if explicitly defined in the URL | ||
var port = Number(urlObj.port); | ||
const port = Number(urlObj.port); | ||
if (port && port !== CERT_CHAIN_URL_PORT) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, "SignatureCertChainUrl has invalid port: " + port + "." | ||
+ (" Expecting " + CERT_CHAIN_URL_PORT)); | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, `SignatureCertChainUrl has invalid port: ${port}.` | ||
+ ` Expecting ${CERT_CHAIN_URL_PORT}`); | ||
} | ||
}; | ||
} | ||
/** | ||
@@ -201,27 +153,18 @@ * Load certificate chain | ||
*/ | ||
SkillRequestSignatureVerifier.prototype._loadCertChain = function (signatureCertChainUrl) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var pemCert; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
// try to get cert chain in cache | ||
if (this.certCache.has(signatureCertChainUrl)) { | ||
return [2 /*return*/, this.certCache.get(signatureCertChainUrl)]; | ||
} | ||
return [4 /*yield*/, this._getCertChainByUrl(signatureCertChainUrl)]; | ||
case 1: | ||
pemCert = _a.sent(); | ||
// validate the cert chain loaded from url, if it is valid, update cache | ||
this._validateCertChain(pemCert); | ||
this.certCache.set(signatureCertChainUrl, pemCert); | ||
return [2 /*return*/, pemCert]; | ||
} | ||
}); | ||
}); | ||
}; | ||
async _loadCertChain(signatureCertChainUrl) { | ||
// try to get cert chain in cache | ||
if (this.certCache.has(signatureCertChainUrl)) { | ||
return this.certCache.get(signatureCertChainUrl); | ||
} | ||
// if there is a cache miss, load cert chain from certificate Url | ||
const pemCert = await this._getCertChainByUrl(signatureCertChainUrl); | ||
// validate the cert chain loaded from url, if it is valid, update cache | ||
this._validateCertChain(pemCert); | ||
this.certCache.set(signatureCertChainUrl, pemCert); | ||
return pemCert; | ||
} | ||
/** | ||
* Loads the certificate chain from the URL. | ||
* | ||
* This method use the validated cerificate url to retrive certificate chain | ||
* This method use the validated certificate url to retrieve certificate chain | ||
* @private | ||
@@ -231,22 +174,22 @@ * @param {string} signatureCertChainUrl URL for retrieving certificate chain | ||
*/ | ||
SkillRequestSignatureVerifier.prototype._getCertChainByUrl = function (signatureCertChainUrl) { | ||
return new Promise(function (resolve, reject) { | ||
var clientRequest = client.get(signatureCertChainUrl, function (resp) { | ||
var data = ''; | ||
var statusCode; | ||
_getCertChainByUrl(signatureCertChainUrl) { | ||
return new Promise((resolve, reject) => { | ||
const clientRequest = client.get(signatureCertChainUrl, (resp) => { | ||
let data = ''; | ||
let statusCode; | ||
if (!resp || resp.statusCode !== 200) { | ||
statusCode = resp ? resp.statusCode : 0; | ||
reject(new Error("Unable to load x509 certificate from URL: " + signatureCertChainUrl + ". Response status code: " + statusCode)); | ||
reject(new Error(`Unable to load x509 certificate from URL: ${signatureCertChainUrl}. Response status code: ${statusCode}`)); | ||
} | ||
// A chunk of data has been recieved. | ||
// A chunk of data has been received. | ||
resp.setEncoding(CHARACTER_ENCODING); | ||
resp.on('data', function (chunk) { | ||
resp.on('data', (chunk) => { | ||
data += chunk; | ||
}); | ||
// The whole response has been received. | ||
resp.on('end', function () { | ||
resp.on('end', () => { | ||
resolve(data); | ||
}); | ||
}); | ||
clientRequest.on('error', function (err) { | ||
clientRequest.on('error', (err) => { | ||
reject(new Error(err.message)); | ||
@@ -256,3 +199,3 @@ }); | ||
}); | ||
}; | ||
} | ||
/** | ||
@@ -268,8 +211,8 @@ * Validate certificate chain | ||
*/ | ||
SkillRequestSignatureVerifier.prototype._validateCertChain = function (pemCert) { | ||
var cert = node_forge_1.pki.certificateFromPem(pemCert); | ||
_validateCertChain(pemCert) { | ||
const cert = node_forge_1.pki.certificateFromPem(pemCert); | ||
// check the before/after dates on the certificate are still valid for the present time | ||
var now = new Date().getTime(); | ||
var notAfter = new Date(cert.validity.notAfter).getTime(); | ||
var notBefore = new Date(cert.validity.notBefore).getTime(); | ||
const now = new Date().getTime(); | ||
const notAfter = new Date(cert.validity.notAfter).getTime(); | ||
const notBefore = new Date(cert.validity.notBefore).getTime(); | ||
if (!(now <= notAfter && now >= notBefore)) { | ||
@@ -279,13 +222,13 @@ throw ask_sdk_core_1.createAskSdkError(this.constructor.name, 'Signing Certificate expired or not started'); | ||
// verify Echo API's hostname is specified as one of subject alternative names on the signing certificate | ||
var subjectAltNameExtention = cert.getExtension('subjectAltName'); | ||
var keyName = 'altNames'; | ||
var domainExist = function (domain) { return domain.value === CERT_CHAIN_DOMAIN; }; | ||
if (!subjectAltNameExtention[keyName].some(domainExist)) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, CERT_CHAIN_DOMAIN + " domain missing in Signature Certificate Chain."); | ||
const subjectAltNameExtension = cert.getExtension('subjectAltName'); | ||
const keyName = 'altNames'; | ||
const domainExist = (domain) => domain.value === CERT_CHAIN_DOMAIN; | ||
if (!subjectAltNameExtension[keyName].some(domainExist)) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, `${CERT_CHAIN_DOMAIN} domain missing in Signature Certificate Chain.`); | ||
} | ||
// Use the pki.verifyCertificateChain function from Node-forge to | ||
// validate that all certificates in the chain combine to create a chain of trust to a trusted root CA certificate | ||
// TODO: Implement certificate revocation check which is misssed in pki.verifyCertificateChain function | ||
var certChain = helper_1.generateCertificatesArray(pemCert); | ||
var caStore = helper_1.generateCAStore(require('ssl-root-cas/latest').create()); | ||
// TODO: Implement certificate revocation check which is missed in pki.verifyCertificateChain function | ||
const certChain = helper_1.generateCertificatesArray(pemCert); | ||
const caStore = helper_1.generateCAStore(require('ssl-root-cas/latest').create()); | ||
try { | ||
@@ -297,3 +240,3 @@ node_forge_1.pki.verifyCertificateChain(caStore, certChain); | ||
} | ||
}; | ||
} | ||
/** | ||
@@ -308,4 +251,4 @@ * Validate the request body hash with signature | ||
*/ | ||
SkillRequestSignatureVerifier.prototype._validateRequestBody = function (pemCert, signature, requestEnvelope) { | ||
var verifier = crypto.createVerify('RSA-SHA1'); | ||
_validateRequestBody(pemCert, signature, requestEnvelope) { | ||
const verifier = crypto.createVerify('RSA-SHA1'); | ||
verifier.update(requestEnvelope, CHARACTER_ENCODING); | ||
@@ -315,18 +258,17 @@ if (!verifier.verify(pemCert, signature, SIGNATURE_FORMAT)) { | ||
} | ||
}; | ||
return SkillRequestSignatureVerifier; | ||
}()); | ||
} | ||
} | ||
exports.SkillRequestSignatureVerifier = SkillRequestSignatureVerifier; | ||
/** | ||
* Implemention of Verifier which provides a utility method to handle | ||
* Implementation of Verifier which provides a utility method to handle | ||
* the request timestamp verification of the input request. | ||
*/ | ||
var TimestampVerifier = /** @class */ (function () { | ||
function TimestampVerifier(tolerance) { | ||
if (tolerance === void 0) { tolerance = DEFAULT_TIMESTAMP_TOLERANCE_IN_MILLIS; } | ||
if (tolerance > MAX_TIMESTAMP_TOLERANCE_IN_MILLIS) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, "Provided tolerance value " + tolerance + " exceeds the maximum allowed value " + MAX_TIMESTAMP_TOLERANCE_IN_MILLIS); | ||
class TimestampVerifier { | ||
constructor(tolerance = MAXIMUM_NORMAL_REQUEST_TOLERANCE_IN_MILLIS) { | ||
if (tolerance > MAXIMUM_NORMAL_REQUEST_TOLERANCE_IN_MILLIS) { | ||
console.warn(`ask-sdk-express-adapter TimestampVerifier: Provided tolerance value ${tolerance} exceeds the maximum allowed value ${MAXIMUM_NORMAL_REQUEST_TOLERANCE_IN_MILLIS}, Maximum value will be used instead.`); | ||
tolerance = MAXIMUM_NORMAL_REQUEST_TOLERANCE_IN_MILLIS; | ||
} | ||
if (tolerance < 0) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, "Negative tolerance values not supported"); | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, `Negative tolerance values not supported`); | ||
} | ||
@@ -345,22 +287,20 @@ this.toleranceInMillis = tolerance; | ||
*/ | ||
TimestampVerifier.prototype.verify = function (requestEnvelope) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var requestEnvelopeJson, requestTimeStamp, localNow; | ||
return __generator(this, function (_a) { | ||
requestEnvelopeJson = JSON.parse(requestEnvelope); | ||
if (!(requestEnvelopeJson.request && requestEnvelopeJson.request.timestamp)) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, 'Timestamp is not present in request'); | ||
} | ||
requestTimeStamp = new Date(requestEnvelopeJson.request.timestamp); | ||
localNow = new Date(); | ||
if (requestTimeStamp.getTime() + this.toleranceInMillis < localNow.getTime()) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, 'Timestamp verification failed'); | ||
} | ||
return [2 /*return*/]; | ||
}); | ||
}); | ||
}; | ||
return TimestampVerifier; | ||
}()); | ||
async verify(requestEnvelope) { | ||
const requestEnvelopeJson = JSON.parse(requestEnvelope); | ||
if (!(requestEnvelopeJson.request && requestEnvelopeJson.request.timestamp)) { | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, 'Timestamp is not present in request'); | ||
} | ||
const requestTimeStamp = new Date(requestEnvelopeJson.request.timestamp); | ||
const localNow = new Date(); | ||
if (requestTimeStamp.getTime() + this.toleranceInMillis < localNow.getTime()) { | ||
// If the request is a skill event, check whether the time delta exceed the maximum tolerance for skill event | ||
if (ALEXA_SKILL_EVENT_LIST.has(ask_sdk_core_1.getRequestType(requestEnvelopeJson)) | ||
&& (requestTimeStamp.getTime() + MAXIMUM_SKILL_EVENT_TOLERANCE_IN_MILLIS >= localNow.getTime())) { | ||
return; | ||
} | ||
throw ask_sdk_core_1.createAskSdkError(this.constructor.name, 'Timestamp verification failed'); | ||
} | ||
} | ||
} | ||
exports.TimestampVerifier = TimestampVerifier; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "ask-sdk-express-adapter", | ||
"version": "2.1.0", | ||
"version": "2.10.0", | ||
"description": "Express adapter package for Alexa Skills Kit SDK", | ||
@@ -8,3 +8,9 @@ "main": "dist/index.js", | ||
"scripts": { | ||
"gulp": "./node_modules/.bin/gulp" | ||
"build": "tsc && npm run lint", | ||
"compile": "tsc", | ||
"test": "cross-env TS_NODE_FILES=true mocha -r ts-node/register \"./tst/**/*.spec.ts\"", | ||
"lint": "eslint \"lib/**/*.{ts,tsx}\" \"tst/**/*.{ts,tsx}\"", | ||
"clean": "rm -rf ./dist", | ||
"reinstall": "rm -rf ./node_modules && npm install", | ||
"coverage": "nyc -x tst -e .ts -r html -r text-summary -t coverage/.nyc_output --cache npm test" | ||
}, | ||
@@ -27,3 +33,3 @@ "keywords": [ | ||
"body-parser": "^1.18.2", | ||
"node-forge": "^0.8.0", | ||
"node-forge": "^0.10.0", | ||
"ssl-root-cas": "^1.3.1" | ||
@@ -42,8 +48,11 @@ }, | ||
"@types/supertest": "^2.0.7", | ||
"ask-sdk-core": "^2.7.0", | ||
"ask-sdk-model": "^1.9.0", | ||
"@typescript-eslint/eslint-plugin": "^3.9.0", | ||
"@typescript-eslint/parser": "^3.9.0", | ||
"ask-sdk-core": "^2.10.0", | ||
"ask-sdk-model": "^1.29.0", | ||
"chai": "^4.1.2", | ||
"del": "^3.0.0", | ||
"cross-env": "^7.0.2", | ||
"eslint": "^7.6.0", | ||
"eslint-plugin-tsdoc": "^0.2.6", | ||
"express": "^4.16.4", | ||
"gulp": "^4.0.0", | ||
"mocha": "^5.0.5", | ||
@@ -55,3 +64,2 @@ "nock": "^9.2.3", | ||
"ts-node": "^6.0.1", | ||
"tslint": "^5.9.1", | ||
"typescript": "^3.5.3" | ||
@@ -61,3 +69,4 @@ }, | ||
"bugs": "https://github.com/alexa/alexa-skill-sdk-for-nodejs/issues", | ||
"homepage": "https://github.com/alexa/alexa-skill-sdk-for-nodejs#readme" | ||
"homepage": "https://github.com/alexa/alexa-skill-sdk-for-nodejs#readme", | ||
"gitHead": "c1d833ebae31ae822d30eef733afb6581d47453d" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
59224
23
653
+ Addednode-forge@0.10.0(transitive)
- Removednode-forge@0.8.5(transitive)
Updatednode-forge@^0.10.0