@lando/platformsh
Version:
A Lando plugin that provides a tight integration with Platform.sh.
331 lines (328 loc) • 14.8 kB
JavaScript
(function (factory) {
typeof define === 'function' && define.amd ? define(factory) :
factory();
}((function () { 'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var OAuth2PopupFlow = /** @class */ (function () {
function OAuth2PopupFlow(options) {
this.authorizationUri = options.authorizationUri;
this.clientId = options.clientId;
this.redirectUri = options.redirectUri;
this.scope = options.scope;
this.responseType = options.responseType || 'token';
this.accessTokenStorageKey = options.accessTokenStorageKey || 'token';
this.accessTokenResponseKey =
options.accessTokenResponseKey || 'access_token';
this.storage = options.storage || window.localStorage;
this.pollingTime = options.pollingTime || 200;
this.additionalAuthorizationParameters =
options.additionalAuthorizationParameters;
this.tokenValidator = options.tokenValidator;
this.beforePopup = options.beforePopup;
this.afterResponse = options.afterResponse;
this._eventListeners = {};
}
Object.defineProperty(OAuth2PopupFlow.prototype, "_rawToken", {
get: function () {
return this.storage.getItem(this.accessTokenStorageKey) || undefined;
},
set: function (value) {
if (value === null)
return;
if (value === undefined)
return;
this.storage.setItem(this.accessTokenStorageKey, value);
},
enumerable: true,
configurable: true
});
Object.defineProperty(OAuth2PopupFlow.prototype, "_rawTokenPayload", {
get: function () {
var rawToken = this._rawToken;
if (!rawToken)
return undefined;
var tokenSplit = rawToken.split('.');
var encodedPayload = tokenSplit[1];
if (!encodedPayload)
return undefined;
var decodedPayloadJson = window.atob(encodedPayload.replace('-', '+').replace('_', '/'));
var decodedPayload = OAuth2PopupFlow.jsonParseOrUndefined(decodedPayloadJson);
return decodedPayload;
},
enumerable: true,
configurable: true
});
/**
* A simple synchronous method that returns whether or not the user is logged in by checking
* whether or not their token is present and not expired.
*/
OAuth2PopupFlow.prototype.loggedIn = function () {
var decodedPayload = this._rawTokenPayload;
if (!decodedPayload)
return false;
if (this.tokenValidator) {
var token = this._rawToken;
if (!this.tokenValidator({ payload: decodedPayload, token: token }))
return false;
}
var exp = decodedPayload.exp;
if (!exp)
return false;
if (new Date().getTime() > exp * 1000)
return false;
return true;
};
/**
* Returns true only if there is a token in storage and that token is expired. Use this to method
* in conjunction with `loggedIn` to display a message like "you need to *re*login" vs "you need
* to login".
*/
OAuth2PopupFlow.prototype.tokenExpired = function () {
var decodedPayload = this._rawTokenPayload;
if (!decodedPayload)
return false;
var exp = decodedPayload.exp;
if (!exp)
return false;
if (new Date().getTime() <= exp * 1000)
return false;
return true;
};
/**
* Deletes the token from the given storage causing `loggedIn` to return false on its next call.
* Also dispatches `logout` event
*/
OAuth2PopupFlow.prototype.logout = function () {
this.storage.removeItem(this.accessTokenStorageKey);
this.dispatchEvent(new Event('logout'));
};
/**
* Call this method in a route of the `redirectUri`. This method takes the value of the hash at
* `window.location.hash` and attempts to grab the token from the URL.
*
* If the method was able to grab the token, it will return `'SUCCESS'` else it will return a
* different string.
*/
OAuth2PopupFlow.prototype.handleRedirect = function () {
var locationHref = window.location.href;
if (!locationHref.startsWith(this.redirectUri))
return 'REDIRECT_URI_MISMATCH';
var rawHash = window.location.hash;
if (!rawHash)
return 'FALSY_HASH';
var hashMatch = /#(.*)/.exec(rawHash);
// this case won't happen because the browser typically adds the `#` always
if (!hashMatch)
return 'NO_HASH_MATCH';
var hash = hashMatch[1];
var authorizationResponse = OAuth2PopupFlow.decodeUriToObject(hash);
if (this.afterResponse) {
this.afterResponse(authorizationResponse);
}
var rawToken = authorizationResponse[this.accessTokenResponseKey];
if (!rawToken)
return 'FALSY_TOKEN';
this._rawToken = rawToken;
window.location.hash = '';
return 'SUCCESS';
};
/**
* supported events are:
*
* 1. `logout`–fired when the `logout()` method is called and
* 2. `login`–fired during the `tryLoginPopup()` method is called and succeeds
*/
OAuth2PopupFlow.prototype.addEventListener = function (type, listener) {
var listeners = this._eventListeners[type] || [];
listeners.push(listener);
this._eventListeners[type] = listeners;
};
/**
* Use this to dispatch an event to the internal `EventTarget`
*/
OAuth2PopupFlow.prototype.dispatchEvent = function (event) {
var listeners = this._eventListeners[event.type] || [];
for (var _i = 0, listeners_1 = listeners; _i < listeners_1.length; _i++) {
var listener = listeners_1[_i];
var dispatch = typeof listener === 'function'
? listener
: typeof listener === 'object' &&
typeof listener.handleEvent === 'function'
? listener.handleEvent.bind(listener)
: function () { };
dispatch(event);
}
return true;
};
/**
* Removes the event listener in target's event listener list with the same type, callback, and options.
*/
OAuth2PopupFlow.prototype.removeEventListener = function (type, listener) {
var listeners = this._eventListeners[type] || [];
this._eventListeners[type] = listeners.filter(function (l) { return l !== listener; });
};
/**
* Tries to open a popup to login the user in. If the user is already `loggedIn()` it will
* immediately return `'ALREADY_LOGGED_IN'`. If the popup fails to open, it will immediately
* return `'POPUP_FAILED'` else it will wait for `loggedIn()` to be `true` and eventually
* return `'SUCCESS'`.
*
* Also dispatches `login` event
*/
OAuth2PopupFlow.prototype.tryLoginPopup = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var additionalParams, popup;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (this.loggedIn())
return [2 /*return*/, 'ALREADY_LOGGED_IN'];
if (!this.beforePopup) return [3 /*break*/, 2];
return [4 /*yield*/, Promise.resolve(this.beforePopup())];
case 1:
_a.sent();
_a.label = 2;
case 2:
additionalParams = typeof this.additionalAuthorizationParameters === 'function'
? this.additionalAuthorizationParameters()
: typeof this.additionalAuthorizationParameters === 'object'
? this.additionalAuthorizationParameters
: {};
popup = window.open(this.authorizationUri + "?" + OAuth2PopupFlow.encodeObjectToUri(tslib_1.__assign({ client_id: this.clientId, response_type: this.responseType, redirect_uri: this.redirectUri, scope: this.scope }, additionalParams)));
if (!popup)
return [2 /*return*/, 'POPUP_FAILED'];
return [4 /*yield*/, this.authenticated()];
case 3:
_a.sent();
popup.close();
this.dispatchEvent(new Event('login'));
return [2 /*return*/, 'SUCCESS'];
}
});
});
};
/**
* A promise that does not resolve until `loggedIn()` is true. This uses the `pollingTime`
* to wait until checking if `loggedIn()` is `true`.
*/
OAuth2PopupFlow.prototype.authenticated = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!!this.loggedIn()) return [3 /*break*/, 2];
return [4 /*yield*/, OAuth2PopupFlow.time(this.pollingTime)];
case 1:
_a.sent();
return [3 /*break*/, 0];
case 2: return [2 /*return*/];
}
});
});
};
/**
* If the user is `loggedIn()`, the token will be returned immediate, else it will open a popup
* and wait until the user is `loggedIn()` (i.e. a new token has been added).
*/
OAuth2PopupFlow.prototype.token = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var token;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.authenticated()];
case 1:
_a.sent();
token = this._rawToken;
if (!token)
throw new Error('Token was falsy after being authenticated.');
return [2 /*return*/, token];
}
});
});
};
/**
* If the user is `loggedIn()`, the token payload will be returned immediate, else it will open a
* popup and wait until the user is `loggedIn()` (i.e. a new token has been added).
*/
OAuth2PopupFlow.prototype.tokenPayload = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var payload;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.authenticated()];
case 1:
_a.sent();
payload = this._rawTokenPayload;
if (!payload)
throw new Error('Token payload was falsy after being authenticated.');
return [2 /*return*/, payload];
}
});
});
};
/**
* wraps `JSON.parse` and return `undefined` if the parsing failed
*/
OAuth2PopupFlow.jsonParseOrUndefined = function (json) {
try {
return JSON.parse(json);
}
catch (e) {
return undefined;
}
};
/**
* wraps `setTimeout` in a `Promise` that resolves to `'TIMER'`
*/
OAuth2PopupFlow.time = function (milliseconds) {
return new Promise(function (resolve) {
return window.setTimeout(function () { return resolve('TIMER'); }, milliseconds);
});
};
/**
* wraps `decodeURIComponent` and returns the original string if it cannot be decoded
*/
OAuth2PopupFlow.decodeUri = function (str) {
try {
return decodeURIComponent(str);
}
catch (_a) {
return str;
}
};
/**
* Encodes an object of strings to a URL
*
* `{one: 'two', buckle: 'shoes or something'}` ==> `one=two&buckle=shoes%20or%20something`
*/
OAuth2PopupFlow.encodeObjectToUri = function (obj) {
return Object.keys(obj)
.map(function (key) { return ({ key: key, value: obj[key] }); })
.map(function (_a) {
var key = _a.key, value = _a.value;
return encodeURIComponent(key) + "=" + encodeURIComponent(value);
})
.join('&');
};
/**
* Decodes a URL string to an object of string
*
* `one=two&buckle=shoes%20or%20something` ==> `{one: 'two', buckle: 'shoes or something'}`
*/
OAuth2PopupFlow.decodeUriToObject = function (str) {
var _this = this;
return str.split('&').reduce(function (decoded, keyValuePair) {
var _a = keyValuePair.split('='), keyEncoded = _a[0], valueEncoded = _a[1];
var key = _this.decodeUri(keyEncoded);
var value = _this.decodeUri(valueEncoded);
decoded[key] = value;
return decoded;
}, {});
};
return OAuth2PopupFlow;
}());
exports.OAuth2PopupFlow = OAuth2PopupFlow;
exports.default = OAuth2PopupFlow;
})));
//# sourceMappingURL=index.js.map