diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index 61efc0f3a1..85885eeabe 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -35,6 +35,7 @@ import { PermissionsService } from '@backstage/backend-plugin-api'; import { PluginMetadataService } from '@backstage/backend-plugin-api'; import { PushResult } from 'isomorphic-git'; import { ReadCommitResult } from 'isomorphic-git'; +import { Request as Request_2 } from 'express'; import { RequestHandler } from 'express'; import { resolvePackagePath as resolvePackagePath_2 } from '@backstage/backend-plugin-api'; import { resolveSafeChildPath as resolveSafeChildPath_2 } from '@backstage/backend-plugin-api'; @@ -321,6 +322,23 @@ export type KubernetesContainerRunnerOptions = { // @public @deprecated (undocumented) export type LegacyCreateRouter = (deps: TEnv) => Promise; +// @public @deprecated +export interface LegacyIdentityService { + // (undocumented) + getIdentity(options: { request: Request_2 }): Promise< + | { + expiresInSeconds?: number; + token: string; + identity: { + type: 'user'; + userEntityRef: string; + ownershipEntityRefs: string[]; + }; + } + | undefined + >; +} + // @public @deprecated export const legacyPlugin: ( name: string, @@ -335,9 +353,7 @@ export const legacyPlugin: ( logger: LoggerService; permissions: PermissionsService; scheduler: SchedulerService; - tokenManager: TokenManagerService; reader: UrlReaderService; - identity: IdentityService; }, { logger: (log: LoggerService) => Logger; @@ -345,7 +361,10 @@ export const legacyPlugin: ( getClient(options?: CacheServiceOptions | undefined): CacheService; }; } - > + > & { + tokenManager: ServerTokenManager; + identity: LegacyIdentityService; + } >; }>, ) => BackendFeatureCompat; @@ -384,7 +403,12 @@ export function makeLegacyPlugin< ): ( name: string, createRouterImport: Promise<{ - default: LegacyCreateRouter>; + default: LegacyCreateRouter< + TransformedEnv & { + tokenManager: ServerTokenManager; + identity: LegacyIdentityService; + } + >; }>, ) => BackendFeatureCompat; diff --git a/packages/backend-common/src/compat/legacy/index.ts b/packages/backend-common/src/compat/legacy/index.ts index 1172084cd7..7e0646342f 100644 --- a/packages/backend-common/src/compat/legacy/index.ts +++ b/packages/backend-common/src/compat/legacy/index.ts @@ -15,4 +15,4 @@ */ export { legacyPlugin, makeLegacyPlugin } from './legacy'; -export type { LegacyCreateRouter } from './legacy'; +export type { LegacyCreateRouter, LegacyIdentityService } from './legacy'; diff --git a/packages/backend-common/src/compat/legacy/legacy.ts b/packages/backend-common/src/compat/legacy/legacy.ts index 4dc7d1a09a..380c64ce0f 100644 --- a/packages/backend-common/src/compat/legacy/legacy.ts +++ b/packages/backend-common/src/compat/legacy/legacy.ts @@ -18,13 +18,17 @@ import { AuthService, coreServices, createBackendPlugin, - HttpRouterService, + HttpAuthService, + LoggerService, + RootConfigService, ServiceRef, + UserInfoService, } from '@backstage/backend-plugin-api'; import { RequestHandler } from 'express'; import { cacheToPluginCacheManager } from '../cache'; import { loggerToWinstonLogger } from '../logging'; -import { TokenManager } from '../../deprecated'; +import { ServerTokenManager, TokenManager } from '../../deprecated'; +import { Request } from 'express'; /** * @public @@ -46,8 +50,13 @@ type TransformedEnv< // new plugin tokens, which we'll also be signaling by supporting the JWKS endpoint through // the http router. // This makes sure that we accept the new plugin tokens as valid tokens, but otherwise fall -// back to whatever the token manager is doing. -function wrapTokenManager(tokenManager: TokenManager, auth: AuthService) { +// back to the legacy token manager. +function createTokenManagerShim( + auth: AuthService, + config: RootConfigService, + logger: LoggerService, +): TokenManager { + const tokenManager = ServerTokenManager.fromConfig(config, { logger }); return { async getToken() { return tokenManager.getToken(); @@ -64,7 +73,64 @@ function wrapTokenManager(tokenManager: TokenManager, auth: AuthService) { } await tokenManager.authenticate(token); }, - } satisfies TokenManager; + }; +} + +/** + * Originally IdentityApi from `@backstage/plugin-auth-node`, not re-declared here for backwards compatibility + * @public + * @deprecated Only relevant for legacy plugins, which are deprecated. + */ +export interface LegacyIdentityService { + getIdentity(options: { request: Request }): Promise< + | { + expiresInSeconds?: number; + + token: string; + identity: { + type: 'user'; + userEntityRef: string; + ownershipEntityRefs: string[]; + }; + } + | undefined + >; +} + +function createIdentityServiceShim( + httpAuth: HttpAuthService, + userInfo: UserInfoService, +): LegacyIdentityService { + return { + async getIdentity(options) { + const credentials = await httpAuth + .credentials(options.request, { + allow: ['user'], + }) + .catch(() => undefined); + + if (!credentials) { + return undefined; + } + + const info = await userInfo.getUserInfo(credentials); + + return { + get token(): string { + throw new Error( + 'The identity service shim provided by legacyPlugin does not support accessing the user token. ' + + 'The calling plugin needs to be migrated to use the new auth services. ' + + 'See https://backstage.io/docs/tutorials/auth-service-migration', + ); + }, + identity: { + type: 'user', + userEntityRef: info.userEntityRef, + ownershipEntityRefs: info.ownershipEntityRefs, + }, + }; + }, + }; } /** @@ -87,7 +153,12 @@ export function makeLegacyPlugin< return ( name: string, createRouterImport: Promise<{ - default: LegacyCreateRouter>; + default: LegacyCreateRouter< + TransformedEnv & { + tokenManager: ServerTokenManager; + identity: LegacyIdentityService; + } + >; }>, ) => { const compatPlugin = createBackendPlugin({ @@ -96,10 +167,22 @@ export function makeLegacyPlugin< env.registerInit({ deps: { ...envMapping, - _router: coreServices.httpRouter, - _auth: coreServices.auth, + $$router: coreServices.httpRouter, + $$auth: coreServices.auth, + $$httpAuth: coreServices.httpAuth, + $$userInfo: coreServices.userInfo, + $$config: coreServices.rootConfig, + $$logger: coreServices.logger, }, - async init({ _router, _auth, ...envDeps }) { + async init({ + $$auth, + $$config, + $$httpAuth, + $$logger, + $$router, + $$userInfo, + ...envDeps + }) { const { default: createRouter } = await createRouterImport; const pluginEnv = Object.fromEntries( Object.entries(envDeps).map(([key, dep]) => { @@ -107,19 +190,32 @@ export function makeLegacyPlugin< if (transform) { return [key, transform(dep)]; } - if (key === 'tokenManager') { - return [ - key, - wrapTokenManager(dep as TokenManager, _auth as AuthService), - ]; - } return [key, dep]; }), ); - const router = await createRouter( - pluginEnv as TransformedEnv, + + const auth = $$auth as typeof coreServices.auth.T; + const config = $$config as typeof coreServices.rootConfig.T; + const httpAuth = $$httpAuth as typeof coreServices.httpAuth.T; + const logger = $$logger as typeof coreServices.logger.T; + const router = $$router as typeof coreServices.httpRouter.T; + const userInfo = $$userInfo as typeof coreServices.userInfo.T; + + // Token manager and identity services are no longer supported in the new backend system, so we provide shims for them. + pluginEnv.tokenManager = createTokenManagerShim( + auth, + config, + logger, ); - (_router as HttpRouterService).use(router); + pluginEnv.identity = createIdentityServiceShim(httpAuth, userInfo); + + const pluginRouter = await createRouter( + pluginEnv as TransformedEnv & { + tokenManager: ServerTokenManager; + identity: LegacyIdentityService; + }, + ); + router.use(pluginRouter); }, }); }, @@ -155,9 +251,7 @@ export const legacyPlugin = makeLegacyPlugin( logger: coreServices.logger, permissions: coreServices.permissions, scheduler: coreServices.scheduler, - tokenManager: coreServices.tokenManager, reader: coreServices.urlReader, - identity: coreServices.identity, }, { logger: log => loggerToWinstonLogger(log),