import { z } from 'zod'; import AuthenticationStrategy, { zAuthenticationStrategy } from '../enums/AuthenticationStrategy'; import { CONTEXT_WEB_UI_URL, API_URL, } from '../env'; import { AuthenticationResponse, authenticationResponse } from '../services/UserService'; /** ****************************************************************************** * Helpers ******************************************************************************* */ export const FRAME_DATA_SOURCE = 'use-context'; export const authenticationStrategyOAuthPath = (strategy: AuthenticationStrategy): string => { switch (strategy) { // Google case zAuthenticationStrategy.Enum.Google: return fullPath(GOOGLE_PATH); // LinkedIn case zAuthenticationStrategy.Enum.LinkedIn: return fullPath(LINKEDIN_PATH); default: return fullPath(GOOGLE_PATH); } }; export const fullPath = (path: string): string => `${API_URL}/oauth${path}`; export const buildWindowParams = authenticationResponse.merge(z.object({ error: z.string().optional(), })); export type BuildWindowParams = z.infer; export const buildOAuthWindowResponse = (params: BuildWindowParams): string => { const message = JSON.stringify({ ...params, source: FRAME_DATA_SOURCE, }); const hostWindowJSON = JSON.stringify(CONTEXT_WEB_UI_URL); return ``; }; /** ****************************************************************************** * Google ******************************************************************************* */ export const GOOGLE_PATH = '/google'; export const GOOGLE_CALLBACK_PATH = '/google/callback'; /** ****************************************************************************** * LinkedIn ******************************************************************************* */ export const LINKEDIN_PATH = '/linkedin'; export const LINKEDIN_CALLBACK_PATH = '/linkedin/callback'; /** ****************************************************************************** * OAuth Window Management ******************************************************************************* */ let windowObjectReference: Window | null = null; let previousUrl: string | null = null; export type OAuthMessageData = AuthenticationResponse & { source: string }; type OAuthMessage = MessageEvent; const receiveMessage = (event: OAuthMessage): OAuthMessageData | null => { if (event.origin !== API_URL) { return null; } if (event?.data?.source !== FRAME_DATA_SOURCE) { return null; } return event?.data ?? null; }; let previousEventHandler: ((event: OAuthMessage) => void) | null = null; export const openOAuthWindow = (url: string, name: string): Promise => { return new Promise((resolve) => { if (previousEventHandler) { window.removeEventListener('message', previousEventHandler); previousEventHandler = null; } const strWindowFeatures = 'toolbar=no, menubar=no, width=600, height=700, top=100, left=100'; if (windowObjectReference === null || windowObjectReference.closed) { windowObjectReference = window.open(url, name, strWindowFeatures); } else if (previousUrl !== url) { windowObjectReference = window.open(url, name, strWindowFeatures); windowObjectReference?.focus(); } else { windowObjectReference.focus(); } previousEventHandler = (event: OAuthMessage): void => { const data = receiveMessage(event); if (data?.user || data?.error) { windowObjectReference?.close(); resolve(data); } }; window.addEventListener('message', previousEventHandler, false); previousUrl = url; }); }; export const closeOAuthWindow = (): boolean => { windowObjectReference?.close(); const closed = windowObjectReference?.closed ?? true; windowObjectReference = null; return closed; };