UNPKG

@nhost/nhost-js

Version:

Nhost JavaScript SDK

1 lines 6.61 kB
{"version":3,"file":"refreshSession-B_E2FLUs.cjs","names":[],"sources":["../src/session/refreshSession.ts"],"sourcesContent":["import type { Client as AuthClient, ErrorResponse } from '../auth';\nimport type { FetchResponse } from '../fetch';\nimport type { StoredSession } from './session';\nimport type { SessionStorage } from './storage';\n\nclass DummyLock implements Lock {\n async request(\n _name: string,\n _options: { mode: 'exclusive' | 'shared' },\n // biome-ignore lint/suspicious/noExplicitAny: any\n callback: () => Promise<any>,\n ) {\n return callback();\n }\n}\n\ninterface Lock {\n request: (\n name: string,\n options: { mode: 'exclusive' | 'shared' },\n // biome-ignore lint/suspicious/noExplicitAny: blah\n callback: () => Promise<any>,\n // biome-ignore lint/suspicious/noExplicitAny: blah\n ) => Promise<any>;\n}\n\nconst lock: Lock =\n typeof navigator !== 'undefined' && navigator.locks\n ? navigator.locks\n : new DummyLock();\n\n/**\n * Refreshes the authentication session if needed\n *\n * This function checks if the current session needs to be refreshed based on\n * the access token expiration time. If a refresh is needed, it will attempt to\n * refresh the token using the provided auth client.\n *\n * @param auth - The authentication client to use for token refresh\n * @param storage - The session storage implementation\n * @param marginSeconds - The number of seconds before the token expiration to refresh the session. If the token is still valid for this duration, it will not be refreshed. Set to 0 to force the refresh.\n * @returns A promise that resolves to the current session (refreshed if needed) or null if no session exists\n */\nexport const refreshSession = async (\n auth: AuthClient,\n storage: SessionStorage,\n marginSeconds = 60,\n): Promise<StoredSession | null> => {\n try {\n return await _refreshSession(auth, storage, marginSeconds);\n } catch (error) {\n try {\n // we retry the refresh token in case of transient error\n // or race conditions\n console.warn('error refreshing session, retrying:', error);\n return await _refreshSession(auth, storage, marginSeconds);\n } catch (error) {\n const errResponse = error as FetchResponse<ErrorResponse>;\n if (errResponse?.status === 401) {\n // this probably means the refresh token is invalid\n console.error('session probably expired');\n storage.remove();\n }\n return null;\n }\n }\n};\n\n/**\n * Internal implementation of the refresh session logic\n *\n * @param auth - The authentication client to use for token refresh\n * @param storage - The session storage implementation\n * @param marginSeconds - How many seconds before expiration to trigger a refresh\n * @returns A promise that resolves to the current session (refreshed if needed) or null if no session exists\n * @private\n */\nconst _refreshSession = async (\n auth: AuthClient,\n storage: SessionStorage,\n marginSeconds = 60,\n): Promise<StoredSession | null> => {\n const {\n session,\n needsRefresh,\n }: { session: StoredSession | null; needsRefresh: boolean } =\n await lock.request('nhostSessionLock', { mode: 'shared' }, async () => {\n return _needsRefresh(storage, marginSeconds);\n });\n\n if (!session) {\n return null; // No session found\n }\n\n if (!needsRefresh) {\n return session; // No need to refresh\n }\n\n const refreshedSession: StoredSession | null = await lock.request(\n 'nhostSessionLock',\n { mode: 'exclusive' },\n async () => {\n const { session, needsRefresh, sessionExpired } = _needsRefresh(\n storage,\n marginSeconds,\n );\n\n if (!session) {\n return null; // No session found\n }\n\n if (!needsRefresh) {\n return session; // No need to refresh\n }\n\n try {\n const response = await auth.refreshToken({\n refreshToken: session.refreshToken,\n });\n storage.set(response.body);\n\n return response.body;\n } catch (error) {\n if (!sessionExpired) {\n return session;\n }\n\n throw error;\n }\n },\n );\n\n return refreshedSession;\n};\n\n/**\n * Checks if the current session needs to be refreshed based on token expiration\n *\n * @param storage - The session storage implementation\n * @param marginSeconds - How many seconds before expiration to trigger a refresh\n * @returns An object containing the session, whether it needs refreshing, and whether it has expired\n * @private\n */\nconst _needsRefresh = (storage: SessionStorage, marginSeconds = 60) => {\n const session = storage.get();\n if (!session) {\n return { session: null, needsRefresh: false, sessionExpired: false };\n }\n\n if (!session.decodedToken?.exp) {\n // if the session does not have a valid decoded token, treat it as expired\n // as we can't determine its validity\n return { session, needsRefresh: true, sessionExpired: true };\n }\n\n // Force refresh if marginSeconds is 0\n if (marginSeconds === 0) {\n return { session, needsRefresh: true, sessionExpired: false };\n }\n\n const currentTime = Date.now();\n if (session.decodedToken.exp - currentTime > marginSeconds * 1000) {\n return { session, needsRefresh: false, sessionExpired: false };\n }\n\n return {\n session,\n needsRefresh: true,\n sessionExpired: session.decodedToken.exp < currentTime,\n };\n};\n"],"mappings":"AAKA,IAqBM,EACiB,oBAAd,WAA6B,UAAU,MAC1C,UAAU,MACV,IAxBN,MACE,aAAM,CACJ,EACA,EAEA,GAEA,OAAO,GACT,GA8BW,EAAiB,MAC5B,EACA,EACA,EAAgB,MAEhB,IACE,aAAa,EAAgB,EAAM,EAAS,EAC9C,CAAA,MAAS,GACP,IAIE,OADA,QAAQ,KAAK,sCAAuC,SACvC,EAAgB,EAAM,EAAS,EAC9C,CAAA,MAAS,GAOP,OAL4B,MAAxB,GAAa,SAEf,QAAQ,MAAM,4BACd,EAAQ,UAEH,IACT,CACF,GAYI,EAAkB,MACtB,EACA,EACA,EAAgB,MAEhB,MAAM,QACJ,EAAA,aACA,SAEM,EAAK,QAAQ,mBAAoB,CAAE,KAAM,WAAY,SAClD,EAAc,EAAS,KAGlC,OAAK,EAIA,QAIgD,EAAK,QACxD,mBACA,CAAE,KAAM,cACR,UACE,MAAM,QAAE,EAAA,aAAS,EAAA,eAAc,GAAmB,EAChD,EACA,GAGF,IAAK,EACH,OAAO,KAGT,IAAK,EACH,OAAO,EAGT,IACE,MAAM,QAAiB,EAAK,aAAa,CACvC,aAAc,EAAQ,eAIxB,OAFA,EAAQ,IAAI,EAAS,MAEd,EAAS,IAClB,CAAA,MAAS,GACP,IAAK,EACH,OAAO,EAGT,MAAM,CACR,KAjCK,EAJA,IAuCT,EAaI,EAAA,CAAiB,EAAyB,EAAgB,MAC9D,MAAM,EAAU,EAAQ,MACxB,IAAK,EACH,MAAO,CAAE,QAAS,KAAM,cAAc,EAAO,gBAAgB,GAG/D,IAAK,EAAQ,cAAc,IAGzB,MAAO,CAAE,UAAS,cAAc,EAAM,gBAAgB,GAIxD,GAAsB,IAAlB,EACF,MAAO,CAAE,UAAS,cAAc,EAAM,gBAAgB,GAGxD,MAAM,EAAc,KAAK,MACzB,OAAI,EAAQ,aAAa,IAAM,EAA8B,IAAhB,EACpC,CAAE,UAAS,cAAc,EAAO,gBAAgB,GAGlD,CACL,UACA,cAAc,EACd,eAAgB,EAAQ,aAAa,IAAM,EAC7C"}