@okta/okta-auth-js
Version:
The Okta Auth SDK
222 lines (218 loc) • 9.31 kB
JavaScript
/*!
* Copyright (c) 2015-present, Okta, Inc. and/or its affiliates. All rights reserved.
* The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
*
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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.
*/
import { __classPrivateFieldSet, __classPrivateFieldGet } from '../_virtual/_tslib.js';
import AuthSdkError from '../errors/AuthSdkError.js';
import '../errors/WWWAuthError.js';
import { getConsole } from '../util/console.js';
import { PromiseQueue } from '../util/PromiseQueue.js';
import '../http/request.js';
import 'tiny-emitter';
import 'js-cookie';
import '../oidc/types/Token.js';
import { EVENT_ADDED, EVENT_REMOVED } from '../oidc/types/TokenManager.js';
var _CancelablePromise_state, _CancelablePromise_promise, _CancelablePromise_cancelHandlers, _CancelablePromise_rejector;
const INITIAL_AUTH_STATE = null;
const DEFAULT_PENDING = {
updateAuthStatePromise: null,
canceledTimes: 0
};
const EVENT_AUTH_STATE_CHANGE = 'authStateChange';
const MAX_PROMISE_CANCEL_TIMES = 10;
const isSameAuthState = (prevState, state) => {
if (!prevState) {
return false;
}
return prevState.isAuthenticated === state.isAuthenticated
&& JSON.stringify(prevState.idToken) === JSON.stringify(state.idToken)
&& JSON.stringify(prevState.accessToken) === JSON.stringify(state.accessToken)
&& prevState.error === state.error;
};
class CancelablePromise {
constructor(executor) {
_CancelablePromise_state.set(this, 'PENDING');
_CancelablePromise_promise.set(this, void 0);
_CancelablePromise_cancelHandlers.set(this, []);
_CancelablePromise_rejector.set(this, () => { });
__classPrivateFieldSet(this, _CancelablePromise_promise, new Promise((resolve, reject) => {
__classPrivateFieldSet(this, _CancelablePromise_rejector, reject, "f");
const onResolve = (result) => {
if (__classPrivateFieldGet(this, _CancelablePromise_state, "f") !== 'CANCELED') {
resolve(result);
__classPrivateFieldSet(this, _CancelablePromise_state, 'SETTLED', "f");
}
};
const onReject = (error) => {
if (__classPrivateFieldGet(this, _CancelablePromise_state, "f") !== 'CANCELED') {
reject(error);
__classPrivateFieldSet(this, _CancelablePromise_state, 'SETTLED', "f");
}
};
const onCancel = handler => {
__classPrivateFieldGet(this, _CancelablePromise_cancelHandlers, "f").push(handler);
};
executor(onResolve, onReject, onCancel);
}), "f");
}
then(...args) {
return __classPrivateFieldGet(this, _CancelablePromise_promise, "f").then(...args);
}
catch(...args) {
return __classPrivateFieldGet(this, _CancelablePromise_promise, "f").catch(...args);
}
finally(...args) {
return __classPrivateFieldGet(this, _CancelablePromise_promise, "f").finally(...args);
}
cancel() {
if (__classPrivateFieldGet(this, _CancelablePromise_state, "f") !== 'PENDING') {
return;
}
__classPrivateFieldSet(this, _CancelablePromise_state, 'CANCELED', "f");
if (__classPrivateFieldGet(this, _CancelablePromise_cancelHandlers, "f").length > 0) {
try {
for (const handler of __classPrivateFieldGet(this, _CancelablePromise_cancelHandlers, "f")) {
handler();
}
}
catch (error) {
__classPrivateFieldGet(this, _CancelablePromise_rejector, "f").call(this, error);
return;
}
}
}
get isCanceled() {
return __classPrivateFieldGet(this, _CancelablePromise_state, "f") === 'CANCELED';
}
}
_CancelablePromise_state = new WeakMap(), _CancelablePromise_promise = new WeakMap(), _CancelablePromise_cancelHandlers = new WeakMap(), _CancelablePromise_rejector = new WeakMap();
class AuthStateManager {
constructor(sdk) {
if (!sdk.emitter) {
throw new AuthSdkError('Emitter should be initialized before AuthStateManager');
}
this._sdk = sdk;
this._pending = Object.assign({}, DEFAULT_PENDING);
this._authState = INITIAL_AUTH_STATE;
this._logOptions = {};
this._prevAuthState = null;
this._transformQueue = new PromiseQueue({
quiet: true
});
sdk.tokenManager.on(EVENT_ADDED, (key, token) => {
this._setLogOptions({ event: EVENT_ADDED, key, token });
this.updateAuthState();
});
sdk.tokenManager.on(EVENT_REMOVED, (key, token) => {
this._setLogOptions({ event: EVENT_REMOVED, key, token });
this.updateAuthState();
});
}
_setLogOptions(options) {
this._logOptions = options;
}
getAuthState() {
return this._authState;
}
getPreviousAuthState() {
return this._prevAuthState;
}
async updateAuthState() {
const { transformAuthState, devMode } = this._sdk.options;
const log = (status) => {
const { event, key, token } = this._logOptions;
getConsole().group(`OKTA-AUTH-JS:updateAuthState: Event:${event} Status:${status}`);
getConsole().log(key, token);
getConsole().log('Current authState', this._authState);
getConsole().groupEnd();
this._logOptions = {};
};
const emitAuthStateChange = (authState) => {
if (isSameAuthState(this._authState, authState)) {
devMode && log('unchanged');
return;
}
this._prevAuthState = this._authState;
this._authState = authState;
this._sdk.emitter.emit(EVENT_AUTH_STATE_CHANGE, Object.assign({}, authState));
devMode && log('emitted');
};
const finalPromise = (origPromise) => {
return this._pending.updateAuthStatePromise.then(() => {
const curPromise = this._pending.updateAuthStatePromise;
if (curPromise && curPromise !== origPromise) {
return finalPromise(curPromise);
}
return this.getAuthState();
});
};
if (this._pending.updateAuthStatePromise) {
if (this._pending.canceledTimes >= MAX_PROMISE_CANCEL_TIMES) {
devMode && log('terminated');
return finalPromise(this._pending.updateAuthStatePromise);
}
else {
this._pending.updateAuthStatePromise.cancel();
}
}
const cancelablePromise = new CancelablePromise((resolve, _, onCancel) => {
onCancel(() => {
this._pending.updateAuthStatePromise = null;
this._pending.canceledTimes = this._pending.canceledTimes + 1;
devMode && log('canceled');
});
const emitAndResolve = (authState) => {
if (cancelablePromise.isCanceled) {
resolve(undefined);
return;
}
emitAuthStateChange(authState);
resolve(undefined);
this._pending = Object.assign({}, DEFAULT_PENDING);
};
this._sdk.isAuthenticated()
.then(() => {
if (cancelablePromise.isCanceled) {
resolve(undefined);
return;
}
const { accessToken, idToken, refreshToken } = this._sdk.tokenManager.getTokensSync();
const authState = {
accessToken,
idToken,
refreshToken,
isAuthenticated: !!(accessToken && idToken)
};
const promise = transformAuthState
? this._transformQueue.push(transformAuthState, null, this._sdk, authState)
: Promise.resolve(authState);
promise
.then(authState => emitAndResolve(authState))
.catch(error => emitAndResolve({
accessToken,
idToken,
refreshToken,
isAuthenticated: false,
error
}));
});
});
this._pending.updateAuthStatePromise = cancelablePromise;
return finalPromise(cancelablePromise);
}
subscribe(handler) {
this._sdk.emitter.on(EVENT_AUTH_STATE_CHANGE, handler);
}
unsubscribe(handler) {
this._sdk.emitter.off(EVENT_AUTH_STATE_CHANGE, handler);
}
}
export { AuthStateManager, INITIAL_AUTH_STATE };
//# sourceMappingURL=AuthStateManager.js.map