diff --git a/v2/src/components/customAdmonition/index.tsx b/v2/src/components/customAdmonition/index.tsx index 9a78709c3..e61ae79eb 100644 --- a/v2/src/components/customAdmonition/index.tsx +++ b/v2/src/components/customAdmonition/index.tsx @@ -19,7 +19,7 @@ export default function CustomAdmonition(props: PropsWithChildren<{
- + {allowedTypes[props.type]} diff --git a/v2/src/components/httpNetworking.ts b/v2/src/components/httpNetworking.ts index ca32c720d..33c8838e8 100644 --- a/v2/src/components/httpNetworking.ts +++ b/v2/src/components/httpNetworking.ts @@ -1,6 +1,5 @@ import axios from "axios"; -import { getAnalytics, sendSDKLogsToBackend } from "./utils"; -import { getUserInformation } from "./api/user/info"; +import { getHttpNetworkingSDKLogsHooks } from "./sdklogsutils"; export enum HTTP_REQUEST_ERROR { SESSION_EXPIRED, @@ -102,11 +101,12 @@ export async function simpleGETRequest(url: string, userConfig: any = {}, versio "api-version": version + "" } }; - let frontTokenExists = cookieExists("sFrontToken"); + const sdkLogsHooks = getHttpNetworkingSDKLogsHooks(); + sdkLogsHooks.preApiExecutionHook(); let response = await axios.get(url, userConfig); let data = await response.data; let headers = response.headers; - await sendAnalyticsIfFrontTokenRemoved(url, frontTokenExists, headers); + sdkLogsHooks.postApiExecutionHook(url, headers); return { data, headers }; } @@ -122,11 +122,12 @@ export async function simplePOSTRequest(url: string, data: any, userConfig: POST "api-version": version + "" } }; - let frontTokenExists = cookieExists("sFrontToken"); + const sdkLogsHooks = getHttpNetworkingSDKLogsHooks(); + sdkLogsHooks.preApiExecutionHook(); let response = await axios.post(url, data, userConfig); let responseData = response.data; let headers = response.headers; - await sendAnalyticsIfFrontTokenRemoved(url, frontTokenExists, headers); + sdkLogsHooks.postApiExecutionHook(url, headers); return { data: responseData, headers, status: response.status, statusText: response.statusText }; } @@ -142,11 +143,12 @@ export async function simplePATCHRequest(url: string, data: any, userConfig: PAT "api-version": version + "" } }; - let frontTokenExists = cookieExists("sFrontToken"); + const sdkLogsHooks = getHttpNetworkingSDKLogsHooks(); + sdkLogsHooks.preApiExecutionHook(); let response = await axios.patch(url, data, userConfig); let responseData = response.data; let headers = response.headers; - await sendAnalyticsIfFrontTokenRemoved(url, frontTokenExists, headers); + sdkLogsHooks.postApiExecutionHook(url, headers); return { data: responseData, headers }; } @@ -162,11 +164,12 @@ export async function simplePUTRequest(url: string, data: any, userConfig: POSTR "api-version": version + "" } }; - let frontTokenExists = cookieExists("sFrontToken"); + const sdkLogsHooks = getHttpNetworkingSDKLogsHooks(); + sdkLogsHooks.preApiExecutionHook(); let response = await axios.put(url, data, userConfig); let responseData = response.data; let headers = response.headers; - await sendAnalyticsIfFrontTokenRemoved(url, frontTokenExists, headers); + sdkLogsHooks.postApiExecutionHook(url, headers); return { data: responseData, headers }; } @@ -185,132 +188,12 @@ export async function simpleDELETERequest(url: string, userConfig: DELETERequest "api-version": version + "" } }; - let frontTokenExists = cookieExists("sFrontToken"); + const sdkLogsHooks = getHttpNetworkingSDKLogsHooks(); + sdkLogsHooks.preApiExecutionHook(); delete userConfig.params; let response = await axios.delete(url, userConfig); let data = await response.data; let headers = response.headers; - await sendAnalyticsIfFrontTokenRemoved(url, frontTokenExists, headers); + sdkLogsHooks.postApiExecutionHook(url, headers); return { data, headers }; -} - -async function sendAnalyticsIfFrontTokenRemoved(url: string, frontTokenExists: boolean, headers: any) { - if (!frontTokenExists) { - return; - } - let updatedFrontTokenExists = cookieExists("sFrontToken"); - if (!updatedFrontTokenExists) { - // this means it was removed between the api call! - // send analytics - sendAuthAnalytics("front_token_removed", { - url, - headers - }); - await sendSDKLogsToBackend() - } -} - -export function cookieExists(name: string) { - const cookies = document.cookie; - const regex = new RegExp("(^|; )" + encodeURIComponent(name) + "="); - return regex.test(cookies); -} - -const sendAuthAnalytics = (eventName: string, payload: Record, version = "v1") => { - getAnalytics().then((stAnalytics: any) => { - if (stAnalytics === undefined) { - console.log("mocked event send:", eventName, version, payload); - return; - } - stAnalytics.sendEvent( - eventName, - { - type: "auth", - ...payload - }, - version - ); - }); -}; - -function getCookieValue(cookieName: string) { - const cookies = document.cookie; - const cookieArray = cookies.split(';'); - for (let i = 0; i < cookieArray.length; i++) { - const cookie = cookieArray[i].trim(); - if (cookie.startsWith(cookieName + '=')) { - return cookie.substring(cookieName.length + 1); - } - } - return null; - } - - export async function checkForDesyncedSession() { - const EVENT_NAME = 'desynced_session_state'; - const didFrontTokenExistBeforeAPICall = cookieExists("sFrontToken"); - - try { - await getUserInformation(); - const doesFrontendTokenExistAfterAPICall = cookieExists("sFrontToken"); - if (!doesFrontendTokenExistAfterAPICall) { - const payload = { - didFrontTokenExistBeforeAPICall, - stLastAccessTokenUpdate: getCookieValue("st-last-access-token-update"), - statusCode: 200 - }; - getAnalytics().then((stAnalytics: any) => { - if (stAnalytics === undefined) { - console.log('mocked event send:', EVENT_NAME, 'v1', payload); - return; - } - stAnalytics.sendEvent( - EVENT_NAME, - { - type: EVENT_NAME, - ...payload, - }, - 'v1' - ); - }); - } - } catch (e:any) { - if ( - "response" in e && - e.response.status === 401 && - e.response.data && - e.response.data.message === "try refresh token" - ) { - if (!cookieExists("sFrontToken")) { - const payload = { - didFrontTokenExistBeforeAPICall, - stLastAccessTokenUpdate: getCookieValue("st-last-access-token-update"), - statusCode: 401 - }; - getAnalytics().then((stAnalytics: any) => { - if (stAnalytics === undefined) { - console.log("mocked event send:", EVENT_NAME, "v1", payload); - return; - } - stAnalytics.sendEvent( - EVENT_NAME, - { - type: EVENT_NAME, - ...payload - }, - "v1" - ); - }); - } - } - } - } - - export function historyPushStateOverride(onPush: () => void) { - const originalPushState = history.pushState; - history.pushState = function (...args) { - const result = originalPushState.apply(this, args); - onPush(); - return result; - }; - } - \ No newline at end of file +} \ No newline at end of file diff --git a/v2/src/components/sdklogsutils.ts b/v2/src/components/sdklogsutils.ts new file mode 100644 index 000000000..14a614022 --- /dev/null +++ b/v2/src/components/sdklogsutils.ts @@ -0,0 +1,252 @@ +import { CookieHandlerInput } from 'supertokens-website/lib/build/utils/cookieHandler/types'; +import { getUserInformation } from './api/user/info'; +import { getAnalytics } from './utils'; +import { InputType } from 'supertokens-website'; + +const sendAuthAnalytics = ( + eventName: string, + payload: Record, + version = 'v1' +) => { + getAnalytics().then((stAnalytics: any) => { + if (stAnalytics === undefined) { + console.log('mocked event send:', eventName, version, payload); + return; + } + stAnalytics.sendEvent( + eventName, + { + type: 'auth', + ...payload, + }, + version + ); + }); +}; + +export const SDK_LOGS_STORAGE_KEY = 'Supertokens-sdk-logs'; +function NOOP() {} +function shouldEnableSDKLogs() { + return false; // change to true, to enable sdk logs +} + +export function cookieExists(name: string) { + const cookies = document.cookie; + const regex = new RegExp('(^|; )' + encodeURIComponent(name) + '='); + return regex.test(cookies); +} + +export function getCookieValue(cookieName: string) { + const cookies = document.cookie; + const cookieArray = cookies.split(';'); + for (let i = 0; i < cookieArray.length; i++) { + const cookie = cookieArray[i].trim(); + if (cookie.startsWith(cookieName + '=')) { + return cookie.substring(cookieName.length + 1); + } + } + return null; +} + +const isSuperTokensSDKLog = (data: any) => { + return typeof data === 'string' && data.includes('supertokens-website-ver:'); +}; + +type ConsoleLog = typeof globalThis.console.log; +export const overrideConsoleImplementation = ( + customImplementation: ConsoleLog +) => { + const oldConsoleLogImplementation = globalThis.console.log; + globalThis.console.log = (data) => { + customImplementation(data, oldConsoleLogImplementation); + }; +}; + +export const saveSDKLogsConsoleOverride = ( + data: any, + oldConsoleImplementation: ConsoleLog +) => { + if (isSuperTokensSDKLog(data)) { + const logArrayStr = localStorage.getItem(SDK_LOGS_STORAGE_KEY) || '[]'; + const logArray = JSON.parse(logArrayStr) as string[]; + + if (logArray.length === 1000) { + logArray.shift(); + } + logArray.push(data); + + localStorage.setItem(SDK_LOGS_STORAGE_KEY, JSON.stringify(logArray)); + } else { + oldConsoleImplementation(data); + } +}; + +export async function sendSDKLogsToBackend(customData?: Record) { + if (!shouldEnableSDKLogs()) { + return; + } + const sdkLogs = localStorage.getItem(SDK_LOGS_STORAGE_KEY) || '[]'; + const parsedSDKLogs = JSON.parse(sdkLogs); + + getAnalytics().then((stAnalytics: any) => { + if (stAnalytics === undefined) { + console.log('mocked event send:', 'auth_error_sdk_logs', parsedSDKLogs); + return; + } + stAnalytics.sendSupertokensSDKLogs({ logs: parsedSDKLogs, ...customData }); + }); +} + +export async function checkForDesyncedSession() { + if (!shouldEnableSDKLogs()) { + return; + } + const EVENT_NAME = 'desynced_session_state'; + const didFrontTokenExistBeforeAPICall = cookieExists('sFrontToken'); + + try { + await getUserInformation(); + const doesFrontendTokenExistAfterAPICall = cookieExists('sFrontToken'); + if (!doesFrontendTokenExistAfterAPICall) { + const payload = { + didFrontTokenExistBeforeAPICall, + stLastAccessTokenUpdate: getCookieValue('st-last-access-token-update'), + statusCode: 200, + }; + getAnalytics().then((stAnalytics: any) => { + if (stAnalytics === undefined) { + console.log('mocked event send:', EVENT_NAME, 'v1', payload); + return; + } + stAnalytics.sendEvent( + EVENT_NAME, + { + type: EVENT_NAME, + ...payload, + }, + 'v1' + ); + }); + } + } catch (e: any) { + if ( + 'response' in e && + e.response?.status === 401 && + e.response.data && + e.response.data.message === 'try refresh token' + ) { + if (!cookieExists('sFrontToken')) { + const payload = { + didFrontTokenExistBeforeAPICall, + stLastAccessTokenUpdate: getCookieValue( + 'st-last-access-token-update' + ), + statusCode: 401, + }; + getAnalytics().then((stAnalytics: any) => { + if (stAnalytics === undefined) { + console.log('mocked event send:', EVENT_NAME, 'v1', payload); + return; + } + stAnalytics.sendEvent( + EVENT_NAME, + { + type: EVENT_NAME, + ...payload, + }, + 'v1' + ); + }); + } + } + } +} + +export function historyPushStateOverride(onPush: () => void) { + const originalPushState = history.pushState; + history.pushState = function (...args) { + const result = originalPushState.apply(this, args); + onPush(); + return result; + }; +} + +const cookieSDKHook: CookieHandlerInput = (original) => { + return { + ...original, + setCookie: (cookieString: string) => { + const cookieName = cookieString.split(';')[0].split('=')[0]; + if (cookieName === 'sFrontToken') { + let cookieValue = cookieString.split(';')[0].split('=')[1].trim(); + if (cookieValue === '' && cookieExists('sFrontToken')) { + const stack = new Error().stack; + sendSDKLogsToBackend({ + stack, + title: 'front_token_cookie_removed', + }); + } + } + return original.setCookie(cookieString); + }, + }; +}; + +export async function sendAnalyticsIfFrontTokenRemoved( + url: string, + frontTokenExists: boolean, + headers: any +) { + if (!frontTokenExists) { + return; + } + let updatedFrontTokenExists = cookieExists('sFrontToken'); + if (!updatedFrontTokenExists) { + // this means it was removed between the api call! + // send analytics + sendAuthAnalytics('front_token_removed', { + url, + headers, + }); + await sendSDKLogsToBackend(); + } +} + +export function preSDKLogsOverrides() { + if (!shouldEnableSDKLogs()) { + return; + } + overrideConsoleImplementation(saveSDKLogsConsoleOverride); + historyPushStateOverride(checkForDesyncedSession); + checkForDesyncedSession(); +} + +export function getSdkLogConfigs(): Partial { + return shouldEnableSDKLogs() + ? { + enableDebugLogs: true, + cookieHandler: cookieSDKHook, + } + : {}; +} + +export function getHttpNetworkingSDKLogsHooks() { + if (!shouldEnableSDKLogs()) { + return { + postApiExecutionHook: NOOP, + preApiExecutionHook: NOOP, + }; + } + let frontTokenExists = false; + const preApiExecutionHook = () => { + frontTokenExists = cookieExists('sFrontToken'); + }; + + const postApiExecutionHook = async (url: string, headers: any) => { + await sendAnalyticsIfFrontTokenRemoved(url, frontTokenExists, headers); + }; + + return { + postApiExecutionHook, + preApiExecutionHook, + }; +} diff --git a/v2/src/components/utils.ts b/v2/src/components/utils.ts index 695bac6ce..70c706bbd 100644 --- a/v2/src/components/utils.ts +++ b/v2/src/components/utils.ts @@ -110,57 +110,4 @@ export function mockDelay(timeout = 2000) { export function isDev() { return window.location.host.startsWith("localhost"); -} - - -const isSuperTokensSDKLog = (data: any) => { - return typeof data === "string" && data.includes("supertokens-website-ver:"); -}; - -type ConsoleLog = typeof globalThis.console.log; - -export const overrideConsoleImplementation = (customImplementation: ConsoleLog) => { - const oldConsoleLogImplementation = globalThis.console.log; - globalThis.console.log = (data) => { - customImplementation(data, oldConsoleLogImplementation); - }; -}; - -export const SDK_LOGS_STORAGE_KEY = "Supertokens-sdk-logs"; - -export const saveSDKLogsConsoleOverride = (data: any, oldConsoleImplementation: ConsoleLog) => { - if (isSuperTokensSDKLog(data)) { - const logArrayStr = localStorage.getItem(SDK_LOGS_STORAGE_KEY) || "[]"; - const logArray = JSON.parse(logArrayStr) as string[]; - - if (logArray.length === 1000) { - logArray.shift(); - } - logArray.push(data); - - localStorage.setItem(SDK_LOGS_STORAGE_KEY, JSON.stringify(logArray)); - } else { - oldConsoleImplementation(data); - } -}; - -export async function sendSDKLogsToBackend(customData?: Record) { - const sdkLogs = localStorage.getItem(SDK_LOGS_STORAGE_KEY) || "[]"; - const parsedSDKLogs = JSON.parse(sdkLogs); - - getAnalytics().then((stAnalytics: any) => { - if (stAnalytics === undefined) { - console.log("mocked event send:", "auth_error_sdk_logs", parsedSDKLogs); - return; - } - stAnalytics.sendEvent( - 'auth_error_sdk_logs', - { - type: 'auth_error_sdk_logs', - logs: parsedSDKLogs, - ...customData - }, - 'v1' - ) - }); } \ No newline at end of file diff --git a/v2/src/theme/DocItem/index.js b/v2/src/theme/DocItem/index.js index be4703bb2..11e1d9026 100644 --- a/v2/src/theme/DocItem/index.js +++ b/v2/src/theme/DocItem/index.js @@ -221,7 +221,7 @@ function DocItem(props) { {location.pathname.startsWith("/docs/contribute/") ? <> -
important

This is a contributors guide and NOT a user guide. Please visit these docs if you are using or evaluating SuperTokens.

+
important

This is a contributors guide and NOT a user guide. Please visit these docs if you are using or evaluating SuperTokens.

: null}
diff --git a/v2/src/theme/Layout/index.js b/v2/src/theme/Layout/index.js index 6cf8e0a68..341bc9e91 100644 --- a/v2/src/theme/Layout/index.js +++ b/v2/src/theme/Layout/index.js @@ -18,11 +18,12 @@ import Head from '@docusaurus/Head'; import { useLocation } from '@docusaurus/router'; import './styles.css'; import supertokens from "supertokens-website"; -import {overrideConsoleImplementation,saveSDKLogsConsoleOverride, sendSDKLogsToBackend} from '../../components/utils' -import {checkForDesyncedSession, cookieExists, historyPushStateOverride} from '../../components/httpNetworking' +import { + getSdkLogConfigs, + preSDKLogsOverrides, +} from '../../components/sdklogsutils'; import styles from "./styles.module.css"; - if (typeof window !== 'undefined') { let API_DOMAIN let API_BASE_PATH @@ -33,31 +34,11 @@ if (typeof window !== 'undefined') { API_DOMAIN = "https://dev.api.supertokens.com" API_BASE_PATH = "/0/auth" } - overrideConsoleImplementation(saveSDKLogsConsoleOverride); let sessionExpiredStatusCode = 401; + preSDKLogsOverrides(); supertokens.init({ apiDomain: API_DOMAIN, apiBasePath: API_BASE_PATH, - enableDebugLogs: true, - cookieHandler: (original) => { - return { - ...original, - setCookie: (cookieString) => { - const cookieName = cookieString.split(";")[0].split("=")[0]; - if (cookieName === "sFrontToken") { - let cookieValue = cookieString.split(";")[0].split("=")[1].trim(); - if (cookieValue === "" && cookieExists("sFrontToken")) { - const stack = new Error().stack; - sendSDKLogsToBackend({ - stack, - title: "front_token_cookie_removed", - }); - } - } - return original.setCookie(cookieString); - }, - }; - }, sessionExpiredStatusCode, preAPIHook: async (context) => { return { @@ -66,14 +47,13 @@ if (typeof window !== 'undefined') { ...context.requestInit, headers: { ...context.requestInit.headers, - "api-version": "0" - } - } - } - } + 'api-version': '0', + }, + }, + }; + }, + ...getSdkLogConfigs(), }); - checkForDesyncedSession(); - historyPushStateOverride(checkForDesyncedSession); } function OriginalLayout(props) { diff --git a/v2/src/theme/NavbarItem/recipeSelector.js b/v2/src/theme/NavbarItem/recipeSelector.js index 6d5839605..b6bd1c262 100644 --- a/v2/src/theme/NavbarItem/recipeSelector.js +++ b/v2/src/theme/NavbarItem/recipeSelector.js @@ -44,7 +44,7 @@ export default function RecipeSelector(props) { }; useEffect(() => { const closeDropDown = (e) => { - if (e.target.classList[0].startsWith("recipe_selector")) { + if (e.target.classList[0]?.startsWith("recipe_selector")) { // no-op } else { setOpen(false);