import { KeycloakAdapter, KeycloakInstance, KeycloakLoginOptions, KeycloakLogoutOptions, KeycloakRegisterOptions } from 'keycloak-js'; import { createLogger } from './app/utils/utils'; const logger = createLogger('keycloak'); export type OptionsOrProvider = Partial | (() => Partial); /** * Create and immediately resolve a KeycloakPromise */ const createPromise = () => new Promise(resolve => resolve()); /** * Resolve OptionsOrProvider: if it's an function, execute it, otherwise return it. */ const resolveOptions = (opt: OptionsOrProvider): Partial => (typeof opt === 'function' ? opt() : opt); /** * * Update options with the overrides given as OptionsOrProvider * * @param options * @param overrides * @returns */ const updateOptions = (options: T, overrides: OptionsOrProvider): T => Object.assign(options ?? {}, resolveOptions(overrides)); /** * Keycloak adapter that supports options customization. * * All options can either be given as lazily evaluated provider functions (that will be evaluated * right before navigating) or eagerly evaluated objects. These options will have precedence * over options passed by keycloak-js. * * Cf. https://www.keycloak.org/docs/15.0/securing_apps/#custom-adapters * * Actual implementation copied more or less verbatim from * https://github.com/keycloak/keycloak-js-bower/blob/10.0.2/dist/keycloak.js#L1136 * * @param kc Function that returns a Keycloak instance * @param loginOptions login options * @param logoutOptions logout options * @param registerOptions register options * @returns KeycloakAdapter */ export function customKeycloakAdapter( kc: () => KeycloakInstance, loginOptions: OptionsOrProvider = {}, logoutOptions: OptionsOrProvider = {}, registerOptions: OptionsOrProvider = {}, ): KeycloakAdapter { return { login: (options?: KeycloakLoginOptions): Promise => { updateOptions(options, loginOptions); logger.info('Executing login. Options: ', options); window.location.replace(kc().createLoginUrl(options)); return createPromise(); }, logout: (options?: KeycloakLogoutOptions): Promise => { updateOptions(options, logoutOptions); logger.info('Executing logout. Options: ', options); window.location.replace(kc().createLogoutUrl(options)); return createPromise(); }, register: (options?: KeycloakRegisterOptions): Promise => { updateOptions(options, registerOptions); logger.info('Executing register. Options: ', options); window.location.replace(kc().createRegisterUrl(options)); return createPromise(); }, accountManagement: (): Promise => { const accountUrl = kc().createAccountUrl(); logger.info('Executing account management'); if (typeof accountUrl !== 'undefined') { window.location.href = accountUrl; } else { throw new Error('Not supported by the OIDC server'); } return createPromise(); }, redirectUri: (options: { redirectUri: string }) => { if (options?.redirectUri) { return options.redirectUri; } if (kc().redirectUri) { return kc().redirectUri; } return window.location.href; }, }; }