@ionic/cli
Version:
A tool for creating and developing Ionic Framework mobile apps.
110 lines (109 loc) • 5.22 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.OAuth2Flow = void 0;
const tslib_1 = require("tslib");
const utils_fs_1 = require("@ionic/utils-fs");
const utils_network_1 = require("@ionic/utils-network");
const crypto = tslib_1.__importStar(require("crypto"));
const http = tslib_1.__importStar(require("http"));
const path = tslib_1.__importStar(require("path"));
const qs = tslib_1.__importStar(require("querystring"));
const constants_1 = require("../../constants");
const errors_1 = require("../errors");
const http_1 = require("../http");
const open_1 = require("../open");
const REDIRECT_PORT = 8123;
const REDIRECT_HOST = 'localhost';
class OAuth2Flow {
constructor({ redirectHost = REDIRECT_HOST, redirectPort = REDIRECT_PORT, accessTokenRequestContentType = "application/json" /* ContentType.JSON */ }, e) {
this.e = e;
this.oauthConfig = this.getAuthConfig();
this.redirectHost = redirectHost;
this.redirectPort = redirectPort;
this.accessTokenRequestContentType = accessTokenRequestContentType;
}
get redirectUrl() {
return `http://${this.redirectHost}:${this.redirectPort}`;
}
async run() {
const verifier = this.generateVerifier();
const challenge = this.generateChallenge(verifier);
const authorizationParams = this.generateAuthorizationParameters(challenge);
const authorizationUrl = `${this.oauthConfig.authorizationUrl}?${qs.stringify(authorizationParams)}`;
await (0, open_1.openUrl)(authorizationUrl);
const { code, state } = await this.getAuthorizationCode();
const token = await this.exchangeAuthForAccessToken(code, verifier);
token.state = state;
return token;
}
async exchangeRefreshToken(refreshToken) {
const params = this.generateRefreshTokenParameters(refreshToken);
const { req } = await this.e.client.make('POST', this.oauthConfig.tokenUrl, this.accessTokenRequestContentType);
const res = await req.send(params);
// check the response status code first here
if (!res.ok) {
throw new errors_1.FatalException('API request to refresh token was not successful.\n' +
'Please try to login again.\n' +
(0, http_1.formatResponseError)(req, res.status));
}
if (!this.checkValidExchangeTokenRes(res)) {
throw new errors_1.FatalException('API request was successful, but the refreshed token was unrecognized.\n' +
'Please try to login again.\n');
}
return res.body;
}
async getSuccessHtml() {
const p = path.resolve(constants_1.ASSETS_DIRECTORY, 'oauth', 'success', 'index.html');
const contents = await (0, utils_fs_1.readFile)(p, { encoding: 'utf8' });
return contents;
}
async getAuthorizationCode() {
if (!(await (0, utils_network_1.isPortAvailable)(this.redirectPort))) {
throw new Error(`Cannot start local server. Port ${this.redirectPort} is in use.`);
}
const successHtml = await this.getSuccessHtml();
return new Promise((resolve, reject) => {
const server = http.createServer((req, res) => {
if (req.url) {
const params = qs.parse(req.url.substring(req.url.indexOf('?') + 1));
if (params.code) {
res.writeHead(200, { 'Content-Type': "text/html" /* ContentType.HTML */ });
res.end(successHtml);
req.socket.destroy();
server.close();
const authResult = {
code: Array.isArray(params.code) ? params.code[0] : params.code,
state: params.state ? (Array.isArray(params.state) ? decodeURI(params.state[0]) : decodeURI(params.state)) : '',
};
resolve(authResult);
}
// TODO, timeout, error handling
}
});
server.listen(this.redirectPort, this.redirectHost);
});
}
async exchangeAuthForAccessToken(authorizationCode, verifier) {
const params = this.generateTokenParameters(authorizationCode, verifier);
const { req } = await this.e.client.make('POST', this.oauthConfig.tokenUrl, this.accessTokenRequestContentType);
const res = await req.send(params);
if (!this.checkValidExchangeTokenRes(res)) {
throw new errors_1.FatalException('API request was successful, but the response format was unrecognized.\n' +
(0, http_1.formatResponseError)(req, res.status));
}
return res.body;
}
generateVerifier() {
return this.base64URLEncode(crypto.randomBytes(32));
}
generateChallenge(verifier) {
return this.base64URLEncode(crypto.createHash('sha256').update(verifier).digest());
}
base64URLEncode(buffer) {
return buffer.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
}
exports.OAuth2Flow = OAuth2Flow;
;