UNPKG

@brionmario-experimental/asgardeo-auth-spa

Version:

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

234 lines (199 loc) 7.99 kB
/** * Copyright (c) 2020-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. */ import { AsgardeoAuthClient, SIGN_OUT_SUCCESS_PARAM, SIGN_OUT_URL } from "@asgardeo/auth-js"; import { SignOutError } from ".."; import { ERROR, ERROR_DESCRIPTION, INITIALIZED_SILENT_SIGN_IN, PROMPT_NONE_REQUEST_SENT, SILENT_SIGN_IN_STATE, STATE_QUERY } from "../constants"; export class SPAUtils { // eslint-disable-next-line @typescript-eslint/no-empty-function private constructor() {} public static removeAuthorizationCode(): void { const url = location.href; history.pushState({}, document.title, url.replace(/\?code=.*$/, "")); } public static getPKCE(pkceKey: string): string { return sessionStorage.getItem(pkceKey) ?? ""; } public static setPKCE(pkceKey: string, pkce: string): void { sessionStorage.setItem(pkceKey, pkce); } public static setSignOutURL(url: string, clientID: string, instanceID: number): void { sessionStorage.setItem(`${SIGN_OUT_URL}-instance_${ instanceID }-${ clientID }`, url); } public static getSignOutURL(clientID: string, instanceID: number): string { return sessionStorage.getItem(`${SIGN_OUT_URL}-instance_${ instanceID }-${ clientID }`) ?? ""; } public static removePKCE(pkceKey: string): void { sessionStorage.removeItem(pkceKey); } /** * This method is used to discontinue the execution of the `signIn` method if `callOnlyOnRedirect` is true and * the method is not called on being redirected from the authorization server. * * This method can be used to allow the `signIn` method to be called only * on being redirected from the authorization server. * * @param callOnlyOnRedirect {boolean} - True if the method should only be called on redirect. * @param authorizationCode {string} - Authorization code. * * @returns {boolean} - True if the method should be called. */ public static canContinueSignIn(callOnlyOnRedirect: boolean, authorizationCode?: string): boolean { if ( callOnlyOnRedirect && !SPAUtils.hasErrorInURL() && !SPAUtils.hasAuthSearchParamsInURL() && !authorizationCode ) { return false; } return true; } /** * Specifies if `trySilentSignIn` has been called. * * @returns {boolean} True if the `trySilentSignIn` method has been called once. */ public static isInitializedSilentSignIn(): boolean { return SPAUtils.isSilentStatePresentInURL(); } /** * Specifies if the `signIn` method has been called. * * @returns {boolean} True if the `signIn` has been called. */ public static wasSignInCalled(): boolean { if (SPAUtils.hasErrorInURL() || SPAUtils.hasAuthSearchParamsInURL()) { if (!this.isSilentStatePresentInURL()) { return true; } } return false; } public static wasSilentSignInCalled(): boolean { const silentSignIsInitialized = sessionStorage.getItem(INITIALIZED_SILENT_SIGN_IN); const isSilentSignInInitialized = silentSignIsInitialized ? JSON.parse(silentSignIsInitialized) : null; return Boolean(isSilentSignInInitialized); } public static async isSignOutSuccessful(): Promise<boolean> { if (AsgardeoAuthClient.isSignOutSuccessful(window.location.href)) { const newUrl = window.location.href.split("?")[0]; history.pushState({}, document.title, newUrl); await AsgardeoAuthClient.clearUserSessionData(); return true; } return false; } public static didSignOutFail(): boolean | SignOutError { if (AsgardeoAuthClient.didSignOutFail(window.location.href)) { const url: URL = new URL(window.location.href); const error: string | null = url.searchParams.get(ERROR); const description: string | null = url.searchParams.get(ERROR_DESCRIPTION); const newUrl = window.location.href.split("?")[0]; history.pushState({}, document.title, newUrl); return { description: description ?? "", error: error ?? "" }; } return false; } /** * Checks if the URL the user agent is redirected to after an authorization request has the state parameter. * * @returns {boolean} True if there is a session-check state or a silent sign-in state. */ public static isSilentStatePresentInURL(): boolean { const state = new URL(window.location.href).searchParams.get("state"); return state?.includes(SILENT_SIGN_IN_STATE) ?? false; } /** * Util function to test if `code` and `session_state` are available in the URL as search params. * @since 0.2.0 * * @param params - Search params. * @return {boolean} */ public static hasAuthSearchParamsInURL(params: string = window.location.search): boolean { const AUTH_CODE_REGEXP: RegExp = /[?&]code=[^&]+/; return AUTH_CODE_REGEXP.test(params); } /** * Util function to check if the URL contains an error. * * @param url - URL to be checked. * * @returns {boolean} - True if the URL contains an error. */ public static hasErrorInURL(url: string = window.location.href): boolean { const urlObject: URL = new URL(url); return ( !!urlObject.searchParams.get(ERROR) && urlObject.searchParams.get(STATE_QUERY) !== SIGN_OUT_SUCCESS_PARAM ); } /** * Checks if a prompt none can be sent by checking if a request has already been sent. * * @since 0.2.3 * * @returns {boolean} - True if a prompt none request has not been sent. */ public static canSendPromptNoneRequest(): boolean { const promptNoneRequestSentRaw = sessionStorage.getItem(PROMPT_NONE_REQUEST_SENT); const promptNoneRequestSent = promptNoneRequestSentRaw ? JSON.parse(promptNoneRequestSentRaw) : null; return !promptNoneRequestSent; } /** * Sets the status of prompt none request. * * @since 0.2.3 * * @param canSend {boolean} - True if a prompt none request can be sent. */ public static setPromptNoneRequestSent(canSend: boolean): void { sessionStorage.setItem(PROMPT_NONE_REQUEST_SENT, JSON.stringify(canSend)); } /** * Waits for a specified amount of time to give the user agent enough time to redirect. * * @param time {number} - Time in seconds. */ public static async waitTillPageRedirect(time?: number): Promise<void> { const timeToWait = time ?? 3000; await new Promise((resolve) => setTimeout(resolve, timeToWait * 1000)); } /** * Waits for a condition before executing the rest of the code in non-blocking manner. * * @param condition {() => boolean} - Condition to be checked. * @param timeout {number} - Time in miliseconds. */ public static until = ( condition: () => boolean, timeout: number = 500 ) => { const poll = (done) => (condition() ? done() : setTimeout(() => poll(done), timeout)); return new Promise(poll); }; }