UNPKG

@openpass/openpass-js-sdk

Version:
141 lines 7.77 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const config_1 = require("../config"); const constants_1 = require("./constants"); const pkce_1 = require("./utils/pkce"); const state_1 = require("./utils/state"); const url_1 = require("./url"); const codes_1 = require("./error/codes"); const sdkTelemetry_1 = require("./utils/sdkTelemetry"); const errors_1 = require("./error/errors"); const authCode_1 = require("./utils/authCode"); /* * Handle silent authentication for QuickAuth and InlineSignIn popup mode */ class SilentAuth { constructor(openPassOptions, openPassApiClient, popup) { this.popup = popup; this.openPassOptions = openPassOptions; this.openPassApiClient = openPassApiClient; } async handleSilentAuthWithPopupFallback(parentContainer, options, openPassApiBaseUrl, iframe, popupFallbackFlow) { var _a; let authResponse; try { authResponse = await this.silentAuth(parentContainer, options); } catch (error) { (_a = iframe === null || iframe === void 0 ? void 0 : iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage({ type: constants_1.SILENT_AUTH_OUTGOING_MESSAGE_TYPE.SilentAuthSignInCompletion, }, openPassApiBaseUrl); //if silent auth fails, then popup fails and auth starts again - need to refocus if (this.popup.refocusIfPopupExists()) { return; } //Inline sign in uses its own handlePopupSignIn method authResponse = popupFallbackFlow ? await popupFallbackFlow() : await this.popup.signInWithPopup(options); } return authResponse; } async silentAuth(parentContainer, options, openPassApiBaseUrl) { var _a; const authIframe = this.createHiddenSilentAuthIframe(parentContainer); const verifier = (0, pkce_1.generateCodeVerifier)(); const authSession = { clientState: options === null || options === void 0 ? void 0 : options.clientState, clientId: this.openPassOptions.clientId, redirectUrl: options === null || options === void 0 ? void 0 : options.redirectUrl, codeVerifier: verifier, codeChallenge: await (0, pkce_1.generateCodeChallenge)(verifier), codeChallengeMethod: constants_1.PARAM_CODE_CHALLENGE_METHOD_VALUE, state: (0, state_1.generateStateValue)(), responseMode: constants_1.PARAM_CODE_RESPONSE_MODE_MESSAGE, loginHint: options === null || options === void 0 ? void 0 : options.loginHint, disableLoginHintEditing: options === null || options === void 0 ? void 0 : options.disableLoginHintEditing, originatingUri: (_a = window === null || window === void 0 ? void 0 : window.location) === null || _a === void 0 ? void 0 : _a.href, useSilentAuth: true, allowUnverifiedEmail: options.allowUnverifiedEmail }; const source = options ? options.source : "Custom"; const loginUri = (0, url_1.buildAuthorizeUrl)((0, config_1.getOpenPassApiBaseUrl)(this.openPassOptions.baseUrl), config_1.config.SSO_AUTHORIZE_PATH, authSession, source, options === null || options === void 0 ? void 0 : options.customQueryParameters); authIframe.src = loginUri; return await this.waitForIframeResponse(authIframe, authSession, parentContainer, options); } async waitForIframeResponse(iframe, authSession, parentContainer, options, openPassApiBaseUrl) { const authCodeResponse = await this.listenForIframeResponse(iframe); if (!(0, authCode_1.isAuthCodeValid)(authCodeResponse, authSession) || !authCodeResponse.code) { const authError = new errors_1.AuthError(authCodeResponse.error ? authCodeResponse.error : codes_1.ERROR_CODE_INVALID_AUTH_CODE, authCodeResponse.errorDescription ? authCodeResponse.errorDescription : "Error, invalid authorization code response", authCodeResponse.errorUri ? authCodeResponse.errorUri : "", authSession.clientState); (0, sdkTelemetry_1.sendSdkTelemetryErrorEvent)("SilentAuth:WaitForIframeResponse", authError, this.openPassApiClient); parentContainer.removeChild(iframe); throw authError; } const openPassTokens = await this.openPassApiClient.exchangeAuthCodeForTokens(authCodeResponse.code, authSession); try { const { idToken, rawIdToken, rawAccessToken, refreshToken, expiresIn, tokenType } = openPassTokens; return { clientState: authSession.clientState, originatingUri: authSession.originatingUri, idToken: idToken, rawIdToken: rawIdToken, accessToken: rawAccessToken, rawAccessToken: rawAccessToken, refreshToken: refreshToken, expiresIn: expiresIn, tokenType: tokenType, }; } catch (e) { throw new errors_1.SdkError("Error retrieving tokens from iframe response: " + e); } finally { parentContainer.removeChild(iframe); } } createHiddenSilentAuthIframe(parentContainer) { const quickAuthIFrame = document.createElement("iframe"); quickAuthIFrame.style.setProperty("display", "none", "important"); if (!parentContainer) { throw new errors_1.SdkError("Parent container is not defined: unable to create hidden iframe"); } parentContainer.appendChild(quickAuthIFrame); return quickAuthIFrame; } async listenForIframeResponse(iframe) { let messageTimeout; let messageHandler; // We create a promise that will resolve with the authentication data once the iframe posts the message const iframePromise = new Promise((resolve, reject) => { messageHandler = (event) => { // Check that the message is from the authorization server if (!(0, url_1.matchesEventOrigin)(event.origin, (0, config_1.getOpenPassApiBaseUrl)(this.openPassOptions.baseUrl))) { return; } (0, sdkTelemetry_1.sendSdkTelemetryInfoEvent)("IframeAuth.listenForIframeResponse", `Iframe message received. Has Data: ${!!event.data}`, this.openPassApiClient); if (!event.data) { return; } const { data } = event; // Ensure the message source is from the expected iframe source if (!data.source || data.source !== constants_1.IFRAME_MESSAGE_SOURCE) { console.warn("Received message from unexpected source:", data.source); return; } resolve(data); }; // Listen for messages from the iframe window.addEventListener("message", messageHandler, false); // Set up timeout in case no message is received from the iframe messageTimeout = window.setTimeout(() => { reject(new errors_1.SdkError("No Response received from iframe")); (0, sdkTelemetry_1.sendSdkTelemetryInfoEvent)("IframeAuth.listenForIframeResponse", "No Response received from iframe after maximum timeout", this.openPassApiClient); }, config_1.config.IFRAME_RESPONSE_TIMEOUT_MS); }); // Return the promise and clean up return iframePromise.finally(() => { clearTimeout(messageTimeout); window.removeEventListener("message", messageHandler); }); } } exports.default = SilentAuth; //# sourceMappingURL=silentAuth.js.map