@openpass/openpass-js-sdk
Version:
OpenPass SSO JavaScript SDK
141 lines • 7.77 kB
JavaScript
;
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