diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts index f315b43314..e3f1acf270 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts @@ -354,6 +354,76 @@ describe('ExternalTokenHandler', () => { expect(verifyMock).toHaveBeenCalledWith(customToken, { context: 'a' }); }); + it('should fail if custom handler has same type as builtin handlers', async () => { + const logger = mockServices.logger.mock(); + const customStaticHandler: ExternalTokenHandler = + createExternalTokenHandler({ + type: 'static', + initialize: jest.fn().mockResolvedValue(undefined), + verifyToken: jest.fn().mockResolvedValue({ + subject: 'custom-static-subject', + }), + }); + + const createHandler = () => + ExternalAuthTokenHandler.create({ + ownPluginId: 'catalog', + logger, + config: mockServices.rootConfig({ + data: { + backend: { + auth: { + externalAccess: [ + { + type: 'static', + options: { + token: 'mytoken', + subject: 'static-subject', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], + }, + ], + }, + }, + }, + }), + externalTokenHandlers: [customStaticHandler], + }); + + expect(createHandler).toThrowErrorMatchingInlineSnapshot( + `"Duplicate external token handler type 'static', each handler must have a unique type"`, + ); + }); + it('should fail if 2 custom handlers have the same type', async () => { + const createHandler = () => + ExternalAuthTokenHandler.create({ + ownPluginId: 'catalog', + logger: mockServices.logger.mock(), + config: mockServices.rootConfig(), + externalTokenHandlers: [ + createExternalTokenHandler({ + type: 'internal-custom', + initialize: jest.fn().mockResolvedValue(undefined), + verifyToken: jest.fn().mockResolvedValue({ + subject: 'sub', + }), + }), + createExternalTokenHandler({ + type: 'internal-custom', + initialize: jest.fn().mockResolvedValue(undefined), + verifyToken: jest.fn().mockResolvedValue({ + subject: 'sub', + }), + }), + ], + }); + + expect(createHandler).toThrowErrorMatchingInlineSnapshot( + `"Duplicate external token handler type 'internal-custom', each handler must have a unique type"`, + ); + }); it('should fail if config contains types not declared', async () => { const createHandler = () => ExternalAuthTokenHandler.create({ diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts index 4b2cbb3e5a..21b3aed99d 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts @@ -42,11 +42,11 @@ export const externalTokenHandlersServiceRef = createServiceRef< multiton: true, }); -const defaultHandlers: ExternalTokenHandler[] = [ - staticTokenHandler, - legacyTokenHandler, - jwksTokenHandler, -]; +const defaultHandlers: Record> = { + static: staticTokenHandler, + legacy: legacyTokenHandler, + jwks: jwksTokenHandler, +}; type ContextMapEntry = { context: T; @@ -70,20 +70,35 @@ export class ExternalAuthTokenHandler { const { ownPluginId, config, - externalTokenHandlers: customHandlers, + externalTokenHandlers: customHandlers = [], logger, } = options; - const handlersTypes = [...defaultHandlers, ...(customHandlers ?? [])]; + const handlersTypes = customHandlers.reduce< + Record> + >( + (acc, handler) => { + if (acc[handler.type]) { + throw new Error( + `Duplicate external token handler type '${handler.type}', each handler must have a unique type`, + ); + } + acc[handler.type] = handler; + return acc; + }, + { ...defaultHandlers }, + ); const handlerConfigs = config.getOptionalConfigArray(NEW_CONFIG_KEY) ?? []; const contexts: ContextMapEntry[] = handlerConfigs.map( handlerConfig => { const type = handlerConfig.getString('type'); - const handler = handlersTypes.find(h => h.type === type); + const handler = handlersTypes[type]; if (!handler) { - const valid = handlersTypes.map(h => `'${h.type}'`).join(', '); + const valid = Object.keys(handlersTypes) + .map(h => `'${h}'`) + .join(', '); throw new Error( `Unknown type '${type}' in ${NEW_CONFIG_KEY}, expected one of ${valid}`, ); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts b/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts index 892fcf2181..33b55df775 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts @@ -75,7 +75,6 @@ export const jwksTokenHandler = createExternalTokenHandler({ : 'external:'; return { subject: `${prefix}${sub}`, - allAccessRestrictions: context.allAccessRestrictions, }; } } catch {