UNPKG

@brionmario-experimental/asgardeo-auth-spa

Version:

Asgardeo Auth SPA SDK to be used in Single-Page Applications.

539 lines 29.2 kB
/** * Copyright (c) 2022-2024, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with 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. */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { AsgardeoAuthException, AuthenticationUtils } from "@asgardeo/auth-js"; import { ACCESS_TOKEN_INVALID, CHECK_SESSION_SIGNED_IN, CHECK_SESSION_SIGNED_OUT, CUSTOM_GRANT_CONFIG, ERROR, ERROR_DESCRIPTION, PROMPT_NONE_IFRAME, REFRESH_ACCESS_TOKEN_ERR0R, RP_IFRAME, Storage } from "../constants"; import { SPAUtils } from "../utils"; export class AuthenticationHelper { constructor(authClient, spaHelper) { this._authenticationClient = authClient; this._dataLayer = this._authenticationClient.getDataLayer(); this._spaHelper = spaHelper; this._instanceID = this._authenticationClient.getInstanceID(); this._isTokenRefreshing = false; } enableHttpHandler(httpClient) { (httpClient === null || httpClient === void 0 ? void 0 : httpClient.enableHandler) && httpClient.enableHandler(); } disableHttpHandler(httpClient) { (httpClient === null || httpClient === void 0 ? void 0 : httpClient.disableHandler) && httpClient.disableHandler(); } initializeSessionManger(config, oidcEndpoints, getSessionState, getAuthzURL, sessionManagementHelper) { var _a, _b, _c; sessionManagementHelper.initialize(config.clientID, (_a = oidcEndpoints.checkSessionIframe) !== null && _a !== void 0 ? _a : "", getSessionState, (_b = config.checkSessionInterval) !== null && _b !== void 0 ? _b : 3, (_c = config.sessionRefreshInterval) !== null && _c !== void 0 ? _c : 300, config.signInRedirectURL, getAuthzURL); } requestCustomGrant(config, enableRetrievingSignOutURLFromSession) { var _a, _b, _c; return __awaiter(this, void 0, void 0, function* () { let useDefaultEndpoint = true; let matches = false; // If the config does not contains a token endpoint, default token endpoint will be used. if (config === null || config === void 0 ? void 0 : config.tokenEndpoint) { useDefaultEndpoint = false; for (const baseUrl of [ ...((_b = (_a = (yield this._dataLayer.getConfigData())) === null || _a === void 0 ? void 0 : _a.resourceServerURLs) !== null && _b !== void 0 ? _b : []), config.baseUrl ]) { if (baseUrl && ((_c = config.tokenEndpoint) === null || _c === void 0 ? void 0 : _c.startsWith(baseUrl))) { matches = true; break; } } } if (config.shouldReplayAfterRefresh) { this._dataLayer.setTemporaryDataParameter(CUSTOM_GRANT_CONFIG, JSON.stringify(config)); } if (useDefaultEndpoint || matches) { return this._authenticationClient .requestCustomGrant(config) .then((response) => __awaiter(this, void 0, void 0, function* () { if (enableRetrievingSignOutURLFromSession && typeof enableRetrievingSignOutURLFromSession === "function") { enableRetrievingSignOutURLFromSession(config); } if (config.returnsSession) { this._spaHelper.refreshAccessTokenAutomatically(this); return this._authenticationClient.getBasicUserInfo(); } else { return response; } })) .catch((error) => { return Promise.reject(error); }); } else { return Promise.reject(new AsgardeoAuthException("SPA-MAIN_THREAD_CLIENT-RCG-IV01", "Request to the provided endpoint is prohibited.", "Requests can only be sent to resource servers specified by the `resourceServerURLs`" + " attribute while initializing the SDK. The specified token endpoint in this request " + "cannot be found among the `resourceServerURLs`")); } }); } getCustomGrantConfigData() { return __awaiter(this, void 0, void 0, function* () { const configString = yield this._dataLayer.getTemporaryDataParameter(CUSTOM_GRANT_CONFIG); if (configString) { return JSON.parse(configString); } else { return null; } }); } refreshAccessToken(enableRetrievingSignOutURLFromSession) { return __awaiter(this, void 0, void 0, function* () { try { yield this._authenticationClient.refreshAccessToken(); const customGrantConfig = yield this.getCustomGrantConfigData(); if (customGrantConfig) { yield this.requestCustomGrant(customGrantConfig, enableRetrievingSignOutURLFromSession); } this._spaHelper.refreshAccessTokenAutomatically(this); return this._authenticationClient.getBasicUserInfo(); } catch (error) { const refreshTokenError = { type: REFRESH_ACCESS_TOKEN_ERR0R }; window.postMessage(refreshTokenError); return Promise.reject(error); } }); } retryFailedRequests(failedRequest) { return __awaiter(this, void 0, void 0, function* () { const httpClient = failedRequest.httpClient; const requestConfig = failedRequest.requestConfig; const isHttpHandlerEnabled = failedRequest.isHttpHandlerEnabled; const httpErrorCallback = failedRequest.httpErrorCallback; const httpFinishCallback = failedRequest.httpFinishCallback; // Wait until the token is refreshed. yield SPAUtils.until(() => !this._isTokenRefreshing); try { const httpResponse = yield httpClient.request(requestConfig); return Promise.resolve(httpResponse); } catch (error) { if (isHttpHandlerEnabled) { if (typeof httpErrorCallback === "function") { yield httpErrorCallback(error); } if (typeof httpFinishCallback === "function") { httpFinishCallback(); } } return Promise.reject(error); } }); } httpRequest(httpClient, requestConfig, isHttpHandlerEnabled, httpErrorCallback, httpFinishCallback, enableRetrievingSignOutURLFromSession) { var _a, _b; return __awaiter(this, void 0, void 0, function* () { let matches = false; const config = yield this._dataLayer.getConfigData(); for (const baseUrl of [ ...((_a = (yield (config === null || config === void 0 ? void 0 : config.resourceServerURLs))) !== null && _a !== void 0 ? _a : []), config.baseUrl ]) { if (baseUrl && ((_b = requestConfig === null || requestConfig === void 0 ? void 0 : requestConfig.url) === null || _b === void 0 ? void 0 : _b.startsWith(baseUrl))) { matches = true; break; } } if (matches) { return httpClient .request(requestConfig) .then((response) => { return Promise.resolve(response); }) .catch((error) => __awaiter(this, void 0, void 0, function* () { var _c, _d, _e; if (((_c = error === null || error === void 0 ? void 0 : error.response) === null || _c === void 0 ? void 0 : _c.status) === 401 || !(error === null || error === void 0 ? void 0 : error.response)) { if (this._isTokenRefreshing) { return this.retryFailedRequests({ enableRetrievingSignOutURLFromSession, httpClient, httpErrorCallback, httpFinishCallback, isHttpHandlerEnabled, requestConfig }); } this._isTokenRefreshing = true; // Try to refresh the token let refreshAccessTokenResponse; try { refreshAccessTokenResponse = yield this.refreshAccessToken(enableRetrievingSignOutURLFromSession); this._isTokenRefreshing = false; } catch (refreshError) { this._isTokenRefreshing = false; if (isHttpHandlerEnabled) { if (typeof httpErrorCallback === "function") { yield httpErrorCallback(Object.assign(Object.assign({}, error), { code: ACCESS_TOKEN_INVALID })); } if (typeof httpFinishCallback === "function") { httpFinishCallback(); } } throw new AsgardeoAuthException("SPA-AUTH_HELPER-HR-SE01", (_d = refreshError === null || refreshError === void 0 ? void 0 : refreshError.name) !== null && _d !== void 0 ? _d : "Refresh token request failed.", (_e = refreshError === null || refreshError === void 0 ? void 0 : refreshError.message) !== null && _e !== void 0 ? _e : "An error occurred while trying to refresh the " + "access token following a 401 response from the server."); } // Retry the request after refreshing the token if (refreshAccessTokenResponse) { try { const httpResponse = yield httpClient.request(requestConfig); return Promise.resolve(httpResponse); } catch (error) { if (isHttpHandlerEnabled) { if (typeof httpErrorCallback === "function") { yield httpErrorCallback(error); } if (typeof httpFinishCallback === "function") { httpFinishCallback(); } } return Promise.reject(error); } } } if (isHttpHandlerEnabled) { if (typeof httpErrorCallback === "function") { yield httpErrorCallback(error); } if (typeof httpFinishCallback === "function") { httpFinishCallback(); } } return Promise.reject(error); })); } else { return Promise.reject(new AsgardeoAuthException("SPA-AUTH_HELPER-HR-IV02", "Request to the provided endpoint is prohibited.", "Requests can only be sent to resource servers specified by the `resourceServerURLs`" + " attribute while initializing the SDK. The specified endpoint in this request " + "cannot be found among the `resourceServerURLs`")); } }); } httpRequestAll(requestConfigs, httpClient, isHttpHandlerEnabled, httpErrorCallback, httpFinishCallback) { var _a, _b, _c; return __awaiter(this, void 0, void 0, function* () { let matches = true; const config = yield this._dataLayer.getConfigData(); for (const requestConfig of requestConfigs) { let urlMatches = false; for (const baseUrl of [ ...((_b = (_a = (yield config)) === null || _a === void 0 ? void 0 : _a.resourceServerURLs) !== null && _b !== void 0 ? _b : []), config.baseUrl ]) { if (baseUrl && ((_c = requestConfig.url) === null || _c === void 0 ? void 0 : _c.startsWith(baseUrl))) { urlMatches = true; break; } } if (!urlMatches) { matches = false; break; } } const requests = []; if (matches) { requestConfigs.forEach((request) => { requests.push(httpClient.request(request)); }); return ((httpClient === null || httpClient === void 0 ? void 0 : httpClient.all) && httpClient .all(requests) .then((responses) => { return Promise.resolve(responses); }) .catch((error) => __awaiter(this, void 0, void 0, function* () { var _d, _e, _f; if (((_d = error === null || error === void 0 ? void 0 : error.response) === null || _d === void 0 ? void 0 : _d.status) === 401 || !(error === null || error === void 0 ? void 0 : error.response)) { let refreshTokenResponse; try { refreshTokenResponse = yield this._authenticationClient.refreshAccessToken(); } catch (refreshError) { if (isHttpHandlerEnabled) { if (typeof httpErrorCallback === "function") { yield httpErrorCallback(Object.assign(Object.assign({}, error), { code: ACCESS_TOKEN_INVALID })); } if (typeof httpFinishCallback === "function") { httpFinishCallback(); } } throw new AsgardeoAuthException("SPA-AUTH_HELPER-HRA-SE01", (_e = refreshError === null || refreshError === void 0 ? void 0 : refreshError.name) !== null && _e !== void 0 ? _e : "Refresh token request failed.", (_f = refreshError === null || refreshError === void 0 ? void 0 : refreshError.message) !== null && _f !== void 0 ? _f : "An error occurred while trying to refresh the " + "access token following a 401 response from the server."); } if (refreshTokenResponse) { return (httpClient.all && httpClient .all(requests) .then((response) => { return Promise.resolve(response); }) .catch((error) => __awaiter(this, void 0, void 0, function* () { if (isHttpHandlerEnabled) { if (typeof httpErrorCallback === "function") { yield httpErrorCallback(error); } if (typeof httpFinishCallback === "function") { httpFinishCallback(); } } return Promise.reject(error); }))); } } if (isHttpHandlerEnabled) { if (typeof httpErrorCallback === "function") { yield httpErrorCallback(error); } if (typeof httpFinishCallback === "function") { httpFinishCallback(); } } return Promise.reject(error); }))); } else { throw new AsgardeoAuthException("SPA-AUTH_HELPER-HRA-IV02", "Request to the provided endpoint is prohibited.", "Requests can only be sent to resource servers specified by the `resourceServerURLs`" + " attribute while initializing the SDK. The specified endpoint in this request " + "cannot be found among the `resourceServerURLs`"); } }); } requestAccessToken(authorizationCode, sessionState, checkSession, pkce, state, tokenRequestConfig) { return __awaiter(this, void 0, void 0, function* () { const config = yield this._dataLayer.getConfigData(); if (config.storage === Storage.BrowserMemory && config.enablePKCE && sessionState) { const pkce = SPAUtils.getPKCE(AuthenticationUtils.extractPKCEKeyFromStateParam(sessionState)); yield this._authenticationClient.setPKCECode(AuthenticationUtils.extractPKCEKeyFromStateParam(sessionState), pkce); } else if (config.storage === Storage.WebWorker && pkce) { yield this._authenticationClient.setPKCECode(pkce, state !== null && state !== void 0 ? state : ""); } if (authorizationCode) { return this._authenticationClient .requestAccessToken(authorizationCode, sessionState !== null && sessionState !== void 0 ? sessionState : "", state !== null && state !== void 0 ? state : "", undefined, tokenRequestConfig) .then(() => __awaiter(this, void 0, void 0, function* () { // Disable this temporarily /* if (config.storage === Storage.BrowserMemory) { SPAUtils.setSignOutURL(await _authenticationClient.getSignOutURL()); } */ if (config.storage !== Storage.WebWorker) { SPAUtils.setSignOutURL(yield this._authenticationClient.getSignOutURL(), config.clientID, this._instanceID); if (this._spaHelper) { this._spaHelper.clearRefreshTokenTimeout(); this._spaHelper.refreshAccessTokenAutomatically(this); } // Enable OIDC Sessions Management only if it is set to true in the config. if (checkSession && typeof checkSession === "function" && config.enableOIDCSessionManagement) { checkSession(); } } else { if (this._spaHelper) { this._spaHelper.refreshAccessTokenAutomatically(this); } } return this._authenticationClient.getBasicUserInfo(); })) .catch((error) => { return Promise.reject(error); }); } return Promise.reject(new AsgardeoAuthException("SPA-AUTH_HELPER-RAT1-NF01", "No authorization code.", "No authorization code was found.")); }); } trySignInSilently(constructSilentSignInUrl, requestAccessToken, sessionManagementHelper, additionalParams, tokenRequestConfig) { var _a; return __awaiter(this, void 0, void 0, function* () { // This block is executed by the iFrame when the server redirects with the authorization code. if (SPAUtils.isInitializedSilentSignIn()) { yield sessionManagementHelper.receivePromptNoneResponse(); return Promise.resolve({ allowedScopes: "", displayName: "", email: "", sessionState: "", sub: "", tenantDomain: "", username: "" }); } // This gets executed in the main thread and sends the prompt none request. const rpIFrame = document.getElementById(RP_IFRAME); const promptNoneIFrame = (_a = rpIFrame === null || rpIFrame === void 0 ? void 0 : rpIFrame.contentDocument) === null || _a === void 0 ? void 0 : _a.getElementById(PROMPT_NONE_IFRAME); try { const url = yield constructSilentSignInUrl(additionalParams); promptNoneIFrame.src = url; } catch (error) { return Promise.reject(error); } return new Promise((resolve, reject) => { const timer = setTimeout(() => { resolve(false); }, 10000); const listenToPromptNoneIFrame = (e) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d; const data = e.data; if ((data === null || data === void 0 ? void 0 : data.type) == CHECK_SESSION_SIGNED_OUT) { window.removeEventListener("message", listenToPromptNoneIFrame); clearTimeout(timer); resolve(false); } if ((data === null || data === void 0 ? void 0 : data.type) == CHECK_SESSION_SIGNED_IN && ((_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.code)) { requestAccessToken((_b = data === null || data === void 0 ? void 0 : data.data) === null || _b === void 0 ? void 0 : _b.code, (_c = data === null || data === void 0 ? void 0 : data.data) === null || _c === void 0 ? void 0 : _c.sessionState, (_d = data === null || data === void 0 ? void 0 : data.data) === null || _d === void 0 ? void 0 : _d.state, tokenRequestConfig) .then((response) => { window.removeEventListener("message", listenToPromptNoneIFrame); resolve(response); }) .catch((error) => { window.removeEventListener("message", listenToPromptNoneIFrame); reject(error); }) .finally(() => { clearTimeout(timer); }); } }); window.addEventListener("message", listenToPromptNoneIFrame); }); }); } handleSignIn(shouldStopAuthn, checkSession, tryRetrievingUserInfo) { return __awaiter(this, void 0, void 0, function* () { const config = yield this._dataLayer.getConfigData(); if (yield shouldStopAuthn()) { return Promise.resolve({ allowedScopes: "", displayName: "", email: "", sessionState: "", sub: "", tenantDomain: "", username: "" }); } if (config.storage !== Storage.WebWorker) { if (yield this._authenticationClient.isAuthenticated()) { this._spaHelper.clearRefreshTokenTimeout(); this._spaHelper.refreshAccessTokenAutomatically(this); // Enable OIDC Sessions Management only if it is set to true in the config. if (config.enableOIDCSessionManagement) { checkSession(); } return Promise.resolve(yield this._authenticationClient.getBasicUserInfo()); } } const error = new URL(window.location.href).searchParams.get(ERROR); const errorDescription = new URL(window.location.href).searchParams.get(ERROR_DESCRIPTION); if (error) { const url = new URL(window.location.href); url.searchParams.delete(ERROR); url.searchParams.delete(ERROR_DESCRIPTION); history.pushState(null, document.title, url.toString()); throw new AsgardeoAuthException("SPA-AUTH_HELPER-SI-SE01", error, errorDescription !== null && errorDescription !== void 0 ? errorDescription : ""); } if (config.storage === Storage.WebWorker && tryRetrievingUserInfo) { const basicUserInfo = yield tryRetrievingUserInfo(); if (basicUserInfo) { return basicUserInfo; } } }); } attachTokenToRequestConfig(request) { return __awaiter(this, void 0, void 0, function* () { const requestConfig = Object.assign({ attachToken: true }, request); if (requestConfig.attachToken) { if (requestConfig.shouldAttachIDPAccessToken) { request.headers = Object.assign(Object.assign({}, request.headers), { Authorization: `Bearer ${yield this.getIDPAccessToken()}` }); } else { request.headers = Object.assign(Object.assign({}, request.headers), { Authorization: `Bearer ${yield this.getAccessToken()}` }); } } }); } getBasicUserInfo() { return __awaiter(this, void 0, void 0, function* () { return this._authenticationClient.getBasicUserInfo(); }); } getDecodedIDToken() { return __awaiter(this, void 0, void 0, function* () { return this._authenticationClient.getDecodedIDToken(); }); } getDecodedIDPIDToken() { return __awaiter(this, void 0, void 0, function* () { return this._authenticationClient.getDecodedIDToken(); }); } getCryptoHelper() { return __awaiter(this, void 0, void 0, function* () { return this._authenticationClient.getCryptoHelper(); }); } getIDToken() { return __awaiter(this, void 0, void 0, function* () { return this._authenticationClient.getIDToken(); }); } getOIDCServiceEndpoints() { return __awaiter(this, void 0, void 0, function* () { return this._authenticationClient.getOIDCServiceEndpoints(); }); } getAccessToken() { return __awaiter(this, void 0, void 0, function* () { return this._authenticationClient.getAccessToken(); }); } getIDPAccessToken() { var _a; return __awaiter(this, void 0, void 0, function* () { return (_a = (yield this._dataLayer.getSessionData())) === null || _a === void 0 ? void 0 : _a.access_token; }); } getDataLayer() { return this._dataLayer; } isAuthenticated() { return __awaiter(this, void 0, void 0, function* () { return this._authenticationClient.isAuthenticated(); }); } } //# sourceMappingURL=authentication-helper.js.map