aboutsummaryrefslogtreecommitdiffstats
path: root/htdocs/amazon.js
diff options
context:
space:
mode:
Diffstat (limited to 'htdocs/amazon.js')
-rw-r--r--htdocs/amazon.js323
1 files changed, 323 insertions, 0 deletions
diff --git a/htdocs/amazon.js b/htdocs/amazon.js
new file mode 100644
index 0000000..05704f1
--- /dev/null
+++ b/htdocs/amazon.js
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+AMAZON = function() {
+
+var afb;
+var alexaWs;
+
+var base = {
+ base: "api",
+ token: "HELLO",
+};
+
+// GUID generator for generating device serial number.
+function guid() {
+ function s4() {
+ return Math.floor((1 + Math.random()) * 0x10000)
+ .toString(16)
+ .substring(1);
+ }
+ return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
+}
+
+/*********************************************/
+/**** ****/
+/**** AMAZON_cbl ****/
+/**** ****/
+/*********************************************/
+var AMAZON_Cbl;
+{
+ const amazonHostUrl = "https://api.amazon.com";
+ const amazonCodePairUrl = amazonHostUrl + "/auth/O2/create/codepair";
+ const amazonTokenUrl = amazonHostUrl + "/auth/O2/token";
+ const deviceSerialNumber = guid();
+ var clientID = localStorage.getItem("client_id");
+ var productID = localStorage.getItem("product_id");
+ var alexaVAAddress = localStorage.getItem("alexa_va_address");
+ var alexaVAConnected = false;
+ var alexaVAAddressInput;
+ var clientIDInput;
+ var productIDInput;
+
+ AMAZON_Cbl = function() {
+ // Alexa VA Address
+ const alexaVAAddressInput = document.getElementById('alexa-va-address');
+ alexaVAAddress = alexaVAAddressInput.value;
+ connectToAlexaVA(alexaVAAddress);
+
+ alexaVAAddressInput.addEventListener("change",(evt) => {
+ var newAlexaVAAddress = alexaVAAddressInput.value;
+ if (alexaVAAddress != newAlexaVAAddress) {
+ connectToAlexaVA(newAlexaVAAddress);
+ localStorage.setItem("alexa_va_address", newAlexaVAAddress);
+ }
+ });
+
+ // Client ID
+ const clientIDInput = document.getElementById('client-id');
+ clientIDInput.addEventListener("change",(evt) => {
+ var newClientID = clientIDInput.value;
+ if (clientID != newClientID) {
+ clientID = newClientID;
+ localStorage.setItem("client_id", newClientID);
+ }
+ });
+
+ // Product ID
+ const productIDInput = document.getElementById('product-id');
+ productIDInput.addEventListener("change",(evt) => {
+ var newProductID = productIDInput.value;
+ if (productID != newProductID) {
+ productID = newProductID;
+ localStorage.setItem("product_id", newProductID);
+ }
+ });
+ }
+
+ function connectToAlexaVA(address) {
+ base.host = address;
+ afb = new AFB(base, "secret");
+
+ function onopen() {
+ console.log("Connected to Alexa VA");
+ alexaVAConnected = true;
+ }
+
+ function onabort() {
+ console.log("Alexa VA connection aborted.");
+ alexaVAConnected = false;
+ }
+
+ alexaWs = new afb.ws(onopen, onabort);
+ }
+
+ function sendRequest(httpReq, paramsJson, url, responseCb) {
+ httpReq.onreadystatechange = responseCb;
+ var paramsQueryString = Object.keys(paramsJson).map(key => key + '=' + paramsJson[key]).join('&');
+ httpReq.open("POST", url, true);
+ httpReq.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ httpReq.send(paramsQueryString);
+ }
+
+ //**********************************************
+ // Generic function to call VA binder
+ //***********************************************
+ function callVABinder(voiceAgent, verb, query) {
+ console.log(voiceAgent.api, verb, query);
+
+ // ws.call return a Promise
+ return alexaWs.call(voiceAgent.api + '/' + verb, query)
+ .then(function (res) {
+ log.reply(res);
+ count++;
+ return res;
+ })
+ .catch(function (err) {
+ log.reply(err);
+ count++;
+ throw err;
+ });
+ };
+
+ function updateAccessToken(voiceAgent, tokenResponseJson) {
+ if (alexaVAAddress === undefined || alexaVAAddress === null) {
+ console.log("No Alexa VA. So not updating the access token.");
+ return;
+ }
+
+ // store the access and refresh tokens.
+ if (typeof(Storage) !== "undefined") {
+ localStorage.setItem("access_token", tokenResponseJson["access_token"]);
+ localStorage.setItem("refresh_token", tokenResponseJson["refresh_token"]);
+ }
+
+ // Set the auth token
+ if (alexaVAConnected) {
+ // Set new token
+ const query = {"token": tokenResponseJson["access_token"]};
+ callVABinder(voiceAgent, 'setAuthToken', query);
+ }
+
+ // Refresh the token as soon as it expires.
+ setTimeout(refreshToken, tokenResponseJson["expires_in"] * 1000);
+ }
+
+ function refreshToken(voiceAgent) {
+ if (voiceAgent == "undefined") {
+ console.log("Error: VoiceAgent undefined");
+ return;
+ }
+
+ var refreshToken = localStorage.getItem("refresh_token");
+ if (refreshToken == null) {
+ console.log("Error: No refresh token");
+ return;
+ }
+
+ var paramsJson = {
+ "grant_type":"refresh_token",
+ "refresh_token":refreshToken,
+ "client_id":clientID,
+ };
+
+ const tokenRefreshReq = new XMLHttpRequest();
+ sendRequest(tokenRefreshReq, paramsJson, amazonTokenUrl, function() {
+ if (tokenRefreshReq.readyState == 4) {
+ if (tokenRefreshReq.status == 200) {
+ console.log("Got access token " + tokenRefreshReq.responseText);
+ var tokenResponseJson = JSON.parse(tokenRefreshReq.responseText);
+ updateAccessToken(voiceAgent, tokenResponseJson);
+ } else {
+ console.log("Failed to refresh access token: " + tokenRefreshReq.responseText);
+ }
+ }
+ });
+ }
+
+ function displayUserCodeAndURI(authResponseJson) {
+ const modal = document.getElementById('login-with-amazon');
+ const cblStatusDiv = document.createElement("div");
+ const cblStatusMsg = document.createElement("p");
+ const blank = "_blank";
+
+ var cblPage = authResponseJson["verification_uri"] + "?cbl-code=" + authResponseJson["user_code"]
+ var msg = "To use Alexa,you must sign in to Amazon.<br> Go to " +
+ "<a href=" + cblPage + " target="+ blank+ " >" +
+ cblPage + "</a>";
+ cblStatusMsg.innerHTML = msg;
+ cblStatusDiv.appendChild(cblStatusMsg);
+ modal.appendChild(cblStatusDiv);
+
+ const closeBtn = document.createElement("button");
+ closeBtn.addEventListener('click', (evt) => {
+ modal.close();
+ });
+ closeBtn.style = "margin: 10px";
+ closeBtn.innerHTML = "Close";
+ modal.appendChild(closeBtn);
+ }
+
+ function hideLoginUI() {
+ const loginDiv = document.getElementById('login-area');
+ loginDiv.style.display = "none";
+ }
+
+ function login(voiceAgent) {
+ if (voiceAgent == undefined) {
+ console.log("Error: VoiceAgent undefined");
+ return;
+ }
+
+ const modal = document.getElementById('login-with-amazon');
+ const submitBtn = document.getElementById('submit-btn');
+ const cancelBtn = document.getElementById('cancel-btn');
+ submitBtn.addEventListener('click', (evt) => {
+ console.log("Alexa Destination address set to: " + alexaVAAddress);
+ startLoginProcess(voiceAgent);
+ });
+
+ cancelBtn.addEventListener('click', (evt) => {
+ modal.close();
+ });
+
+ const alexaVAAddressInput = document.getElementById('alexa-va-address');
+ alexaVAAddressInput.value = alexaVAAddress;
+
+ const clientIDInput = document.getElementById('client-id');
+ clientIDInput.value = clientID;
+
+ const productIDInput = document.getElementById('product-id');
+ productIDInput.value = productID;
+
+ modal.showModal();
+ }
+
+ function startLoginProcess(voiceAgent) {
+ if (clientID == null || productID == null || alexaVAAddress == null) {
+ console.log("Required information missing to start login process.");
+ return;
+ }
+
+ var reqJson = {
+ "response_type": "device_code",
+ "client_id": clientID,
+ "scope":"alexa:all",
+ "scope_data": JSON.stringify({
+ "alexa:all": {
+ "productID":productID,
+ "productInstanceAttributes" : {
+ "deviceSerialNumber": deviceSerialNumber
+ }
+ }
+ })
+ };
+
+ const authReq = new XMLHttpRequest();
+ var tokenUrl = amazonTokenUrl;
+ sendRequest(authReq, reqJson, amazonCodePairUrl, function() {
+ if (authReq.readyState == 4) {
+ if (authReq.status == 200) {
+ var authResponse = JSON.parse(authReq.responseText);
+ console.log("Got auth codepair " + authReq.responseText);
+ hideLoginUI();
+ displayUserCodeAndURI(authResponse);
+ var maxTokenReqCnt = authResponse["expires_in"] / authResponse["interval"];
+ var tokenReqFuncId = setTimeout(function tokenReqFunc() {
+ var reqJson = {
+ "grant_type":"device_code",
+ "device_code":authResponse["device_code"],
+ "user_code":authResponse["user_code"]
+ };
+ const tokenReq = new XMLHttpRequest();
+ sendRequest(tokenReq, reqJson, tokenUrl, function() {
+ if (tokenReq.readyState == 4) {
+ if (tokenReq.status == 200) {
+ console.log("Got access token " + tokenReq.responseText);
+ var tokenResponseJson = JSON.parse(tokenReq.responseText);
+ updateAccessToken(voiceAgent, tokenResponseJson);
+ }
+ else {
+ maxTokenReqCnt--;
+ console.log("Retrying... " + tokenReq.responseText);
+ setTimeout(tokenReqFunc, authResponse["interval"] * 1000);
+ }
+ }
+ });
+ }, authResponse["interval"] * 1000);
+ // Cancel if max token request attempts are reached.
+ if (maxTokenReqCnt == 0) {
+ console.log("Reached max token request attemps limit.");
+ }
+ } else {
+ console.log(authReq.status);
+ }
+ }
+ });
+ }
+
+ AMAZON_Cbl.prototype = {
+ login: login,
+ refreshToken: refreshToken,
+ };
+}
+/*********************************************/
+/**** ****/
+/**** ****/
+/**** ****/
+/*********************************************/
+return {
+ cbl: AMAZON_Cbl
+};
+}; \ No newline at end of file