diff --git a/docs/auth/service-to-service-auth.md b/docs/auth/service-to-service-auth.md index 8e8ef6a183..4d8829d91d 100644 --- a/docs/auth/service-to-service-auth.md +++ b/docs/auth/service-to-service-auth.md @@ -414,7 +414,7 @@ Each entry has one or more of the following fields: ## Adding custom or logic for validation and issuing of tokens -The `pluginTokenHandlerDecoratorServiceRef` and `externalTokenHandlersServiceRef` can be used to extend the existing token handler without having to re-implement the entire `AuthService` implementation. +The `pluginTokenHandlerDecoratorServiceRef` and `externalTokenTypeHandlersRef` can be used to extend the existing token handler without having to re-implement the entire `AuthService` implementation. This is particularly useful when you want to add additional logic to the handler, such as logging or metrics or custom token validation. ### PluginTokenHandler decoration @@ -446,26 +446,47 @@ const decoratedPluginTokenHandler = createServiceFactory({ ### ExternalTokenHandler decoration -The `externalTokenHandlersServiceRef` can be used to add custom external token handlers to the default implementation. +The `externalTokenTypeHandlersRef` can be used to add custom external token handlers to the default implementation. -The returned object should be a map of token custom types and their handler factories. The object keys will be matched to the configured `externalAccess` type in the app-config, calling the factory function with the config object for that type. Custom token handlers should implement the `TokenHandler` interface, which provides methods for verifying tokens. +The returned object from the factory function must have a `type` property which is used to identify the handler. The `factory` method is called with an array of `Config` objects, for the given type. The factory method can return a single handler or an array of handlers. The handlers are then used to handle the token for the given type. -For example, to add a custom token handler for a type called 'custom': +For example, if we whant to add a custom external token handler for the `custom` type: + +our config would look like this: + +```yaml title="in e.g. app-config.production.yaml" +backend: + auth: + externalAccess: + - type: custom + options: + customOptions: additional-value + accessRestrictions: + - plugin: events + - type: custom + options: + customOptions: another-value + accessRestrictions: + - plugin: events +``` + +And we can implement the custom token handler like this: ```ts import { TokenHandler, - externalTokenHandlersServiceRef, + externalTokenTypeHandlersRef, } from '@backstage/backend-defaults/auth'; import { Config } from '@backstage/config'; import { createServiceFactory } from '@backstage/backend-plugin-api'; const customExternalTokenHandlers = createServiceFactory({ - service: externalTokenHandlersServiceRef, + service: externalTokenTypeHandlersRef, deps: {}, async factory() { return { - custom: (config: Config) => new CustomTokenHandler(config); + type: 'custom', + factory: (configs: Config[]) => new CustomTokenHandler(configs), // can return a simple handler or an array of handlers }; }, }); diff --git a/packages/backend-defaults/report-auth.api.md b/packages/backend-defaults/report-auth.api.md index bcc5c4a073..2a80a59d2d 100644 --- a/packages/backend-defaults/report-auth.api.md +++ b/packages/backend-defaults/report-auth.api.md @@ -24,10 +24,7 @@ export const authServiceFactory: ServiceFactory< // @public export const externalTokenTypeHandlersRef: ServiceRef< - { - type: string; - factory: (config: Config[]) => TokenHandler; - }, + TokenTypeHandler, 'plugin', 'multiton' >; @@ -74,5 +71,12 @@ export interface TokenHandler { >; } +// @public +export interface TokenTypeHandler { + factory: (configs: Config[]) => TokenHandler | TokenHandler[]; + // (undocumented) + type: string; +} + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts index cc93b7b182..c3fab70026 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts @@ -27,32 +27,12 @@ import { JWKSHandler } from './jwks'; import { TokenHandler } from './types'; import { Config } from '@backstage/config'; import { groupBy } from 'lodash'; +import { TokenTypeHandler } from './types'; const NEW_CONFIG_KEY = 'backend.auth.externalAccess'; const OLD_CONFIG_KEY = 'backend.auth.keys'; let loggedDeprecationWarning = false; -/** - * @public - * This service is used to decorate the default plugin token handler with custom logic. - */ -export const externalTokenTypeHandlersRef = createServiceRef<{ - type: string; - factory: (config: Config[]) => TokenHandler; -}>({ - id: 'core.auth.externalTokenHandlers', - multiton: true, - // defaultFactory // :pepe-think: seems like is not possible to use defaultFactory with multiton -}); - -type TokenTypeHandler = { - type: string; - /** - * A factory function that takes all token configuration for a given type - * and returns a TokenHandler or an array of TokenHandlers. - */ - factory: (config: Config[]) => TokenHandler | TokenHandler[]; -}; type LegacyTokenTypeHandler = { type: 'legacy'; /** @@ -60,10 +40,23 @@ type LegacyTokenTypeHandler = { * and returns a TokenHandler or an array of TokenHandlers. */ factory: ( - config: (Config | LegacyConfigWrapper)[], + configs: ( + | import('@backstage/config').Config + | { legacy: true; config: import('@backstage/config').Config } + )[], ) => TokenHandler | TokenHandler[]; }; +/** + * @public + * This service is used to decorate the default plugin token handler with custom logic. + */ +export const externalTokenTypeHandlersRef = createServiceRef({ + id: 'core.auth.externalTokenHandlers', + multiton: true, + // defaultFactory // :pepe-think: seems like is not possible to use defaultFactory with multiton +}); + const defaultHandlers: (TokenTypeHandler | LegacyTokenTypeHandler)[] = [ { type: 'static', diff --git a/packages/backend-defaults/src/entrypoints/auth/external/types.ts b/packages/backend-defaults/src/entrypoints/auth/external/types.ts index c3d45fd0d7..5e26b66da5 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/types.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/types.ts @@ -38,3 +38,18 @@ export interface TokenHandler { | undefined >; } + +/** + * @public + * This interface is used to handle external tokens. + */ +export interface TokenTypeHandler { + type: string; + /** + * A factory function that takes all token configuration for a given type + * and returns a TokenHandler or an array of TokenHandlers. + */ + factory: ( + configs: import('@backstage/config').Config[], + ) => TokenHandler | TokenHandler[]; +} diff --git a/packages/backend-defaults/src/entrypoints/auth/index.ts b/packages/backend-defaults/src/entrypoints/auth/index.ts index 1b1a29a247..335fc2f62c 100644 --- a/packages/backend-defaults/src/entrypoints/auth/index.ts +++ b/packages/backend-defaults/src/entrypoints/auth/index.ts @@ -21,7 +21,7 @@ export { export { externalTokenTypeHandlersRef } from './external/ExternalTokenHandler'; -export type { TokenHandler } from './external/types'; +export type { TokenHandler, TokenTypeHandler } from './external/types'; export type { AccessRestrictionsMap } from './external/types'; export type { PluginTokenHandler } from './plugin/PluginTokenHandler';