ask-cli-x
Version:
Alexa Skills Kit (ASK) Command Line Interfaces
136 lines (135 loc) • 5.72 kB
JavaScript
;
const R = require("ramda");
const { URL } = require("url");
const queryString = require("querystring");
const addSeconds = require("date-fns/addSeconds");
const isAfter = require("date-fns/isAfter");
const parseISO = require("date-fns/parseISO");
const httpClient = require("../../clients/http-client");
const CONSTANTS = require("../../utils/constants");
const DynamicConfig = require("../../utils/dynamic-config");
const stringUtils = require("../../utils/string-utils");
const jsonView = require("../../view/json-view");
const CliError = require("../../exceptions/cli-error");
module.exports = class LWAAuthCodeClient {
constructor(config) {
this.config = this._handleDefaultLwaAuthCodeConfiguration(config);
}
/**
* @param {String} authCode | used for fetching accessTokens
* @param {Function} callback (err, accessToken)
* @returns accessToken | Used for request validation in skill development process.
*/
getAccessTokenUsingAuthCode(authCode, callback) {
const url = new URL(this.config.tokenPath, this.config.tokenHost);
const body = {
grant_type: "authorization_code",
redirect_uri: this.config.redirectUri,
client_id: this.config.clientId,
client_secret: this.config.clientConfirmation,
code: authCode,
};
const options = {
url: `${url}`,
method: "POST",
body,
json: !!body,
};
httpClient.request(options, "GET_ACCESS_TOKEN", this.config.doDebug, (err, response) => {
if (err) {
return callback(err);
}
const tokenBody = R.clone(response.body);
if (tokenBody.error) {
return callback(new CliError(tokenBody.error));
}
tokenBody.expires_at = this._getExpiresAt(tokenBody.expires_in).toISOString();
callback(null, tokenBody);
});
}
/**
* @param {Object} token | accessToken of the profile being used currently.
* @param {Function} callback (err, token)
* @returns accessToken | a new access token.
*/
refreshToken(token, callback) {
const url = new URL(this.config.tokenPath, this.config.tokenHost);
const body = {
grant_type: "refresh_token",
refresh_token: token.refresh_token,
client_id: this.config.clientId,
client_secret: this.config.clientConfirmation,
};
const options = {
url: `${url}`,
method: "POST",
body,
json: !!body,
};
httpClient.request(options, "GET_ACCESS_TOKEN_USING_REFRESH_TOKEN", this.config.doDebug, (err, response) => {
if (err) {
return callback(err);
}
const responseErr = R.view(R.lensPath(["body", "error"]), response);
if (stringUtils.isNonBlankString(responseErr)) {
return callback(`Refresh LWA tokens failed, please run "ask configure" to manually update your tokens. Error: ${responseErr}.`);
}
const expiresIn = R.view(R.lensPath(["body", "expires_in"]), response);
if (!expiresIn) {
return callback(`Received invalid response body from LWA without "expires_in":\n${jsonView.toString(response.body)}`);
}
const tokenBody = R.clone(response.body);
if (tokenBody.error) {
return callback(new CliError(tokenBody.error));
}
tokenBody.expires_at = this._getExpiresAt(expiresIn).toISOString();
callback(null, tokenBody);
});
}
/**
* @param {Object} token
* @returns boolean | checks validity of a given token
*/
isValidToken(token) {
return !isAfter(new Date(), parseISO(token.expires_at));
}
/**
* @returns {String} authorization code URL
*/
generateAuthorizeUrl() {
const queryParams = {
response_type: "code",
client_id: this.config.clientId,
state: this.config.state,
};
if (stringUtils.isNonBlankString(this.config.scope)) {
queryParams.scope = this.config.scope;
}
if (stringUtils.isNonBlankString(this.config.redirectUri)) {
queryParams.redirect_uri = this.config.redirectUri;
}
const baseUrl = new URL(this.config.authorizePath, this.config.authorizeHost);
return `${baseUrl}?${queryString.stringify(queryParams)}`;
}
/**
* @param {Object} authConfig
* @returns {Object} config | sets default values if some of the values are missing.
* @private
*/
_handleDefaultLwaAuthCodeConfiguration(authConfig) {
const { doDebug, redirectUri } = authConfig;
const authorizePath = CONSTANTS.LWA.DEFAULT_AUTHORIZE_PATH;
const tokenPath = CONSTANTS.LWA.DEFAULT_TOKEN_PATH;
// Overwrite LWA options from Environmental Variable
const state = authConfig.state || Date.now();
const scope = authConfig.scope || CONSTANTS.LWA.DEFAULT_SCOPES;
const clientId = authConfig.clientId || DynamicConfig.lwaClientId;
const clientConfirmation = authConfig.clientConfirmation || DynamicConfig.lwaClientConfirmation;
const authorizeHost = DynamicConfig.lwaAuthorizationHost;
const tokenHost = DynamicConfig.lwaTokenHost;
return { clientId, clientConfirmation, authorizeHost, tokenHost, authorizePath, tokenPath, scope, state, redirectUri, doDebug };
}
_getExpiresAt(expiresIn) {
return addSeconds(new Date(), Number.parseInt(expiresIn, 10));
}
};