@gen3/frontend 0.12.57 → 0.12.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dts/api/auth/sessionLogout.d.ts.map +1 -1
- package/dist/dts/api/auth/sessionLogout.js +16 -12
- package/dist/dts/api/auth/sessionLogout.js.map +1 -1
- package/dist/dts/api/auth/tests/sessionLogout.unit.test.js +18 -7
- package/dist/dts/api/auth/tests/sessionLogout.unit.test.js.map +1 -1
- package/dist/dts/server.d.ts +2 -1
- package/dist/dts/server.d.ts.map +1 -1
- package/dist/dts/server.js +2 -1
- package/dist/dts/server.js.map +1 -1
- package/dist/index/cjs/QueryPanel-Bw-xhntZ.js +43 -0
- package/dist/index/cjs/QueryPanel-Bw-xhntZ.js.map +1 -0
- package/dist/index/cjs/QueryPanel-CyPKyG13.js +43 -0
- package/dist/index/cjs/QueryPanel-CyPKyG13.js.map +1 -0
- package/dist/index/cjs/QueryPanel-v-n3iGjq.js +43 -0
- package/dist/index/cjs/QueryPanel-v-n3iGjq.js.map +1 -0
- package/dist/index/cjs/index-Bx2OoF3F.js +41847 -0
- package/dist/index/cjs/index-Bx2OoF3F.js.map +1 -0
- package/dist/index/cjs/index-CHR1uioW.js +41847 -0
- package/dist/index/cjs/index-CHR1uioW.js.map +1 -0
- package/dist/index/cjs/index-Cjc3XMjn.js +41847 -0
- package/dist/index/cjs/index-Cjc3XMjn.js.map +1 -0
- package/dist/index/cjs/index.js +1 -1
- package/dist/index/esm/QueryPanel-CBkgOEOs.js +37 -0
- package/dist/index/esm/QueryPanel-CBkgOEOs.js.map +1 -0
- package/dist/index/esm/QueryPanel-CULgIAVP.js +37 -0
- package/dist/index/esm/QueryPanel-CULgIAVP.js.map +1 -0
- package/dist/index/esm/QueryPanel-ic9Je2fM.js +37 -0
- package/dist/index/esm/QueryPanel-ic9Je2fM.js.map +1 -0
- package/dist/index/esm/index-0Uqgg63f.js +41560 -0
- package/dist/index/esm/index-0Uqgg63f.js.map +1 -0
- package/dist/index/esm/index-BzfVgdDd.js +41560 -0
- package/dist/index/esm/index-BzfVgdDd.js.map +1 -0
- package/dist/index/esm/index-OtmztDtU.js +41560 -0
- package/dist/index/esm/index-OtmztDtU.js.map +1 -0
- package/dist/index/esm/index.js +1 -1
- package/dist/server/cjs/server.js +67 -0
- package/dist/server/cjs/server.js.map +1 -1
- package/dist/server/esm/server.js +69 -3
- package/dist/server/esm/server.js.map +1 -1
- package/dist/server.d.ts +3 -1
- package/package.json +3 -3
package/dist/index/esm/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as ACTIVITY_CHANNEL, a as AISearchPageGetServerSideProps, b as AccessLevel, c as AccordionPanel, d as ActionBar, e as ActionButton, f as AdminAuthZPageGetServerSideProps, g as AiSearch, h as AiSearchPage, i as AnalysisCenter, j as AnalysisCenterWithSections, k as AnalysisEditorPage, l as AnalysisEditorPageGetServerSideProps, m as AnalysisPage, n as AnalysisPageGetServerSideProps, o as AnalysisWithCloseButton, p as AuthzPage, B as BAD_DATA_MESSAGE, q as Banner, r as BaseModal, C as CardContainer, s as Charts, t as ClinicalDataAnalysisPage, u as ClinicalDataAnalysisServerSideProps, v as CohortActionButton, w as CohortBuilder, x as CohortDropdownActionButton, y as CohortManager, z as CohortManagerAndExpression, D as CollapsableCharts, E as CollapsableSidebar, F as ColorThemePage, G as ColorThemePageGetServerSideProps, H as ContentSource, I as CountsValue, J as CreateCredentialsAPIKeyModal, K as CrosswalkPage, L as CrosswalkPageGetServerSideProps, M as Custom403Page, N as Custom403PageGetServerSideProps, O as Custom404Page, P as Custom404PageGetServerSideProps, Q as DAYS_IN_YEAR, R as DEFAULT_MAXIMUM, S as DEFAULT_MINIMUM, T as DEFAULT_VISIBLE_ITEMS, U as DataDictionaryPage, V as DataLibraryPage, W as DataLibraryPageGetServerSideProps, X as DefaultAuthorizedRoutesConfig, Y as DictionaryPageGetServerSideProps, Z as DictionaryWithContext, _ as DiscoveryCellRendererFactory, $ as DiscoveryConfigProvider, a0 as DiscoveryIndexPanel, a1 as DiscoveryPage, a2 as DiscoveryPageGetServerSideProps, a3 as DownloadButton, a4 as DownloadsPanel, a5 as DropdownButton, a6 as DropdownPanel, a7 as DropdownWithIcon, a8 as EmptyTableMessage, a9 as EnumFacet, aa as EnumFacetChart, ab as ErrorCard, ac as ExactValueFacet, ad as ExplorerPage, ae as ExplorerPageGetServerSideProps, af as ExplorerPageGetServerSidePropsForConfigId, ag as ExplorerTable, ah as ExplorerTableCellRendererFactory, ai as ExplorerTableDetailsPanelFactory, aj as FacetControlsHeader, ak as FacetExpander, al as FacetSelector, am as FacetSortPanel, an as FacetTabs, ao as FileSummary, ap as FiltersPanel, aq as FirstTimeModal, ar as FixedNavPageLayout, as as Footer, at as Gen3Button, au as Gen3ButtonReverse, av as Gen3Link, aw as Gen3ModalsProvider, ax as Gen3Provider, ay as GqlQueryEditor, az as Header, aA as HoverLink, aB as IconButton, aC as InfoRolloverButton, aD as JSONObjectDownloadButton, aE as LandingPage, aF as LandingPageGetServerSideProps, aG as LoginButton, aH as LoginPage, aI as LoginPageGetServerSideProps, aJ as LoginPanel, aK as LoginProvidersPanel, aL as MessageCard, aM as MessagePage, aN as MetadataDictionary, aO as MultiSelectValueFacet, aP as NavPageLayout, aQ as NavigationBar, aR as NavigationButton, aS as NavigationRail, aT as NoAccessModal, aU as NotebookLitePage, aV as NotebookLitePageGetStaticProps, aW as OverflowTooltippedLabel, aX as PanelErrorBoundary, aY as Profile, aZ as ProfilePage, a_ as ProfilePageGetServerSideProps, a$ as ProfileProvider, b0 as ProtectedContent, b1 as ProtectedContentUI, b2 as ProtectedOverlay, b3 as QueryExpression, b4 as QueryExpressionContext, b5 as QueryExpressionSection, b6 as QueryPage, b7 as QueryPageGetServerSideProps, b8 as RangeFacet, b9 as ReactECharts, ba as Repository, bb as RepositoryPage, bc as RepositoryPageGetServerSideProps, bd as ResourcesFilters, be as SPECIAL_CASE_FIELDS, bf as SegmentedControl, bg as SessionExpiredModal, bh as SinglePageStudyDetailsPanel, bi as StaticNotebookIFrame, bj as StudyDetailsPanel, bk as StudyGroup, bl as StudyGroupPanel, bm as StudyProvider, bn as SubmissionPage, bo as SubmissionPageGetServerSideProps, bp as TabbedCohortBuilder, bq as TabbedCohortBuilderPage, br as TabbedCohortBuilderPageGetServerSideProps, bs as TabbedPanel, bt as TableXPositionContext, bu as TagCloud, bv as TailwindConfig, bw as TopBar, bx as UploadJSONButton, by as User, bz as WarningCard, bA as Workspace, bB as WorkspaceNoAccessPage, bC as WorkspaceNoAccessPageServerSideProps, bD as WorkspacePage, bE as WorkspacePageGetServerSideProps, bF as accessibleFieldName, bG as addIntersectionToIncludes, bH as addUnion, bI as analysisApiCohortDiscovery, bJ as capitalize, bK as classifyFacets, bL as compareKeysAscending, bM as compareKeysDescending, bN as computeRowSpan, bO as convertMinutesToMilliseconds, bP as convertPathsToTree, bQ as convertSecondsToMilliseconds, bR as convertToStringArray, bS as createCSSVariables, bT as createLabelFromHistogramData, bU as createMantineTheme, bV as credentialsLogin, bW as credentialsLogout, bX as extractObjectKey, bY as extractRangeValues, bZ as filterAggregationsDataKeys, b_ as focusStyles, b$ as gen3Modals, c0 as getAllFieldsFromFilterConfigs, c1 as getByPath, c2 as getCurrentUnixTimestamp, c3 as getFormattedTimestamp, c4 as getHighResolutionTimestamp, c5 as getNavPageLayoutPropsFromConfig, c6 as getParamsValueAsString, c7 as getTagInfo, c8 as hasOwnProperty, c9 as isArrayOfString, ca as isEnumFacetResponse, cb as isHeaderMetadata, cc as isPropertyKey, cd as isTextTransform, ce as isTimeGreaterThan, cf as itemsInCart, cg as mapFacetSortToSortType, ch as measureExecutionTime, ci as mergeDefaultTailwindClassnames, cj as minutesToMilliseconds, ck as processBucketData, cl as processDefinedRangeData, cm as processLabel, cn as processRangeData, co as processRangeKeyLabel, cp as registerCohortBuilderDefaultPreviewRenderers, cq as registerCohortDiscoveryApp, cr as registerCohortSimilarityApp, cs as registerDefaultDiscoveryDataLoaders, ct as registerDiscoveryDefaultCellRenderers, cu as registerDownloadButtonAction, cv as registerExplorerDefaultCellRenderers, cw as registerIGVApp, cx as registerMetadataSchemaApp, cy as removeIntersectionFromEnum, cz as removeKey, cA as removeMultipleSlashes, cB as removeUnion, cC as resourcePathFromProjectID, cD as sessionLogout, cE as sessionToken, cF as stripTrailingSlash, cG as toDisplayName, cH as truncateString, cI as typographyPlugin, cJ as unixTimeToString, cK as updateFacetEnum, cL as useClearFilters, cM as useDiscoveryContext, cN as useExtractEnumFilterValues, cO as useExtractRangeFilterValues, cP as useFieldNameToLabel, cQ as useGetFacetFilters, cR as useGuppyActionButton, cS as useUpdateFilters, cT as useUpdateFiltersFlat, cU as userHasMethodForServiceOnResource, cV as validateObjectHasRequiredFields, cW as withBasePath } from './index-
|
|
1
|
+
export { A as ACTIVITY_CHANNEL, a as AISearchPageGetServerSideProps, b as AccessLevel, c as AccordionPanel, d as ActionBar, e as ActionButton, f as AdminAuthZPageGetServerSideProps, g as AiSearch, h as AiSearchPage, i as AnalysisCenter, j as AnalysisCenterWithSections, k as AnalysisEditorPage, l as AnalysisEditorPageGetServerSideProps, m as AnalysisPage, n as AnalysisPageGetServerSideProps, o as AnalysisWithCloseButton, p as AuthzPage, B as BAD_DATA_MESSAGE, q as Banner, r as BaseModal, C as CardContainer, s as Charts, t as ClinicalDataAnalysisPage, u as ClinicalDataAnalysisServerSideProps, v as CohortActionButton, w as CohortBuilder, x as CohortDropdownActionButton, y as CohortManager, z as CohortManagerAndExpression, D as CollapsableCharts, E as CollapsableSidebar, F as ColorThemePage, G as ColorThemePageGetServerSideProps, H as ContentSource, I as CountsValue, J as CreateCredentialsAPIKeyModal, K as CrosswalkPage, L as CrosswalkPageGetServerSideProps, M as Custom403Page, N as Custom403PageGetServerSideProps, O as Custom404Page, P as Custom404PageGetServerSideProps, Q as DAYS_IN_YEAR, R as DEFAULT_MAXIMUM, S as DEFAULT_MINIMUM, T as DEFAULT_VISIBLE_ITEMS, U as DataDictionaryPage, V as DataLibraryPage, W as DataLibraryPageGetServerSideProps, X as DefaultAuthorizedRoutesConfig, Y as DictionaryPageGetServerSideProps, Z as DictionaryWithContext, _ as DiscoveryCellRendererFactory, $ as DiscoveryConfigProvider, a0 as DiscoveryIndexPanel, a1 as DiscoveryPage, a2 as DiscoveryPageGetServerSideProps, a3 as DownloadButton, a4 as DownloadsPanel, a5 as DropdownButton, a6 as DropdownPanel, a7 as DropdownWithIcon, a8 as EmptyTableMessage, a9 as EnumFacet, aa as EnumFacetChart, ab as ErrorCard, ac as ExactValueFacet, ad as ExplorerPage, ae as ExplorerPageGetServerSideProps, af as ExplorerPageGetServerSidePropsForConfigId, ag as ExplorerTable, ah as ExplorerTableCellRendererFactory, ai as ExplorerTableDetailsPanelFactory, aj as FacetControlsHeader, ak as FacetExpander, al as FacetSelector, am as FacetSortPanel, an as FacetTabs, ao as FileSummary, ap as FiltersPanel, aq as FirstTimeModal, ar as FixedNavPageLayout, as as Footer, at as Gen3Button, au as Gen3ButtonReverse, av as Gen3Link, aw as Gen3ModalsProvider, ax as Gen3Provider, ay as GqlQueryEditor, az as Header, aA as HoverLink, aB as IconButton, aC as InfoRolloverButton, aD as JSONObjectDownloadButton, aE as LandingPage, aF as LandingPageGetServerSideProps, aG as LoginButton, aH as LoginPage, aI as LoginPageGetServerSideProps, aJ as LoginPanel, aK as LoginProvidersPanel, aL as MessageCard, aM as MessagePage, aN as MetadataDictionary, aO as MultiSelectValueFacet, aP as NavPageLayout, aQ as NavigationBar, aR as NavigationButton, aS as NavigationRail, aT as NoAccessModal, aU as NotebookLitePage, aV as NotebookLitePageGetStaticProps, aW as OverflowTooltippedLabel, aX as PanelErrorBoundary, aY as Profile, aZ as ProfilePage, a_ as ProfilePageGetServerSideProps, a$ as ProfileProvider, b0 as ProtectedContent, b1 as ProtectedContentUI, b2 as ProtectedOverlay, b3 as QueryExpression, b4 as QueryExpressionContext, b5 as QueryExpressionSection, b6 as QueryPage, b7 as QueryPageGetServerSideProps, b8 as RangeFacet, b9 as ReactECharts, ba as Repository, bb as RepositoryPage, bc as RepositoryPageGetServerSideProps, bd as ResourcesFilters, be as SPECIAL_CASE_FIELDS, bf as SegmentedControl, bg as SessionExpiredModal, bh as SinglePageStudyDetailsPanel, bi as StaticNotebookIFrame, bj as StudyDetailsPanel, bk as StudyGroup, bl as StudyGroupPanel, bm as StudyProvider, bn as SubmissionPage, bo as SubmissionPageGetServerSideProps, bp as TabbedCohortBuilder, bq as TabbedCohortBuilderPage, br as TabbedCohortBuilderPageGetServerSideProps, bs as TabbedPanel, bt as TableXPositionContext, bu as TagCloud, bv as TailwindConfig, bw as TopBar, bx as UploadJSONButton, by as User, bz as WarningCard, bA as Workspace, bB as WorkspaceNoAccessPage, bC as WorkspaceNoAccessPageServerSideProps, bD as WorkspacePage, bE as WorkspacePageGetServerSideProps, bF as accessibleFieldName, bG as addIntersectionToIncludes, bH as addUnion, bI as analysisApiCohortDiscovery, bJ as capitalize, bK as classifyFacets, bL as compareKeysAscending, bM as compareKeysDescending, bN as computeRowSpan, bO as convertMinutesToMilliseconds, bP as convertPathsToTree, bQ as convertSecondsToMilliseconds, bR as convertToStringArray, bS as createCSSVariables, bT as createLabelFromHistogramData, bU as createMantineTheme, bV as credentialsLogin, bW as credentialsLogout, bX as extractObjectKey, bY as extractRangeValues, bZ as filterAggregationsDataKeys, b_ as focusStyles, b$ as gen3Modals, c0 as getAllFieldsFromFilterConfigs, c1 as getByPath, c2 as getCurrentUnixTimestamp, c3 as getFormattedTimestamp, c4 as getHighResolutionTimestamp, c5 as getNavPageLayoutPropsFromConfig, c6 as getParamsValueAsString, c7 as getTagInfo, c8 as hasOwnProperty, c9 as isArrayOfString, ca as isEnumFacetResponse, cb as isHeaderMetadata, cc as isPropertyKey, cd as isTextTransform, ce as isTimeGreaterThan, cf as itemsInCart, cg as mapFacetSortToSortType, ch as measureExecutionTime, ci as mergeDefaultTailwindClassnames, cj as minutesToMilliseconds, ck as processBucketData, cl as processDefinedRangeData, cm as processLabel, cn as processRangeData, co as processRangeKeyLabel, cp as registerCohortBuilderDefaultPreviewRenderers, cq as registerCohortDiscoveryApp, cr as registerCohortSimilarityApp, cs as registerDefaultDiscoveryDataLoaders, ct as registerDiscoveryDefaultCellRenderers, cu as registerDownloadButtonAction, cv as registerExplorerDefaultCellRenderers, cw as registerIGVApp, cx as registerMetadataSchemaApp, cy as removeIntersectionFromEnum, cz as removeKey, cA as removeMultipleSlashes, cB as removeUnion, cC as resourcePathFromProjectID, cD as sessionLogout, cE as sessionToken, cF as stripTrailingSlash, cG as toDisplayName, cH as truncateString, cI as typographyPlugin, cJ as unixTimeToString, cK as updateFacetEnum, cL as useClearFilters, cM as useDiscoveryContext, cN as useExtractEnumFilterValues, cO as useExtractRangeFilterValues, cP as useFieldNameToLabel, cQ as useGetFacetFilters, cR as useGuppyActionButton, cS as useUpdateFilters, cT as useUpdateFiltersFlat, cU as userHasMethodForServiceOnResource, cV as validateObjectHasRequiredFields, cW as withBasePath } from './index-BzfVgdDd.js';
|
|
2
2
|
import '@gen3/core';
|
|
3
3
|
import 'react/jsx-runtime';
|
|
4
4
|
import 'react';
|
|
@@ -322,9 +322,76 @@ const HTTP_NOT_AUTHORIZED = 401;
|
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
+
async function sessionLogout(req, res) {
|
|
326
|
+
if (req.method !== 'POST') {
|
|
327
|
+
res.setHeader('Allow', 'POST');
|
|
328
|
+
res.status(405).json({
|
|
329
|
+
error: 'Method not allowed'
|
|
330
|
+
});
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const FENCE_API = process.env.NODE_ENV === 'development' ? server.GEN3_FENCE_API : server.GEN3_FENCE_SERVICE;
|
|
334
|
+
try {
|
|
335
|
+
const fenceCookies = [
|
|
336
|
+
req.cookies.fence && `fence=${encodeURIComponent(req.cookies.fence)}`,
|
|
337
|
+
req.cookies.access_token && `access_token=${encodeURIComponent(req.cookies.access_token)}`
|
|
338
|
+
].filter((cookie)=>Boolean(cookie));
|
|
339
|
+
const fenceResponse = await fetch(`${FENCE_API}/logout`, {
|
|
340
|
+
method: 'GET',
|
|
341
|
+
headers: {
|
|
342
|
+
'Content-Type': 'text/html',
|
|
343
|
+
...fenceCookies.length > 0 ? {
|
|
344
|
+
Cookie: fenceCookies.join('; ')
|
|
345
|
+
} : {}
|
|
346
|
+
},
|
|
347
|
+
redirect: 'manual'
|
|
348
|
+
});
|
|
349
|
+
if (!fenceResponse.ok) {
|
|
350
|
+
console.warn(`Fence logout failed (${fenceResponse.status})`);
|
|
351
|
+
}
|
|
352
|
+
} catch (error) {
|
|
353
|
+
// GEN3_FENCE_SERVICE is an internal, cluster-only URL. Some deployments
|
|
354
|
+
// (e.g. local dev, or a frontend that isn't network-adjacent to Fence)
|
|
355
|
+
// cannot reach it at all — that must not block logout, since the part
|
|
356
|
+
// that matters to this app is clearing its own cookies below.
|
|
357
|
+
console.warn('Unable to reach Fence to complete logout', error);
|
|
358
|
+
}
|
|
359
|
+
// Clear local authentication regardless of whether Fence could be reached.
|
|
360
|
+
// This is what actually logs the user out of this app; telling Fence is
|
|
361
|
+
// best-effort on top of it.
|
|
362
|
+
res.setHeader('Set-Cookie', [
|
|
363
|
+
cookie.serialize('fence', '', {
|
|
364
|
+
sameSite: 'lax',
|
|
365
|
+
httpOnly: true,
|
|
366
|
+
secure: true,
|
|
367
|
+
path: '/',
|
|
368
|
+
expires: new Date(0)
|
|
369
|
+
}),
|
|
370
|
+
cookie.serialize('access_token', '', {
|
|
371
|
+
sameSite: 'lax',
|
|
372
|
+
httpOnly: true,
|
|
373
|
+
secure: true,
|
|
374
|
+
path: '/',
|
|
375
|
+
expires: new Date(0)
|
|
376
|
+
}),
|
|
377
|
+
// delete the credentials_token cookie for credentials login
|
|
378
|
+
cookie.serialize('credentials_token', '', {
|
|
379
|
+
sameSite: 'lax',
|
|
380
|
+
httpOnly: process.env.NODE_ENV === 'production',
|
|
381
|
+
secure: process.env.NODE_ENV === 'production',
|
|
382
|
+
path: '/',
|
|
383
|
+
expires: new Date(0)
|
|
384
|
+
})
|
|
385
|
+
]);
|
|
386
|
+
res.status(200).json({
|
|
387
|
+
success: 'success'
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
325
391
|
exports.ContentSource = index;
|
|
326
392
|
exports.DefaultAuthorizedRoutesConfig = DefaultAuthorizedRoutesConfig;
|
|
327
393
|
exports.fetchJWTKey = fetchJWTKey;
|
|
328
394
|
exports.getAccessToken = getAccessToken;
|
|
395
|
+
exports.sessionLogout = sessionLogout;
|
|
329
396
|
exports.sessionToken = handler;
|
|
330
397
|
//# sourceMappingURL=server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sources":["../../../src/lib/content/filesystem.ts","../../../src/lib/content/ContentDatabase.ts","../../../src/lib/content/constants.ts","../../../src/lib/content/index.ts","../../../src/lib/authz/type.ts","../../../src/lib/auth/utils.ts","../../../src/api/auth/errorHandler.ts","../../../src/api/auth/sessionToken.ts"],"sourcesContent":["import { ContentSourceInterface } from './types';\nimport fs from 'fs';\nimport path from 'path';\n\nconst myGlob = (dir: string, filter: string) => {\n try {\n const files = fs.readdirSync(dir);\n return files.filter((file) => file.search(filter) !== -1);\n } catch (error: any) {}\n return [];\n};\n\nexport class FilesystemContent implements ContentSourceInterface {\n rootPath: string;\n constructor({ rootPath }: { rootPath?: string }) {\n this.rootPath = rootPath || '';\n }\n\n public async get<T extends Record<string, any>>(\n filepath: string,\n ): Promise<T> {\n try {\n return await JSON.parse(\n fs.readFileSync(path.join(this.rootPath, filepath)).toString('utf8'),\n );\n } catch {\n throw new Error(`Cannot process ${path.join(this.rootPath, filepath)} `);\n }\n }\n public async getAll<T extends Record<string, any>>(\n filepath: string,\n filter: string,\n ): Promise<Array<T>> {\n try {\n const files = myGlob(path.join(this.rootPath, filepath), filter);\n return Promise.all(\n files.map((file) => this.get<T>(path.join(filepath, file))),\n );\n } catch {\n throw new Error(\n `getAllCannot process ${path.join(this.rootPath, filepath)}/${filter}`,\n );\n }\n }\n}\n","import { ContentSourceInterface } from './types';\n\n/**\n * The Content management \"datastore\" which is an abstracted interface to\n * some persistent storage of content needed for Gen3's content management system\n */\n\nexport interface CreateDatabase {\n store: ContentSourceInterface;\n}\n\nexport class ContentDatabase {\n public store: ContentSourceInterface;\n\n constructor(public config: CreateDatabase) {\n this.store = config.store;\n }\n\n public async get<T extends Record<string, any>>(\n filepath: string,\n ): Promise<T> {\n return this.store.get(filepath);\n }\n\n public async getAll<T extends Record<string, any>>(\n filepath: string,\n filter: string,\n ): Promise<Array<T>> {\n // Array<Promise<T>>\n return this.store.getAll(filepath, filter);\n }\n}\n","export const GEN3_FRONTEND_CONFIGURATION_ROOT =\n process.env.GEN3_FRONTEND_CONFIGURATION_ROOT || './config/';\n","import { FilesystemContent } from './filesystem';\nimport { ContentDatabase } from './ContentDatabase';\nimport { GEN3_FRONTEND_CONFIGURATION_ROOT } from './constants';\n\nexport class ContentSource {\n private static instance: ContentSource | null = null;\n private contentDatabase: ContentDatabase;\n\n private constructor() {\n const config = {\n store: new FilesystemContent({\n rootPath: GEN3_FRONTEND_CONFIGURATION_ROOT,\n }),\n };\n this.contentDatabase = new ContentDatabase(config);\n }\n\n public static getInstance(): ContentSource {\n if (!ContentSource.instance) {\n ContentSource.instance = new ContentSource();\n }\n return ContentSource.instance;\n }\n\n // Add methods to access the ContentDatabase functionality\n // For example:\n public getContentDatabase(): ContentDatabase {\n return this.contentDatabase;\n }\n\n // ✅ Compatibility helper: allow `ContentSource.getContentDatabase()`\n public static getContentDatabase(): ContentDatabase {\n return ContentSource.getInstance().getContentDatabase();\n }\n}\n\n// Export a singleton instance\nexport default ContentSource.getInstance();\n","export type RouteRule = {\n loginRequired?: boolean;\n authz?: string[];\n};\n\nexport type RouteConfig = Record<string, RouteRule>;\n\nexport interface AuthorizedRoutesConfig {\n routes: RouteConfig;\n}\n\nexport const DefaultAuthorizedRoutesConfig = {\n routes: {\n '/DataLibrary': {\n loginRequired: true,\n },\n '/Workspace': {\n loginRequired: true,\n },\n '/Profile': {\n loginRequired: true,\n },\n '*': {\n loginRequired: false,\n },\n },\n};\n","import { fetchFence } from '@gen3/core/server';\nimport { parse } from 'cookie';\n\ninterface Gen3JTWKeys {\n keys: string[];\n}\n\n/**\n * Fetches a JSON Web Token (JWT) key from the specified endpoint.\n *\n * This asynchronous function interacts with the Gen3 Fence API to retrieve JWT keys.\n * If the response status is not 200 or the expected key format is not found, the function\n * will return null. Otherwise, it returns the first available key.\n *\n * @function\n * @async\n * @returns {Promise<string|null>} A Promise resolving to the JWT key as a string if available, or null if not.\n */\nexport const fetchJWTKey = async (useService: boolean = false) => {\n const response = await fetchFence<Gen3JTWKeys>(\n {\n endpoint: '/jwt/keys',\n isJSON: true,\n },\n useService,\n );\n if (response.status !== 200) {\n return null;\n }\n\n if (response?.data?.keys.length && response?.data?.keys[0].length > 1) {\n return response.data.keys[0][1];\n }\n return null;\n};\n\n/**\n * Extracts and validates the access token from the provided cookie string.\n *\n * This function parses the cookie string to retrieve an access token, while ensuring\n * its format and size meet expected criteria. It also supports \"credentials login\" in\n * development mode to retrieve a `credentials_token` as a fallback.\n *\n * @param {string} [cookie] - The cookie string containing token information. Optional.\n * @returns {string|null} The valid access token if present and correctly formatted, or `null` if unavailable, invalid, or inappropriate.\n *\n * @property {number} MAX_COOKIE_LENGTH - The maximum allowed length for the cookie string to prevent potential denial-of-service (DoS) attacks.\n * @property {RegExp} JWT_FORMAT_RE - The regex pattern used to validate the format of the JWT access token.\n *\n * @throws {void} - Does not explicitly throw exceptions but logs warnings when provided cookies exceed the size limit or contain invalid tokens.\n *\n * @example\n * A valid access token will be retrieved if it exists as 'access_token' in the cookie or 'credentials_token' in dev mode.\n * If the cookie string exceeds MAX_COOKIE_LENGTH or the token is misformatted, the returned value will be null.\n */\nexport const getAccessToken = (cookie?: string): string | null => {\n if (!cookie || typeof cookie !== 'string') {\n return null;\n }\n\n // Limit cookie header length to prevent DoS\n if (cookie.length > 8192) {\n console.warn('[auth] Cookie header exceeds maximum length');\n return null;\n }\n\n const cookies = cookie ? parse(cookie) : {};\n\n let accessToken = cookies.access_token ?? null;\n // in development mode we support \"credentials login\"\n if (!accessToken && process.env.NODE_ENV === 'development') {\n // NOTE: This cookie can only be accessed from the client side\n // in development mode. Otherwise, the cookie is set as httpOnly\n accessToken = cookies.credentials_token ?? null;\n }\n\n // Validate token format before returning\n if (accessToken) {\n const JWT_FORMAT_RE = /^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/;\n if (!JWT_FORMAT_RE.test(accessToken)) {\n console.warn('[auth] Invalid token format in cookie');\n return null;\n }\n }\n\n return accessToken;\n};\n","import type { NextApiResponse } from 'next';\nimport { errors as joseErrors } from 'jose';\nimport { isFetchError } from '@gen3/core/server';\n\nconst HTTP_INTERNAL_ERROR = 500;\nconst HTTP_NOT_AUTHORIZED = 401;\n\n/**\n * An object that contains predefined error messages for handling JWT-related issues.\n *\n * Properties:\n * - TOKEN_EXPIRED: Error message indicating that the JSON Web Token (JWT) has expired.\n * - TOKEN_INVALID: Error message specifying that the JWT is invalid.\n * - TOKEN_SIGNATURE_INVALID: Error message for when the JWT signature cannot be verified.\n * - JWKS_NO_MATCHING_KEY: Error message indicating that no matching key was found in JWKS\n * (JSON Web Key Set) for JWT verification.\n * - UNKNOWN: General error message for unspecified or unknown errors.\n *\n */\nconst ERRORS = {\n TOKEN_EXPIRED: 'The JWT has expired.',\n TOKEN_INVALID: 'The JWT is invalid.',\n TOKEN_SIGNATURE_INVALID: 'The JWT signature is invalid.',\n JWKS_NO_MATCHING_KEY: 'No matching key found in JWKS for JWT verification.',\n UNKNOWN: 'Unknown error',\n};\n\n/**\n * Handles and returns an appropriate error response based on the provided error.\n *\n * @param {unknown} error - The error object which could be of various types such as fetch errors, JWT errors, or generic errors.\n * @param {NextApiResponse} res - The API response object used to send the error response.\n * @returns {NextApiResponse} - The response object containing the appropriate error status and message.\n *\n * This function evaluates the type of error passed to it and maps these errors to suitable\n * HTTP status codes and human-readable messages. It supports specific cases such as:\n * - Fetch errors.\n * - JSON Web Token (JWT) related errors (expired tokens, invalid tokens, signature issues, missing matching keys).\n * - Standard JavaScript errors.\n * - Unknown errors where it defaults to a generic unknown error message.\n *\n * The response will always include the error status and a message describing the error.\n */\nexport const getWebTokenErrorResponse = (\n error: unknown,\n res: NextApiResponse,\n): NextApiResponse => {\n if (isFetchError(error)) {\n res.status(error.status).json({ message: error.statusText });\n return res;\n }\n\n if (error instanceof joseErrors.JWTExpired) {\n res.status(HTTP_NOT_AUTHORIZED).json({ message: ERRORS.TOKEN_EXPIRED });\n return res;\n }\n if (error instanceof joseErrors.JWTInvalid) {\n res.status(HTTP_INTERNAL_ERROR).json({ message: ERRORS.TOKEN_INVALID });\n return res;\n }\n if (error instanceof joseErrors.JWSInvalid) {\n res\n .status(HTTP_INTERNAL_ERROR)\n .json({ message: ERRORS.TOKEN_SIGNATURE_INVALID });\n return res;\n }\n if (error instanceof joseErrors.JWKSNoMatchingKey) {\n res\n .status(HTTP_INTERNAL_ERROR)\n .json({ message: ERRORS.JWKS_NO_MATCHING_KEY });\n return res;\n }\n\n if (error instanceof Error) {\n res.status(HTTP_INTERNAL_ERROR).json({ message: error.message });\n return res;\n }\n\n res.status(HTTP_INTERNAL_ERROR).json({ message: ERRORS.UNKNOWN });\n return res;\n};\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport { parse } from 'cookie';\nimport type { JWTPayload } from 'jose';\nimport { decodeJwt, errors as joseErrors, importSPKI, jwtVerify } from 'jose';\nimport { fetchJWTKey } from '../../lib/auth/utils';\nimport { getWebTokenErrorResponse } from './errorHandler';\n\nexport interface JWTPayloadAndUser extends JWTPayload {\n /**\n * Fence puts the user in here. Optional because nothing in the JWT spec\n * requires it: a token signed by the same key without one would otherwise\n * crash the handler on a property access.\n */\n context?: Record<string, string>;\n}\n\n/**\n * Claims the client needs from a token whose signature has already been verified.\n */\nconst tokenClaims = (accessToken: string) => {\n const decoded = decodeJwt(accessToken) as JWTPayloadAndUser;\n return {\n issued: decoded.iat,\n expires: decoded.exp,\n userContext: decoded.context?.user,\n };\n};\n\n/**\n * returns the access_token expiration, user, and status\n *\n * Every token state this endpoint can determine — issued, expired, invalid,\n * not present — is reported as a 200 with an explicit `status`. Only a failure\n * to determine the state at all (no JWKS key, an unreachable key source, a\n * malformed request) is an error response, so the client can tell \"your token is\n * expired\" from \"I could not find out\" and stop retrying the former.\n * @param req\n * @param res\n */\nexport default async function handler(\n req: NextApiRequest,\n res: NextApiResponse,\n): Promise<void> {\n try {\n const cookies = req.headers.cookie ? parse(req.headers.cookie) : {};\n let accessToken = cookies.access_token;\n\n // in development mode we support \"credentials login\"\n if (!accessToken && process.env.NODE_ENV === 'development') {\n // NOTE: This cookie can only be accessed from the client side\n // in development mode. Otherwise, the cookie is set as httpOnly\n accessToken = cookies.credentials_token;\n }\n\n if (accessToken) {\n const jwtKey = await fetchJWTKey(process.env.NODE_ENV === 'production');\n if (!jwtKey) {\n res.status(500).json({\n message: 'No JWT Key to verify token',\n });\n return;\n }\n // validate the token\n const publicKey = await importSPKI(jwtKey, 'RS256');\n try {\n await jwtVerify(accessToken, publicKey);\n } catch (error: unknown) {\n // jwtVerify checks the signature before the claims, so an expired token\n // has already proven its signature, and its claims can be reported. This\n // has to be caught here: letting it reach the error handler would turn a\n // definitive \"expired\" into an opaque 401.\n if (error instanceof joseErrors.JWTExpired) {\n res.status(200).json({\n ...tokenClaims(accessToken),\n status: 'expired',\n });\n return;\n }\n throw error;\n }\n\n const claims = tokenClaims(accessToken);\n res.status(200).json({\n ...claims,\n // A token with no `exp` never expires, which we will not honour\n status: claims.expires ? 'issued' : 'invalid',\n });\n return;\n }\n\n res.status(200).json({\n status: 'not present',\n });\n } catch (error: unknown) {\n getWebTokenErrorResponse(error, res); // will update the res object with error\n }\n}\n"],"names":["myGlob","dir","filter","files","fs","readdirSync","file","search","error","FilesystemContent","rootPath","get","filepath","JSON","parse","readFileSync","path","join","toString","Error","getAll","Promise","all","map","ContentDatabase","config","store","GEN3_FRONTEND_CONFIGURATION_ROOT","process","env","ContentSource","instance","contentDatabase","getInstance","getContentDatabase","DefaultAuthorizedRoutesConfig","routes","loginRequired","fetchJWTKey","useService","response","fetchFence","endpoint","isJSON","status","data","keys","length","getAccessToken","cookie","console","warn","cookies","accessToken","access_token","NODE_ENV","credentials_token","JWT_FORMAT_RE","test","HTTP_INTERNAL_ERROR","HTTP_NOT_AUTHORIZED","ERRORS","TOKEN_EXPIRED","TOKEN_INVALID","TOKEN_SIGNATURE_INVALID","JWKS_NO_MATCHING_KEY","UNKNOWN","getWebTokenErrorResponse","res","isFetchError","json","message","statusText","joseErrors","JWTExpired","JWTInvalid","JWSInvalid","JWKSNoMatchingKey","tokenClaims","decoded","decodeJwt","issued","iat","expires","exp","userContext","context","user","handler","req","headers","jwtKey","publicKey","importSPKI","jwtVerify","claims"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.js","sources":["../../../src/lib/content/filesystem.ts","../../../src/lib/content/ContentDatabase.ts","../../../src/lib/content/constants.ts","../../../src/lib/content/index.ts","../../../src/lib/authz/type.ts","../../../src/lib/auth/utils.ts","../../../src/api/auth/errorHandler.ts","../../../src/api/auth/sessionToken.ts","../../../src/api/auth/sessionLogout.ts"],"sourcesContent":["import { ContentSourceInterface } from './types';\nimport fs from 'fs';\nimport path from 'path';\n\nconst myGlob = (dir: string, filter: string) => {\n try {\n const files = fs.readdirSync(dir);\n return files.filter((file) => file.search(filter) !== -1);\n } catch (error: any) {}\n return [];\n};\n\nexport class FilesystemContent implements ContentSourceInterface {\n rootPath: string;\n constructor({ rootPath }: { rootPath?: string }) {\n this.rootPath = rootPath || '';\n }\n\n public async get<T extends Record<string, any>>(\n filepath: string,\n ): Promise<T> {\n try {\n return await JSON.parse(\n fs.readFileSync(path.join(this.rootPath, filepath)).toString('utf8'),\n );\n } catch {\n throw new Error(`Cannot process ${path.join(this.rootPath, filepath)} `);\n }\n }\n public async getAll<T extends Record<string, any>>(\n filepath: string,\n filter: string,\n ): Promise<Array<T>> {\n try {\n const files = myGlob(path.join(this.rootPath, filepath), filter);\n return Promise.all(\n files.map((file) => this.get<T>(path.join(filepath, file))),\n );\n } catch {\n throw new Error(\n `getAllCannot process ${path.join(this.rootPath, filepath)}/${filter}`,\n );\n }\n }\n}\n","import { ContentSourceInterface } from './types';\n\n/**\n * The Content management \"datastore\" which is an abstracted interface to\n * some persistent storage of content needed for Gen3's content management system\n */\n\nexport interface CreateDatabase {\n store: ContentSourceInterface;\n}\n\nexport class ContentDatabase {\n public store: ContentSourceInterface;\n\n constructor(public config: CreateDatabase) {\n this.store = config.store;\n }\n\n public async get<T extends Record<string, any>>(\n filepath: string,\n ): Promise<T> {\n return this.store.get(filepath);\n }\n\n public async getAll<T extends Record<string, any>>(\n filepath: string,\n filter: string,\n ): Promise<Array<T>> {\n // Array<Promise<T>>\n return this.store.getAll(filepath, filter);\n }\n}\n","export const GEN3_FRONTEND_CONFIGURATION_ROOT =\n process.env.GEN3_FRONTEND_CONFIGURATION_ROOT || './config/';\n","import { FilesystemContent } from './filesystem';\nimport { ContentDatabase } from './ContentDatabase';\nimport { GEN3_FRONTEND_CONFIGURATION_ROOT } from './constants';\n\nexport class ContentSource {\n private static instance: ContentSource | null = null;\n private contentDatabase: ContentDatabase;\n\n private constructor() {\n const config = {\n store: new FilesystemContent({\n rootPath: GEN3_FRONTEND_CONFIGURATION_ROOT,\n }),\n };\n this.contentDatabase = new ContentDatabase(config);\n }\n\n public static getInstance(): ContentSource {\n if (!ContentSource.instance) {\n ContentSource.instance = new ContentSource();\n }\n return ContentSource.instance;\n }\n\n // Add methods to access the ContentDatabase functionality\n // For example:\n public getContentDatabase(): ContentDatabase {\n return this.contentDatabase;\n }\n\n // ✅ Compatibility helper: allow `ContentSource.getContentDatabase()`\n public static getContentDatabase(): ContentDatabase {\n return ContentSource.getInstance().getContentDatabase();\n }\n}\n\n// Export a singleton instance\nexport default ContentSource.getInstance();\n","export type RouteRule = {\n loginRequired?: boolean;\n authz?: string[];\n};\n\nexport type RouteConfig = Record<string, RouteRule>;\n\nexport interface AuthorizedRoutesConfig {\n routes: RouteConfig;\n}\n\nexport const DefaultAuthorizedRoutesConfig = {\n routes: {\n '/DataLibrary': {\n loginRequired: true,\n },\n '/Workspace': {\n loginRequired: true,\n },\n '/Profile': {\n loginRequired: true,\n },\n '*': {\n loginRequired: false,\n },\n },\n};\n","import { fetchFence } from '@gen3/core/server';\nimport { parse } from 'cookie';\n\ninterface Gen3JTWKeys {\n keys: string[];\n}\n\n/**\n * Fetches a JSON Web Token (JWT) key from the specified endpoint.\n *\n * This asynchronous function interacts with the Gen3 Fence API to retrieve JWT keys.\n * If the response status is not 200 or the expected key format is not found, the function\n * will return null. Otherwise, it returns the first available key.\n *\n * @function\n * @async\n * @returns {Promise<string|null>} A Promise resolving to the JWT key as a string if available, or null if not.\n */\nexport const fetchJWTKey = async (useService: boolean = false) => {\n const response = await fetchFence<Gen3JTWKeys>(\n {\n endpoint: '/jwt/keys',\n isJSON: true,\n },\n useService,\n );\n if (response.status !== 200) {\n return null;\n }\n\n if (response?.data?.keys.length && response?.data?.keys[0].length > 1) {\n return response.data.keys[0][1];\n }\n return null;\n};\n\n/**\n * Extracts and validates the access token from the provided cookie string.\n *\n * This function parses the cookie string to retrieve an access token, while ensuring\n * its format and size meet expected criteria. It also supports \"credentials login\" in\n * development mode to retrieve a `credentials_token` as a fallback.\n *\n * @param {string} [cookie] - The cookie string containing token information. Optional.\n * @returns {string|null} The valid access token if present and correctly formatted, or `null` if unavailable, invalid, or inappropriate.\n *\n * @property {number} MAX_COOKIE_LENGTH - The maximum allowed length for the cookie string to prevent potential denial-of-service (DoS) attacks.\n * @property {RegExp} JWT_FORMAT_RE - The regex pattern used to validate the format of the JWT access token.\n *\n * @throws {void} - Does not explicitly throw exceptions but logs warnings when provided cookies exceed the size limit or contain invalid tokens.\n *\n * @example\n * A valid access token will be retrieved if it exists as 'access_token' in the cookie or 'credentials_token' in dev mode.\n * If the cookie string exceeds MAX_COOKIE_LENGTH or the token is misformatted, the returned value will be null.\n */\nexport const getAccessToken = (cookie?: string): string | null => {\n if (!cookie || typeof cookie !== 'string') {\n return null;\n }\n\n // Limit cookie header length to prevent DoS\n if (cookie.length > 8192) {\n console.warn('[auth] Cookie header exceeds maximum length');\n return null;\n }\n\n const cookies = cookie ? parse(cookie) : {};\n\n let accessToken = cookies.access_token ?? null;\n // in development mode we support \"credentials login\"\n if (!accessToken && process.env.NODE_ENV === 'development') {\n // NOTE: This cookie can only be accessed from the client side\n // in development mode. Otherwise, the cookie is set as httpOnly\n accessToken = cookies.credentials_token ?? null;\n }\n\n // Validate token format before returning\n if (accessToken) {\n const JWT_FORMAT_RE = /^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/;\n if (!JWT_FORMAT_RE.test(accessToken)) {\n console.warn('[auth] Invalid token format in cookie');\n return null;\n }\n }\n\n return accessToken;\n};\n","import type { NextApiResponse } from 'next';\nimport { errors as joseErrors } from 'jose';\nimport { isFetchError } from '@gen3/core/server';\n\nconst HTTP_INTERNAL_ERROR = 500;\nconst HTTP_NOT_AUTHORIZED = 401;\n\n/**\n * An object that contains predefined error messages for handling JWT-related issues.\n *\n * Properties:\n * - TOKEN_EXPIRED: Error message indicating that the JSON Web Token (JWT) has expired.\n * - TOKEN_INVALID: Error message specifying that the JWT is invalid.\n * - TOKEN_SIGNATURE_INVALID: Error message for when the JWT signature cannot be verified.\n * - JWKS_NO_MATCHING_KEY: Error message indicating that no matching key was found in JWKS\n * (JSON Web Key Set) for JWT verification.\n * - UNKNOWN: General error message for unspecified or unknown errors.\n *\n */\nconst ERRORS = {\n TOKEN_EXPIRED: 'The JWT has expired.',\n TOKEN_INVALID: 'The JWT is invalid.',\n TOKEN_SIGNATURE_INVALID: 'The JWT signature is invalid.',\n JWKS_NO_MATCHING_KEY: 'No matching key found in JWKS for JWT verification.',\n UNKNOWN: 'Unknown error',\n};\n\n/**\n * Handles and returns an appropriate error response based on the provided error.\n *\n * @param {unknown} error - The error object which could be of various types such as fetch errors, JWT errors, or generic errors.\n * @param {NextApiResponse} res - The API response object used to send the error response.\n * @returns {NextApiResponse} - The response object containing the appropriate error status and message.\n *\n * This function evaluates the type of error passed to it and maps these errors to suitable\n * HTTP status codes and human-readable messages. It supports specific cases such as:\n * - Fetch errors.\n * - JSON Web Token (JWT) related errors (expired tokens, invalid tokens, signature issues, missing matching keys).\n * - Standard JavaScript errors.\n * - Unknown errors where it defaults to a generic unknown error message.\n *\n * The response will always include the error status and a message describing the error.\n */\nexport const getWebTokenErrorResponse = (\n error: unknown,\n res: NextApiResponse,\n): NextApiResponse => {\n if (isFetchError(error)) {\n res.status(error.status).json({ message: error.statusText });\n return res;\n }\n\n if (error instanceof joseErrors.JWTExpired) {\n res.status(HTTP_NOT_AUTHORIZED).json({ message: ERRORS.TOKEN_EXPIRED });\n return res;\n }\n if (error instanceof joseErrors.JWTInvalid) {\n res.status(HTTP_INTERNAL_ERROR).json({ message: ERRORS.TOKEN_INVALID });\n return res;\n }\n if (error instanceof joseErrors.JWSInvalid) {\n res\n .status(HTTP_INTERNAL_ERROR)\n .json({ message: ERRORS.TOKEN_SIGNATURE_INVALID });\n return res;\n }\n if (error instanceof joseErrors.JWKSNoMatchingKey) {\n res\n .status(HTTP_INTERNAL_ERROR)\n .json({ message: ERRORS.JWKS_NO_MATCHING_KEY });\n return res;\n }\n\n if (error instanceof Error) {\n res.status(HTTP_INTERNAL_ERROR).json({ message: error.message });\n return res;\n }\n\n res.status(HTTP_INTERNAL_ERROR).json({ message: ERRORS.UNKNOWN });\n return res;\n};\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport { parse } from 'cookie';\nimport type { JWTPayload } from 'jose';\nimport { decodeJwt, errors as joseErrors, importSPKI, jwtVerify } from 'jose';\nimport { fetchJWTKey } from '../../lib/auth/utils';\nimport { getWebTokenErrorResponse } from './errorHandler';\n\nexport interface JWTPayloadAndUser extends JWTPayload {\n /**\n * Fence puts the user in here. Optional because nothing in the JWT spec\n * requires it: a token signed by the same key without one would otherwise\n * crash the handler on a property access.\n */\n context?: Record<string, string>;\n}\n\n/**\n * Claims the client needs from a token whose signature has already been verified.\n */\nconst tokenClaims = (accessToken: string) => {\n const decoded = decodeJwt(accessToken) as JWTPayloadAndUser;\n return {\n issued: decoded.iat,\n expires: decoded.exp,\n userContext: decoded.context?.user,\n };\n};\n\n/**\n * returns the access_token expiration, user, and status\n *\n * Every token state this endpoint can determine — issued, expired, invalid,\n * not present — is reported as a 200 with an explicit `status`. Only a failure\n * to determine the state at all (no JWKS key, an unreachable key source, a\n * malformed request) is an error response, so the client can tell \"your token is\n * expired\" from \"I could not find out\" and stop retrying the former.\n * @param req\n * @param res\n */\nexport default async function handler(\n req: NextApiRequest,\n res: NextApiResponse,\n): Promise<void> {\n try {\n const cookies = req.headers.cookie ? parse(req.headers.cookie) : {};\n let accessToken = cookies.access_token;\n\n // in development mode we support \"credentials login\"\n if (!accessToken && process.env.NODE_ENV === 'development') {\n // NOTE: This cookie can only be accessed from the client side\n // in development mode. Otherwise, the cookie is set as httpOnly\n accessToken = cookies.credentials_token;\n }\n\n if (accessToken) {\n const jwtKey = await fetchJWTKey(process.env.NODE_ENV === 'production');\n if (!jwtKey) {\n res.status(500).json({\n message: 'No JWT Key to verify token',\n });\n return;\n }\n // validate the token\n const publicKey = await importSPKI(jwtKey, 'RS256');\n try {\n await jwtVerify(accessToken, publicKey);\n } catch (error: unknown) {\n // jwtVerify checks the signature before the claims, so an expired token\n // has already proven its signature, and its claims can be reported. This\n // has to be caught here: letting it reach the error handler would turn a\n // definitive \"expired\" into an opaque 401.\n if (error instanceof joseErrors.JWTExpired) {\n res.status(200).json({\n ...tokenClaims(accessToken),\n status: 'expired',\n });\n return;\n }\n throw error;\n }\n\n const claims = tokenClaims(accessToken);\n res.status(200).json({\n ...claims,\n // A token with no `exp` never expires, which we will not honour\n status: claims.expires ? 'issued' : 'invalid',\n });\n return;\n }\n\n res.status(200).json({\n status: 'not present',\n });\n } catch (error: unknown) {\n getWebTokenErrorResponse(error, res); // will update the res object with error\n }\n}\n","import type { NextApiRequest, NextApiResponse } from 'next';\n\nimport { GEN3_FENCE_API, GEN3_FENCE_SERVICE } from '@gen3/core/server';\nimport { serialize } from 'cookie';\n\nexport default async function (req: NextApiRequest, res: NextApiResponse) {\n if (req.method !== 'POST') {\n res.setHeader('Allow', 'POST');\n res.status(405).json({ error: 'Method not allowed' });\n return;\n }\n\n const FENCE_API =\n process.env.NODE_ENV === 'development'\n ? GEN3_FENCE_API\n : GEN3_FENCE_SERVICE;\n\n try {\n const fenceCookies = [\n req.cookies.fence && `fence=${encodeURIComponent(req.cookies.fence)}`,\n req.cookies.access_token &&\n `access_token=${encodeURIComponent(req.cookies.access_token)}`,\n ].filter((cookie): cookie is string => Boolean(cookie));\n\n const fenceResponse = await fetch(`${FENCE_API}/logout`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'text/html',\n ...(fenceCookies.length > 0 ? { Cookie: fenceCookies.join('; ') } : {}),\n },\n redirect: 'manual',\n });\n\n if (!fenceResponse.ok) {\n console.warn(`Fence logout failed (${fenceResponse.status})`);\n }\n } catch (error: unknown) {\n // GEN3_FENCE_SERVICE is an internal, cluster-only URL. Some deployments\n // (e.g. local dev, or a frontend that isn't network-adjacent to Fence)\n // cannot reach it at all — that must not block logout, since the part\n // that matters to this app is clearing its own cookies below.\n console.warn('Unable to reach Fence to complete logout', error);\n }\n\n // Clear local authentication regardless of whether Fence could be reached.\n // This is what actually logs the user out of this app; telling Fence is\n // best-effort on top of it.\n res.setHeader('Set-Cookie', [\n serialize('fence', '', {\n sameSite: 'lax',\n httpOnly: true,\n secure: true,\n path: '/',\n expires: new Date(0),\n }),\n serialize('access_token', '', {\n sameSite: 'lax',\n httpOnly: true,\n secure: true,\n path: '/',\n expires: new Date(0),\n }),\n // delete the credentials_token cookie for credentials login\n serialize('credentials_token', '', {\n sameSite: 'lax',\n httpOnly: process.env.NODE_ENV === 'production',\n secure: process.env.NODE_ENV === 'production',\n path: '/',\n expires: new Date(0),\n }),\n ]);\n\n res.status(200).json({ success: 'success' });\n}\n"],"names":["myGlob","dir","filter","files","fs","readdirSync","file","search","error","FilesystemContent","rootPath","get","filepath","JSON","parse","readFileSync","path","join","toString","Error","getAll","Promise","all","map","ContentDatabase","config","store","GEN3_FRONTEND_CONFIGURATION_ROOT","process","env","ContentSource","instance","contentDatabase","getInstance","getContentDatabase","DefaultAuthorizedRoutesConfig","routes","loginRequired","fetchJWTKey","useService","response","fetchFence","endpoint","isJSON","status","data","keys","length","getAccessToken","cookie","console","warn","cookies","accessToken","access_token","NODE_ENV","credentials_token","JWT_FORMAT_RE","test","HTTP_INTERNAL_ERROR","HTTP_NOT_AUTHORIZED","ERRORS","TOKEN_EXPIRED","TOKEN_INVALID","TOKEN_SIGNATURE_INVALID","JWKS_NO_MATCHING_KEY","UNKNOWN","getWebTokenErrorResponse","res","isFetchError","json","message","statusText","joseErrors","JWTExpired","JWTInvalid","JWSInvalid","JWKSNoMatchingKey","tokenClaims","decoded","decodeJwt","issued","iat","expires","exp","userContext","context","user","handler","req","headers","jwtKey","publicKey","importSPKI","jwtVerify","claims","method","setHeader","FENCE_API","GEN3_FENCE_API","GEN3_FENCE_SERVICE","fenceCookies","fence","encodeURIComponent","Boolean","fenceResponse","fetch","Cookie","redirect","ok","serialize","sameSite","httpOnly","secure","Date","success"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { fetchFence, isFetchError } from '@gen3/core/server';
|
|
4
|
-
import { parse } from 'cookie';
|
|
3
|
+
import { fetchFence, isFetchError, GEN3_FENCE_API, GEN3_FENCE_SERVICE } from '@gen3/core/server';
|
|
4
|
+
import { parse, serialize } from 'cookie';
|
|
5
5
|
import { errors, importSPKI, jwtVerify, decodeJwt } from 'jose';
|
|
6
6
|
|
|
7
7
|
const myGlob = (dir, filter)=>{
|
|
@@ -315,5 +315,71 @@ const HTTP_NOT_AUTHORIZED = 401;
|
|
|
315
315
|
}
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
-
|
|
318
|
+
async function sessionLogout(req, res) {
|
|
319
|
+
if (req.method !== 'POST') {
|
|
320
|
+
res.setHeader('Allow', 'POST');
|
|
321
|
+
res.status(405).json({
|
|
322
|
+
error: 'Method not allowed'
|
|
323
|
+
});
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const FENCE_API = process.env.NODE_ENV === 'development' ? GEN3_FENCE_API : GEN3_FENCE_SERVICE;
|
|
327
|
+
try {
|
|
328
|
+
const fenceCookies = [
|
|
329
|
+
req.cookies.fence && `fence=${encodeURIComponent(req.cookies.fence)}`,
|
|
330
|
+
req.cookies.access_token && `access_token=${encodeURIComponent(req.cookies.access_token)}`
|
|
331
|
+
].filter((cookie)=>Boolean(cookie));
|
|
332
|
+
const fenceResponse = await fetch(`${FENCE_API}/logout`, {
|
|
333
|
+
method: 'GET',
|
|
334
|
+
headers: {
|
|
335
|
+
'Content-Type': 'text/html',
|
|
336
|
+
...fenceCookies.length > 0 ? {
|
|
337
|
+
Cookie: fenceCookies.join('; ')
|
|
338
|
+
} : {}
|
|
339
|
+
},
|
|
340
|
+
redirect: 'manual'
|
|
341
|
+
});
|
|
342
|
+
if (!fenceResponse.ok) {
|
|
343
|
+
console.warn(`Fence logout failed (${fenceResponse.status})`);
|
|
344
|
+
}
|
|
345
|
+
} catch (error) {
|
|
346
|
+
// GEN3_FENCE_SERVICE is an internal, cluster-only URL. Some deployments
|
|
347
|
+
// (e.g. local dev, or a frontend that isn't network-adjacent to Fence)
|
|
348
|
+
// cannot reach it at all — that must not block logout, since the part
|
|
349
|
+
// that matters to this app is clearing its own cookies below.
|
|
350
|
+
console.warn('Unable to reach Fence to complete logout', error);
|
|
351
|
+
}
|
|
352
|
+
// Clear local authentication regardless of whether Fence could be reached.
|
|
353
|
+
// This is what actually logs the user out of this app; telling Fence is
|
|
354
|
+
// best-effort on top of it.
|
|
355
|
+
res.setHeader('Set-Cookie', [
|
|
356
|
+
serialize('fence', '', {
|
|
357
|
+
sameSite: 'lax',
|
|
358
|
+
httpOnly: true,
|
|
359
|
+
secure: true,
|
|
360
|
+
path: '/',
|
|
361
|
+
expires: new Date(0)
|
|
362
|
+
}),
|
|
363
|
+
serialize('access_token', '', {
|
|
364
|
+
sameSite: 'lax',
|
|
365
|
+
httpOnly: true,
|
|
366
|
+
secure: true,
|
|
367
|
+
path: '/',
|
|
368
|
+
expires: new Date(0)
|
|
369
|
+
}),
|
|
370
|
+
// delete the credentials_token cookie for credentials login
|
|
371
|
+
serialize('credentials_token', '', {
|
|
372
|
+
sameSite: 'lax',
|
|
373
|
+
httpOnly: process.env.NODE_ENV === 'production',
|
|
374
|
+
secure: process.env.NODE_ENV === 'production',
|
|
375
|
+
path: '/',
|
|
376
|
+
expires: new Date(0)
|
|
377
|
+
})
|
|
378
|
+
]);
|
|
379
|
+
res.status(200).json({
|
|
380
|
+
success: 'success'
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export { index as ContentSource, DefaultAuthorizedRoutesConfig, fetchJWTKey, getAccessToken, sessionLogout, handler as sessionToken };
|
|
319
385
|
//# sourceMappingURL=server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sources":["../../../src/lib/content/filesystem.ts","../../../src/lib/content/ContentDatabase.ts","../../../src/lib/content/constants.ts","../../../src/lib/content/index.ts","../../../src/lib/authz/type.ts","../../../src/lib/auth/utils.ts","../../../src/api/auth/errorHandler.ts","../../../src/api/auth/sessionToken.ts"],"sourcesContent":["import { ContentSourceInterface } from './types';\nimport fs from 'fs';\nimport path from 'path';\n\nconst myGlob = (dir: string, filter: string) => {\n try {\n const files = fs.readdirSync(dir);\n return files.filter((file) => file.search(filter) !== -1);\n } catch (error: any) {}\n return [];\n};\n\nexport class FilesystemContent implements ContentSourceInterface {\n rootPath: string;\n constructor({ rootPath }: { rootPath?: string }) {\n this.rootPath = rootPath || '';\n }\n\n public async get<T extends Record<string, any>>(\n filepath: string,\n ): Promise<T> {\n try {\n return await JSON.parse(\n fs.readFileSync(path.join(this.rootPath, filepath)).toString('utf8'),\n );\n } catch {\n throw new Error(`Cannot process ${path.join(this.rootPath, filepath)} `);\n }\n }\n public async getAll<T extends Record<string, any>>(\n filepath: string,\n filter: string,\n ): Promise<Array<T>> {\n try {\n const files = myGlob(path.join(this.rootPath, filepath), filter);\n return Promise.all(\n files.map((file) => this.get<T>(path.join(filepath, file))),\n );\n } catch {\n throw new Error(\n `getAllCannot process ${path.join(this.rootPath, filepath)}/${filter}`,\n );\n }\n }\n}\n","import { ContentSourceInterface } from './types';\n\n/**\n * The Content management \"datastore\" which is an abstracted interface to\n * some persistent storage of content needed for Gen3's content management system\n */\n\nexport interface CreateDatabase {\n store: ContentSourceInterface;\n}\n\nexport class ContentDatabase {\n public store: ContentSourceInterface;\n\n constructor(public config: CreateDatabase) {\n this.store = config.store;\n }\n\n public async get<T extends Record<string, any>>(\n filepath: string,\n ): Promise<T> {\n return this.store.get(filepath);\n }\n\n public async getAll<T extends Record<string, any>>(\n filepath: string,\n filter: string,\n ): Promise<Array<T>> {\n // Array<Promise<T>>\n return this.store.getAll(filepath, filter);\n }\n}\n","export const GEN3_FRONTEND_CONFIGURATION_ROOT =\n process.env.GEN3_FRONTEND_CONFIGURATION_ROOT || './config/';\n","import { FilesystemContent } from './filesystem';\nimport { ContentDatabase } from './ContentDatabase';\nimport { GEN3_FRONTEND_CONFIGURATION_ROOT } from './constants';\n\nexport class ContentSource {\n private static instance: ContentSource | null = null;\n private contentDatabase: ContentDatabase;\n\n private constructor() {\n const config = {\n store: new FilesystemContent({\n rootPath: GEN3_FRONTEND_CONFIGURATION_ROOT,\n }),\n };\n this.contentDatabase = new ContentDatabase(config);\n }\n\n public static getInstance(): ContentSource {\n if (!ContentSource.instance) {\n ContentSource.instance = new ContentSource();\n }\n return ContentSource.instance;\n }\n\n // Add methods to access the ContentDatabase functionality\n // For example:\n public getContentDatabase(): ContentDatabase {\n return this.contentDatabase;\n }\n\n // ✅ Compatibility helper: allow `ContentSource.getContentDatabase()`\n public static getContentDatabase(): ContentDatabase {\n return ContentSource.getInstance().getContentDatabase();\n }\n}\n\n// Export a singleton instance\nexport default ContentSource.getInstance();\n","export type RouteRule = {\n loginRequired?: boolean;\n authz?: string[];\n};\n\nexport type RouteConfig = Record<string, RouteRule>;\n\nexport interface AuthorizedRoutesConfig {\n routes: RouteConfig;\n}\n\nexport const DefaultAuthorizedRoutesConfig = {\n routes: {\n '/DataLibrary': {\n loginRequired: true,\n },\n '/Workspace': {\n loginRequired: true,\n },\n '/Profile': {\n loginRequired: true,\n },\n '*': {\n loginRequired: false,\n },\n },\n};\n","import { fetchFence } from '@gen3/core/server';\nimport { parse } from 'cookie';\n\ninterface Gen3JTWKeys {\n keys: string[];\n}\n\n/**\n * Fetches a JSON Web Token (JWT) key from the specified endpoint.\n *\n * This asynchronous function interacts with the Gen3 Fence API to retrieve JWT keys.\n * If the response status is not 200 or the expected key format is not found, the function\n * will return null. Otherwise, it returns the first available key.\n *\n * @function\n * @async\n * @returns {Promise<string|null>} A Promise resolving to the JWT key as a string if available, or null if not.\n */\nexport const fetchJWTKey = async (useService: boolean = false) => {\n const response = await fetchFence<Gen3JTWKeys>(\n {\n endpoint: '/jwt/keys',\n isJSON: true,\n },\n useService,\n );\n if (response.status !== 200) {\n return null;\n }\n\n if (response?.data?.keys.length && response?.data?.keys[0].length > 1) {\n return response.data.keys[0][1];\n }\n return null;\n};\n\n/**\n * Extracts and validates the access token from the provided cookie string.\n *\n * This function parses the cookie string to retrieve an access token, while ensuring\n * its format and size meet expected criteria. It also supports \"credentials login\" in\n * development mode to retrieve a `credentials_token` as a fallback.\n *\n * @param {string} [cookie] - The cookie string containing token information. Optional.\n * @returns {string|null} The valid access token if present and correctly formatted, or `null` if unavailable, invalid, or inappropriate.\n *\n * @property {number} MAX_COOKIE_LENGTH - The maximum allowed length for the cookie string to prevent potential denial-of-service (DoS) attacks.\n * @property {RegExp} JWT_FORMAT_RE - The regex pattern used to validate the format of the JWT access token.\n *\n * @throws {void} - Does not explicitly throw exceptions but logs warnings when provided cookies exceed the size limit or contain invalid tokens.\n *\n * @example\n * A valid access token will be retrieved if it exists as 'access_token' in the cookie or 'credentials_token' in dev mode.\n * If the cookie string exceeds MAX_COOKIE_LENGTH or the token is misformatted, the returned value will be null.\n */\nexport const getAccessToken = (cookie?: string): string | null => {\n if (!cookie || typeof cookie !== 'string') {\n return null;\n }\n\n // Limit cookie header length to prevent DoS\n if (cookie.length > 8192) {\n console.warn('[auth] Cookie header exceeds maximum length');\n return null;\n }\n\n const cookies = cookie ? parse(cookie) : {};\n\n let accessToken = cookies.access_token ?? null;\n // in development mode we support \"credentials login\"\n if (!accessToken && process.env.NODE_ENV === 'development') {\n // NOTE: This cookie can only be accessed from the client side\n // in development mode. Otherwise, the cookie is set as httpOnly\n accessToken = cookies.credentials_token ?? null;\n }\n\n // Validate token format before returning\n if (accessToken) {\n const JWT_FORMAT_RE = /^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/;\n if (!JWT_FORMAT_RE.test(accessToken)) {\n console.warn('[auth] Invalid token format in cookie');\n return null;\n }\n }\n\n return accessToken;\n};\n","import type { NextApiResponse } from 'next';\nimport { errors as joseErrors } from 'jose';\nimport { isFetchError } from '@gen3/core/server';\n\nconst HTTP_INTERNAL_ERROR = 500;\nconst HTTP_NOT_AUTHORIZED = 401;\n\n/**\n * An object that contains predefined error messages for handling JWT-related issues.\n *\n * Properties:\n * - TOKEN_EXPIRED: Error message indicating that the JSON Web Token (JWT) has expired.\n * - TOKEN_INVALID: Error message specifying that the JWT is invalid.\n * - TOKEN_SIGNATURE_INVALID: Error message for when the JWT signature cannot be verified.\n * - JWKS_NO_MATCHING_KEY: Error message indicating that no matching key was found in JWKS\n * (JSON Web Key Set) for JWT verification.\n * - UNKNOWN: General error message for unspecified or unknown errors.\n *\n */\nconst ERRORS = {\n TOKEN_EXPIRED: 'The JWT has expired.',\n TOKEN_INVALID: 'The JWT is invalid.',\n TOKEN_SIGNATURE_INVALID: 'The JWT signature is invalid.',\n JWKS_NO_MATCHING_KEY: 'No matching key found in JWKS for JWT verification.',\n UNKNOWN: 'Unknown error',\n};\n\n/**\n * Handles and returns an appropriate error response based on the provided error.\n *\n * @param {unknown} error - The error object which could be of various types such as fetch errors, JWT errors, or generic errors.\n * @param {NextApiResponse} res - The API response object used to send the error response.\n * @returns {NextApiResponse} - The response object containing the appropriate error status and message.\n *\n * This function evaluates the type of error passed to it and maps these errors to suitable\n * HTTP status codes and human-readable messages. It supports specific cases such as:\n * - Fetch errors.\n * - JSON Web Token (JWT) related errors (expired tokens, invalid tokens, signature issues, missing matching keys).\n * - Standard JavaScript errors.\n * - Unknown errors where it defaults to a generic unknown error message.\n *\n * The response will always include the error status and a message describing the error.\n */\nexport const getWebTokenErrorResponse = (\n error: unknown,\n res: NextApiResponse,\n): NextApiResponse => {\n if (isFetchError(error)) {\n res.status(error.status).json({ message: error.statusText });\n return res;\n }\n\n if (error instanceof joseErrors.JWTExpired) {\n res.status(HTTP_NOT_AUTHORIZED).json({ message: ERRORS.TOKEN_EXPIRED });\n return res;\n }\n if (error instanceof joseErrors.JWTInvalid) {\n res.status(HTTP_INTERNAL_ERROR).json({ message: ERRORS.TOKEN_INVALID });\n return res;\n }\n if (error instanceof joseErrors.JWSInvalid) {\n res\n .status(HTTP_INTERNAL_ERROR)\n .json({ message: ERRORS.TOKEN_SIGNATURE_INVALID });\n return res;\n }\n if (error instanceof joseErrors.JWKSNoMatchingKey) {\n res\n .status(HTTP_INTERNAL_ERROR)\n .json({ message: ERRORS.JWKS_NO_MATCHING_KEY });\n return res;\n }\n\n if (error instanceof Error) {\n res.status(HTTP_INTERNAL_ERROR).json({ message: error.message });\n return res;\n }\n\n res.status(HTTP_INTERNAL_ERROR).json({ message: ERRORS.UNKNOWN });\n return res;\n};\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport { parse } from 'cookie';\nimport type { JWTPayload } from 'jose';\nimport { decodeJwt, errors as joseErrors, importSPKI, jwtVerify } from 'jose';\nimport { fetchJWTKey } from '../../lib/auth/utils';\nimport { getWebTokenErrorResponse } from './errorHandler';\n\nexport interface JWTPayloadAndUser extends JWTPayload {\n /**\n * Fence puts the user in here. Optional because nothing in the JWT spec\n * requires it: a token signed by the same key without one would otherwise\n * crash the handler on a property access.\n */\n context?: Record<string, string>;\n}\n\n/**\n * Claims the client needs from a token whose signature has already been verified.\n */\nconst tokenClaims = (accessToken: string) => {\n const decoded = decodeJwt(accessToken) as JWTPayloadAndUser;\n return {\n issued: decoded.iat,\n expires: decoded.exp,\n userContext: decoded.context?.user,\n };\n};\n\n/**\n * returns the access_token expiration, user, and status\n *\n * Every token state this endpoint can determine — issued, expired, invalid,\n * not present — is reported as a 200 with an explicit `status`. Only a failure\n * to determine the state at all (no JWKS key, an unreachable key source, a\n * malformed request) is an error response, so the client can tell \"your token is\n * expired\" from \"I could not find out\" and stop retrying the former.\n * @param req\n * @param res\n */\nexport default async function handler(\n req: NextApiRequest,\n res: NextApiResponse,\n): Promise<void> {\n try {\n const cookies = req.headers.cookie ? parse(req.headers.cookie) : {};\n let accessToken = cookies.access_token;\n\n // in development mode we support \"credentials login\"\n if (!accessToken && process.env.NODE_ENV === 'development') {\n // NOTE: This cookie can only be accessed from the client side\n // in development mode. Otherwise, the cookie is set as httpOnly\n accessToken = cookies.credentials_token;\n }\n\n if (accessToken) {\n const jwtKey = await fetchJWTKey(process.env.NODE_ENV === 'production');\n if (!jwtKey) {\n res.status(500).json({\n message: 'No JWT Key to verify token',\n });\n return;\n }\n // validate the token\n const publicKey = await importSPKI(jwtKey, 'RS256');\n try {\n await jwtVerify(accessToken, publicKey);\n } catch (error: unknown) {\n // jwtVerify checks the signature before the claims, so an expired token\n // has already proven its signature, and its claims can be reported. This\n // has to be caught here: letting it reach the error handler would turn a\n // definitive \"expired\" into an opaque 401.\n if (error instanceof joseErrors.JWTExpired) {\n res.status(200).json({\n ...tokenClaims(accessToken),\n status: 'expired',\n });\n return;\n }\n throw error;\n }\n\n const claims = tokenClaims(accessToken);\n res.status(200).json({\n ...claims,\n // A token with no `exp` never expires, which we will not honour\n status: claims.expires ? 'issued' : 'invalid',\n });\n return;\n }\n\n res.status(200).json({\n status: 'not present',\n });\n } catch (error: unknown) {\n getWebTokenErrorResponse(error, res); // will update the res object with error\n }\n}\n"],"names":["myGlob","dir","filter","files","fs","readdirSync","file","search","error","FilesystemContent","rootPath","get","filepath","JSON","parse","readFileSync","path","join","toString","Error","getAll","Promise","all","map","ContentDatabase","config","store","GEN3_FRONTEND_CONFIGURATION_ROOT","process","env","ContentSource","instance","contentDatabase","getInstance","getContentDatabase","DefaultAuthorizedRoutesConfig","routes","loginRequired","fetchJWTKey","useService","response","fetchFence","endpoint","isJSON","status","data","keys","length","getAccessToken","cookie","console","warn","cookies","accessToken","access_token","NODE_ENV","credentials_token","JWT_FORMAT_RE","test","HTTP_INTERNAL_ERROR","HTTP_NOT_AUTHORIZED","ERRORS","TOKEN_EXPIRED","TOKEN_INVALID","TOKEN_SIGNATURE_INVALID","JWKS_NO_MATCHING_KEY","UNKNOWN","getWebTokenErrorResponse","res","isFetchError","json","message","statusText","joseErrors","JWTExpired","JWTInvalid","JWSInvalid","JWKSNoMatchingKey","tokenClaims","decoded","decodeJwt","issued","iat","expires","exp","userContext","context","user","handler","req","headers","jwtKey","publicKey","importSPKI","jwtVerify","claims"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.js","sources":["../../../src/lib/content/filesystem.ts","../../../src/lib/content/ContentDatabase.ts","../../../src/lib/content/constants.ts","../../../src/lib/content/index.ts","../../../src/lib/authz/type.ts","../../../src/lib/auth/utils.ts","../../../src/api/auth/errorHandler.ts","../../../src/api/auth/sessionToken.ts","../../../src/api/auth/sessionLogout.ts"],"sourcesContent":["import { ContentSourceInterface } from './types';\nimport fs from 'fs';\nimport path from 'path';\n\nconst myGlob = (dir: string, filter: string) => {\n try {\n const files = fs.readdirSync(dir);\n return files.filter((file) => file.search(filter) !== -1);\n } catch (error: any) {}\n return [];\n};\n\nexport class FilesystemContent implements ContentSourceInterface {\n rootPath: string;\n constructor({ rootPath }: { rootPath?: string }) {\n this.rootPath = rootPath || '';\n }\n\n public async get<T extends Record<string, any>>(\n filepath: string,\n ): Promise<T> {\n try {\n return await JSON.parse(\n fs.readFileSync(path.join(this.rootPath, filepath)).toString('utf8'),\n );\n } catch {\n throw new Error(`Cannot process ${path.join(this.rootPath, filepath)} `);\n }\n }\n public async getAll<T extends Record<string, any>>(\n filepath: string,\n filter: string,\n ): Promise<Array<T>> {\n try {\n const files = myGlob(path.join(this.rootPath, filepath), filter);\n return Promise.all(\n files.map((file) => this.get<T>(path.join(filepath, file))),\n );\n } catch {\n throw new Error(\n `getAllCannot process ${path.join(this.rootPath, filepath)}/${filter}`,\n );\n }\n }\n}\n","import { ContentSourceInterface } from './types';\n\n/**\n * The Content management \"datastore\" which is an abstracted interface to\n * some persistent storage of content needed for Gen3's content management system\n */\n\nexport interface CreateDatabase {\n store: ContentSourceInterface;\n}\n\nexport class ContentDatabase {\n public store: ContentSourceInterface;\n\n constructor(public config: CreateDatabase) {\n this.store = config.store;\n }\n\n public async get<T extends Record<string, any>>(\n filepath: string,\n ): Promise<T> {\n return this.store.get(filepath);\n }\n\n public async getAll<T extends Record<string, any>>(\n filepath: string,\n filter: string,\n ): Promise<Array<T>> {\n // Array<Promise<T>>\n return this.store.getAll(filepath, filter);\n }\n}\n","export const GEN3_FRONTEND_CONFIGURATION_ROOT =\n process.env.GEN3_FRONTEND_CONFIGURATION_ROOT || './config/';\n","import { FilesystemContent } from './filesystem';\nimport { ContentDatabase } from './ContentDatabase';\nimport { GEN3_FRONTEND_CONFIGURATION_ROOT } from './constants';\n\nexport class ContentSource {\n private static instance: ContentSource | null = null;\n private contentDatabase: ContentDatabase;\n\n private constructor() {\n const config = {\n store: new FilesystemContent({\n rootPath: GEN3_FRONTEND_CONFIGURATION_ROOT,\n }),\n };\n this.contentDatabase = new ContentDatabase(config);\n }\n\n public static getInstance(): ContentSource {\n if (!ContentSource.instance) {\n ContentSource.instance = new ContentSource();\n }\n return ContentSource.instance;\n }\n\n // Add methods to access the ContentDatabase functionality\n // For example:\n public getContentDatabase(): ContentDatabase {\n return this.contentDatabase;\n }\n\n // ✅ Compatibility helper: allow `ContentSource.getContentDatabase()`\n public static getContentDatabase(): ContentDatabase {\n return ContentSource.getInstance().getContentDatabase();\n }\n}\n\n// Export a singleton instance\nexport default ContentSource.getInstance();\n","export type RouteRule = {\n loginRequired?: boolean;\n authz?: string[];\n};\n\nexport type RouteConfig = Record<string, RouteRule>;\n\nexport interface AuthorizedRoutesConfig {\n routes: RouteConfig;\n}\n\nexport const DefaultAuthorizedRoutesConfig = {\n routes: {\n '/DataLibrary': {\n loginRequired: true,\n },\n '/Workspace': {\n loginRequired: true,\n },\n '/Profile': {\n loginRequired: true,\n },\n '*': {\n loginRequired: false,\n },\n },\n};\n","import { fetchFence } from '@gen3/core/server';\nimport { parse } from 'cookie';\n\ninterface Gen3JTWKeys {\n keys: string[];\n}\n\n/**\n * Fetches a JSON Web Token (JWT) key from the specified endpoint.\n *\n * This asynchronous function interacts with the Gen3 Fence API to retrieve JWT keys.\n * If the response status is not 200 or the expected key format is not found, the function\n * will return null. Otherwise, it returns the first available key.\n *\n * @function\n * @async\n * @returns {Promise<string|null>} A Promise resolving to the JWT key as a string if available, or null if not.\n */\nexport const fetchJWTKey = async (useService: boolean = false) => {\n const response = await fetchFence<Gen3JTWKeys>(\n {\n endpoint: '/jwt/keys',\n isJSON: true,\n },\n useService,\n );\n if (response.status !== 200) {\n return null;\n }\n\n if (response?.data?.keys.length && response?.data?.keys[0].length > 1) {\n return response.data.keys[0][1];\n }\n return null;\n};\n\n/**\n * Extracts and validates the access token from the provided cookie string.\n *\n * This function parses the cookie string to retrieve an access token, while ensuring\n * its format and size meet expected criteria. It also supports \"credentials login\" in\n * development mode to retrieve a `credentials_token` as a fallback.\n *\n * @param {string} [cookie] - The cookie string containing token information. Optional.\n * @returns {string|null} The valid access token if present and correctly formatted, or `null` if unavailable, invalid, or inappropriate.\n *\n * @property {number} MAX_COOKIE_LENGTH - The maximum allowed length for the cookie string to prevent potential denial-of-service (DoS) attacks.\n * @property {RegExp} JWT_FORMAT_RE - The regex pattern used to validate the format of the JWT access token.\n *\n * @throws {void} - Does not explicitly throw exceptions but logs warnings when provided cookies exceed the size limit or contain invalid tokens.\n *\n * @example\n * A valid access token will be retrieved if it exists as 'access_token' in the cookie or 'credentials_token' in dev mode.\n * If the cookie string exceeds MAX_COOKIE_LENGTH or the token is misformatted, the returned value will be null.\n */\nexport const getAccessToken = (cookie?: string): string | null => {\n if (!cookie || typeof cookie !== 'string') {\n return null;\n }\n\n // Limit cookie header length to prevent DoS\n if (cookie.length > 8192) {\n console.warn('[auth] Cookie header exceeds maximum length');\n return null;\n }\n\n const cookies = cookie ? parse(cookie) : {};\n\n let accessToken = cookies.access_token ?? null;\n // in development mode we support \"credentials login\"\n if (!accessToken && process.env.NODE_ENV === 'development') {\n // NOTE: This cookie can only be accessed from the client side\n // in development mode. Otherwise, the cookie is set as httpOnly\n accessToken = cookies.credentials_token ?? null;\n }\n\n // Validate token format before returning\n if (accessToken) {\n const JWT_FORMAT_RE = /^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/;\n if (!JWT_FORMAT_RE.test(accessToken)) {\n console.warn('[auth] Invalid token format in cookie');\n return null;\n }\n }\n\n return accessToken;\n};\n","import type { NextApiResponse } from 'next';\nimport { errors as joseErrors } from 'jose';\nimport { isFetchError } from '@gen3/core/server';\n\nconst HTTP_INTERNAL_ERROR = 500;\nconst HTTP_NOT_AUTHORIZED = 401;\n\n/**\n * An object that contains predefined error messages for handling JWT-related issues.\n *\n * Properties:\n * - TOKEN_EXPIRED: Error message indicating that the JSON Web Token (JWT) has expired.\n * - TOKEN_INVALID: Error message specifying that the JWT is invalid.\n * - TOKEN_SIGNATURE_INVALID: Error message for when the JWT signature cannot be verified.\n * - JWKS_NO_MATCHING_KEY: Error message indicating that no matching key was found in JWKS\n * (JSON Web Key Set) for JWT verification.\n * - UNKNOWN: General error message for unspecified or unknown errors.\n *\n */\nconst ERRORS = {\n TOKEN_EXPIRED: 'The JWT has expired.',\n TOKEN_INVALID: 'The JWT is invalid.',\n TOKEN_SIGNATURE_INVALID: 'The JWT signature is invalid.',\n JWKS_NO_MATCHING_KEY: 'No matching key found in JWKS for JWT verification.',\n UNKNOWN: 'Unknown error',\n};\n\n/**\n * Handles and returns an appropriate error response based on the provided error.\n *\n * @param {unknown} error - The error object which could be of various types such as fetch errors, JWT errors, or generic errors.\n * @param {NextApiResponse} res - The API response object used to send the error response.\n * @returns {NextApiResponse} - The response object containing the appropriate error status and message.\n *\n * This function evaluates the type of error passed to it and maps these errors to suitable\n * HTTP status codes and human-readable messages. It supports specific cases such as:\n * - Fetch errors.\n * - JSON Web Token (JWT) related errors (expired tokens, invalid tokens, signature issues, missing matching keys).\n * - Standard JavaScript errors.\n * - Unknown errors where it defaults to a generic unknown error message.\n *\n * The response will always include the error status and a message describing the error.\n */\nexport const getWebTokenErrorResponse = (\n error: unknown,\n res: NextApiResponse,\n): NextApiResponse => {\n if (isFetchError(error)) {\n res.status(error.status).json({ message: error.statusText });\n return res;\n }\n\n if (error instanceof joseErrors.JWTExpired) {\n res.status(HTTP_NOT_AUTHORIZED).json({ message: ERRORS.TOKEN_EXPIRED });\n return res;\n }\n if (error instanceof joseErrors.JWTInvalid) {\n res.status(HTTP_INTERNAL_ERROR).json({ message: ERRORS.TOKEN_INVALID });\n return res;\n }\n if (error instanceof joseErrors.JWSInvalid) {\n res\n .status(HTTP_INTERNAL_ERROR)\n .json({ message: ERRORS.TOKEN_SIGNATURE_INVALID });\n return res;\n }\n if (error instanceof joseErrors.JWKSNoMatchingKey) {\n res\n .status(HTTP_INTERNAL_ERROR)\n .json({ message: ERRORS.JWKS_NO_MATCHING_KEY });\n return res;\n }\n\n if (error instanceof Error) {\n res.status(HTTP_INTERNAL_ERROR).json({ message: error.message });\n return res;\n }\n\n res.status(HTTP_INTERNAL_ERROR).json({ message: ERRORS.UNKNOWN });\n return res;\n};\n","import type { NextApiRequest, NextApiResponse } from 'next';\nimport { parse } from 'cookie';\nimport type { JWTPayload } from 'jose';\nimport { decodeJwt, errors as joseErrors, importSPKI, jwtVerify } from 'jose';\nimport { fetchJWTKey } from '../../lib/auth/utils';\nimport { getWebTokenErrorResponse } from './errorHandler';\n\nexport interface JWTPayloadAndUser extends JWTPayload {\n /**\n * Fence puts the user in here. Optional because nothing in the JWT spec\n * requires it: a token signed by the same key without one would otherwise\n * crash the handler on a property access.\n */\n context?: Record<string, string>;\n}\n\n/**\n * Claims the client needs from a token whose signature has already been verified.\n */\nconst tokenClaims = (accessToken: string) => {\n const decoded = decodeJwt(accessToken) as JWTPayloadAndUser;\n return {\n issued: decoded.iat,\n expires: decoded.exp,\n userContext: decoded.context?.user,\n };\n};\n\n/**\n * returns the access_token expiration, user, and status\n *\n * Every token state this endpoint can determine — issued, expired, invalid,\n * not present — is reported as a 200 with an explicit `status`. Only a failure\n * to determine the state at all (no JWKS key, an unreachable key source, a\n * malformed request) is an error response, so the client can tell \"your token is\n * expired\" from \"I could not find out\" and stop retrying the former.\n * @param req\n * @param res\n */\nexport default async function handler(\n req: NextApiRequest,\n res: NextApiResponse,\n): Promise<void> {\n try {\n const cookies = req.headers.cookie ? parse(req.headers.cookie) : {};\n let accessToken = cookies.access_token;\n\n // in development mode we support \"credentials login\"\n if (!accessToken && process.env.NODE_ENV === 'development') {\n // NOTE: This cookie can only be accessed from the client side\n // in development mode. Otherwise, the cookie is set as httpOnly\n accessToken = cookies.credentials_token;\n }\n\n if (accessToken) {\n const jwtKey = await fetchJWTKey(process.env.NODE_ENV === 'production');\n if (!jwtKey) {\n res.status(500).json({\n message: 'No JWT Key to verify token',\n });\n return;\n }\n // validate the token\n const publicKey = await importSPKI(jwtKey, 'RS256');\n try {\n await jwtVerify(accessToken, publicKey);\n } catch (error: unknown) {\n // jwtVerify checks the signature before the claims, so an expired token\n // has already proven its signature, and its claims can be reported. This\n // has to be caught here: letting it reach the error handler would turn a\n // definitive \"expired\" into an opaque 401.\n if (error instanceof joseErrors.JWTExpired) {\n res.status(200).json({\n ...tokenClaims(accessToken),\n status: 'expired',\n });\n return;\n }\n throw error;\n }\n\n const claims = tokenClaims(accessToken);\n res.status(200).json({\n ...claims,\n // A token with no `exp` never expires, which we will not honour\n status: claims.expires ? 'issued' : 'invalid',\n });\n return;\n }\n\n res.status(200).json({\n status: 'not present',\n });\n } catch (error: unknown) {\n getWebTokenErrorResponse(error, res); // will update the res object with error\n }\n}\n","import type { NextApiRequest, NextApiResponse } from 'next';\n\nimport { GEN3_FENCE_API, GEN3_FENCE_SERVICE } from '@gen3/core/server';\nimport { serialize } from 'cookie';\n\nexport default async function (req: NextApiRequest, res: NextApiResponse) {\n if (req.method !== 'POST') {\n res.setHeader('Allow', 'POST');\n res.status(405).json({ error: 'Method not allowed' });\n return;\n }\n\n const FENCE_API =\n process.env.NODE_ENV === 'development'\n ? GEN3_FENCE_API\n : GEN3_FENCE_SERVICE;\n\n try {\n const fenceCookies = [\n req.cookies.fence && `fence=${encodeURIComponent(req.cookies.fence)}`,\n req.cookies.access_token &&\n `access_token=${encodeURIComponent(req.cookies.access_token)}`,\n ].filter((cookie): cookie is string => Boolean(cookie));\n\n const fenceResponse = await fetch(`${FENCE_API}/logout`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'text/html',\n ...(fenceCookies.length > 0 ? { Cookie: fenceCookies.join('; ') } : {}),\n },\n redirect: 'manual',\n });\n\n if (!fenceResponse.ok) {\n console.warn(`Fence logout failed (${fenceResponse.status})`);\n }\n } catch (error: unknown) {\n // GEN3_FENCE_SERVICE is an internal, cluster-only URL. Some deployments\n // (e.g. local dev, or a frontend that isn't network-adjacent to Fence)\n // cannot reach it at all — that must not block logout, since the part\n // that matters to this app is clearing its own cookies below.\n console.warn('Unable to reach Fence to complete logout', error);\n }\n\n // Clear local authentication regardless of whether Fence could be reached.\n // This is what actually logs the user out of this app; telling Fence is\n // best-effort on top of it.\n res.setHeader('Set-Cookie', [\n serialize('fence', '', {\n sameSite: 'lax',\n httpOnly: true,\n secure: true,\n path: '/',\n expires: new Date(0),\n }),\n serialize('access_token', '', {\n sameSite: 'lax',\n httpOnly: true,\n secure: true,\n path: '/',\n expires: new Date(0),\n }),\n // delete the credentials_token cookie for credentials login\n serialize('credentials_token', '', {\n sameSite: 'lax',\n httpOnly: process.env.NODE_ENV === 'production',\n secure: process.env.NODE_ENV === 'production',\n path: '/',\n expires: new Date(0),\n }),\n ]);\n\n res.status(200).json({ success: 'success' });\n}\n"],"names":["myGlob","dir","filter","files","fs","readdirSync","file","search","error","FilesystemContent","rootPath","get","filepath","JSON","parse","readFileSync","path","join","toString","Error","getAll","Promise","all","map","ContentDatabase","config","store","GEN3_FRONTEND_CONFIGURATION_ROOT","process","env","ContentSource","instance","contentDatabase","getInstance","getContentDatabase","DefaultAuthorizedRoutesConfig","routes","loginRequired","fetchJWTKey","useService","response","fetchFence","endpoint","isJSON","status","data","keys","length","getAccessToken","cookie","console","warn","cookies","accessToken","access_token","NODE_ENV","credentials_token","JWT_FORMAT_RE","test","HTTP_INTERNAL_ERROR","HTTP_NOT_AUTHORIZED","ERRORS","TOKEN_EXPIRED","TOKEN_INVALID","TOKEN_SIGNATURE_INVALID","JWKS_NO_MATCHING_KEY","UNKNOWN","getWebTokenErrorResponse","res","isFetchError","json","message","statusText","joseErrors","JWTExpired","JWTInvalid","JWSInvalid","JWKSNoMatchingKey","tokenClaims","decoded","decodeJwt","issued","iat","expires","exp","userContext","context","user","handler","req","headers","jwtKey","publicKey","importSPKI","jwtVerify","claims","method","setHeader","FENCE_API","GEN3_FENCE_API","GEN3_FENCE_SERVICE","fenceCookies","fence","encodeURIComponent","Boolean","fenceResponse","fetch","Cookie","redirect","ok","serialize","sameSite","httpOnly","secure","Date","success"],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -101,5 +101,7 @@ declare const getAccessToken: (cookie?: string) => string | null;
|
|
|
101
101
|
*/
|
|
102
102
|
declare function handler(req: NextApiRequest, res: NextApiResponse): Promise<void>;
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
declare function export_default(req: NextApiRequest, res: NextApiResponse): Promise<void>;
|
|
105
|
+
|
|
106
|
+
export { _default as ContentSource, DefaultAuthorizedRoutesConfig, fetchJWTKey, getAccessToken, export_default as sessionLogout, handler as sessionToken };
|
|
105
107
|
export type { AuthorizedRoutesConfig, RouteConfig };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gen3/frontend",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.58",
|
|
4
4
|
"description": "Gen3 frontend components, content management, and pages",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"author": "Center for Translational Data Science",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"@dnd-kit/core": "^6.3.1",
|
|
64
64
|
"@dnd-kit/modifiers": "^9.0.0",
|
|
65
65
|
"@dnd-kit/sortable": "^10.0.0",
|
|
66
|
-
"@gen3/core": "^0.12.
|
|
66
|
+
"@gen3/core": "^0.12.58",
|
|
67
67
|
"@graphiql/react": "^0.37.7",
|
|
68
68
|
"@hello-pangea/dnd": "^18.0.0",
|
|
69
69
|
"@iconify-icon/react": "^3.0.3",
|
|
@@ -114,5 +114,5 @@
|
|
|
114
114
|
"react-dom": "^19.2.4",
|
|
115
115
|
"tailwindcss": "^3.4.18"
|
|
116
116
|
},
|
|
117
|
-
"gitHead": "
|
|
117
|
+
"gitHead": "7d7c8129ce4e70700c32680db750c9daa5268dd9"
|
|
118
118
|
}
|