@openpass/openpass-js-sdk
Version:
OpenPass SSO JavaScript SDK
152 lines • 10.2 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 errors_1 = require("./error/errors");
const sdkTelemetry_1 = require("./utils/sdkTelemetry");
/**
* Class which handles the redirect authorization flow.
* To realize this flow this class implements the Authorization Code Flow with Proof Key for Code Exchange (PKCE).
* The redirect flow involves redirecting to an authorization service, and then handle the login response send via the redirect.
*/
class RedirectAuth {
constructor(openPassOptions, signInStateRepository, openPassAuthClient) {
this.openPassOptions = openPassOptions;
this.openPassApiClient = openPassAuthClient;
this.signInStateRepository = signInStateRepository;
}
/**
* Initiate the login flow by redirecting to the authorization server
* @param options the login options
*/
async signIn(options) {
var _a, _b;
if (!options.redirectUrl) {
throw new errors_1.SdkError("Error redirectUrl is invalid. Please use a valid redirectUrl");
}
try {
const verifier = (0, pkce_1.generateCodeVerifier)();
const authSession = {
clientState: options.clientState,
clientId: this.openPassOptions.clientId,
redirectUrl: options.redirectUrl,
codeVerifier: verifier,
codeChallenge: await (0, pkce_1.generateCodeChallenge)(verifier),
codeChallengeMethod: constants_1.PARAM_CODE_CHALLENGE_METHOD_VALUE,
state: (0, state_1.generateStateValue)(),
loginHint: options.loginHint,
disableLoginHintEditing: options.disableLoginHintEditing,
originatingUri: (_a = window === null || window === void 0 ? void 0 : window.location) === null || _a === void 0 ? void 0 : _a.href,
allowUnverifiedEmail: (_b = options.allowUnverifiedEmail) !== null && _b !== void 0 ? _b : false,
};
this.signInStateRepository.add(authSession);
const loginUri = (0, url_1.buildAuthorizeUrl)((0, config_1.getOpenPassApiBaseUrl)(this.openPassOptions.baseUrl), config_1.config.SSO_AUTHORIZE_PATH, authSession, options.source, options.customQueryParameters);
window.location.href = loginUri;
}
catch (error) {
console.error("Unable to start a sign-in session", error);
(0, sdkTelemetry_1.sendSdkTelemetryErrorEvent)("RedirectAuth.signIn", error, this.openPassApiClient);
throw error;
}
}
/**
* Determines if a redirect authorization session is in progress, and the current browser url meets the conditions to consider it as a valid redirection from the
* authorization server for the current login session.
*/
isAuthenticationRedirect() {
try {
// Parse the current URL query parameters
const currentPageAuthenticationParameters = (0, url_1.parseAuthRedirectUrlParams)(window.location.search);
// First check if the current URL has state and code, or error parameters.
if (!(0, url_1.urlHasStateAndCodeOrErrorParameters)(currentPageAuthenticationParameters)) {
return false;
}
// Get the current auth session, this contains the redirect url to check against.
const authSession = this.signInStateRepository.get();
// If there is no auth session at this point, then we are unable to complete the redirect flow.
if (!authSession) {
console.warn("No authentication session found when checking authentication redirect, ensure a call to signIn via redirect has been made first");
(0, sdkTelemetry_1.sendSdkTelemetryInfoEvent)("RedirectAuth.isAuthenticationRedirect", "No auth session found", this.openPassApiClient);
return false;
}
// If there is not redirect url in the auth session, then we are unable to complete the redirect flow.
if (!authSession.redirectUrl) {
console.warn("No redirect url found in the auth session, ensure a call to signIn via redirect has been made first");
(0, sdkTelemetry_1.sendSdkTelemetryInfoEvent)("RedirectAuth.isAuthenticationRedirect", "Auth session found, but redirect url was not set", this.openPassApiClient);
return false;
}
// Check if the current URL is a valid redirect URL for the current auth session, including checking the redirect url is the same as the current page url (just origin and path).
const redirectUrlMatchesCurrentUrlValue = (0, url_1.redirectUrlMatchesCurrentUrl)(window.location.href, authSession.redirectUrl);
(0, sdkTelemetry_1.sendSdkTelemetryInfoEvent)("RedirectAuth.isAuthenticationRedirect", `validAuthenticationRedirectUrl is ${redirectUrlMatchesCurrentUrlValue}`, this.openPassApiClient);
return redirectUrlMatchesCurrentUrlValue;
}
catch (error) {
console.error("Unable to check if the current URL is a valid redirect URL for the current auth session", error);
(0, sdkTelemetry_1.sendSdkTelemetryErrorEvent)("RedirectAuth.isAuthenticationRedirect", error, this.openPassApiClient);
throw error;
}
}
/**
* Handles a login redirect and attempts to complete the authorization flow.
*/
async handleAuthenticationRedirect() {
try {
// Parse the current URL query parameters
const currentPageAuthenticationParameters = (0, url_1.parseAuthRedirectUrlParams)(window.location.search);
// First check if the current URL has state and code, or error parameters.
if (!(0, url_1.urlHasStateAndCodeOrErrorParameters)(currentPageAuthenticationParameters)) {
throw new errors_1.AuthError(codes_1.ERROR_CODE_INVALID_REDIRECT, "The current URL does not contain expected state and code or error parameters, cannot handle redirect", "");
}
// Get the current auth session which contains all the required security parameters to complete the sign-in.
const authSession = this.signInStateRepository.get();
// If there is no auth session at this point, then we are unable to complete the redirect flow.
if (!authSession) {
console.error("No auth session found, cannot handle redirect. Ensure a call to signIn via redirect has been made first");
throw new errors_1.AuthError(codes_1.ERROR_CODE_INVALID_REDIRECT, "No auth session found, cannot handle redirect. Ensure a call to signIn via redirect has been made first", "");
}
// If there is not redirect url in the auth session, then we are unable to complete the redirect flow.
if (!authSession.redirectUrl) {
console.error("No redirect url found in the auth session, ensure a call to signIn via redirect has been made first");
throw new errors_1.AuthError(codes_1.ERROR_CODE_INVALID_REDIRECT, "No redirect url found in the auth session, ensure a call to signIn via redirect has been made first", "");
}
// Check if the current URL is a valid redirect URL for the current auth session, including checking the redirect url is the same as the current page url (just origin and path).
if (!(0, url_1.redirectUrlMatchesCurrentUrl)(window.location.href, authSession.redirectUrl)) {
console.error("The current URL does not match the expected redirect URL, cannot handle redirect");
throw new errors_1.AuthError(codes_1.ERROR_CODE_INVALID_REDIRECT, "The current URL does not match the expected redirect URL, cannot handle redirect", "");
}
// Remove the auth session from the repository, we will no longer need it whether the rest of this method passes or fails.
this.signInStateRepository.remove();
// If there is a error parameter on the URL, then we have a error response from the authorization server.
if (currentPageAuthenticationParameters.error) {
throw new errors_1.AuthError(currentPageAuthenticationParameters.error, currentPageAuthenticationParameters.errorDescription ? currentPageAuthenticationParameters.errorDescription : "", currentPageAuthenticationParameters.errorUri ? currentPageAuthenticationParameters.errorUri : "", authSession.clientState);
}
if (currentPageAuthenticationParameters.state !== authSession.state) {
console.error("The state parameter in the redirect URL does not match the state in the auth session, cannot complete the redirect flow");
throw new errors_1.AuthError(codes_1.ERROR_CODE_INVALID_AUTH_SESSION, "The state parameter in the redirect URL does not match the state in the auth session", "", authSession.clientState);
}
const tokens = await this.openPassApiClient.exchangeAuthCodeForTokens(currentPageAuthenticationParameters.code, authSession);
const { idToken, rawIdToken, accessToken, refreshToken, tokenType, expiresIn } = tokens;
return {
clientState: authSession.clientState,
originatingUri: authSession.originatingUri,
idToken: idToken,
rawIdToken: rawIdToken,
accessToken: accessToken,
rawAccessToken: accessToken,
refreshToken: refreshToken,
tokenType: tokenType,
expiresIn: expiresIn,
};
}
catch (error) {
(0, sdkTelemetry_1.sendSdkTelemetryErrorEvent)("RedirectAuth.handleAuthenticationRedirect", error, this.openPassApiClient);
throw error;
}
}
}
exports.default = RedirectAuth;
//# sourceMappingURL=redirect.js.map