diff --git a/.changeset/tasty-beans-confess.md b/.changeset/tasty-beans-confess.md new file mode 100644 index 0000000000..4498272dbf --- /dev/null +++ b/.changeset/tasty-beans-confess.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': minor +--- + +**BREAKING**: The `CatalogIdentityClient` constructor now also requires the `discovery` service to be forwarded from the plugin environment. This is part of the migration to support the new auth services, which has also been done for the `createRouter` function. diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index 8d81751b39..1beddb6419 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -8,6 +8,7 @@ import { AuthProviderFactory as AuthProviderFactory_2 } from '@backstage/plugin- import { AuthProviderRouteHandlers as AuthProviderRouteHandlers_2 } from '@backstage/plugin-auth-node'; import { AuthResolverCatalogUserQuery as AuthResolverCatalogUserQuery_2 } from '@backstage/plugin-auth-node'; import { AuthResolverContext as AuthResolverContext_2 } from '@backstage/plugin-auth-node'; +import { AuthService } from '@backstage/backend-plugin-api'; import { AwsAlbResult as AwsAlbResult_2 } from '@backstage/plugin-auth-backend-module-aws-alb-provider'; import { BackendFeature } from '@backstage/backend-plugin-api'; import { BackstageSignInResult } from '@backstage/plugin-auth-node'; @@ -17,11 +18,13 @@ import { ClientAuthResponse } from '@backstage/plugin-auth-node'; import { Config } from '@backstage/config'; import { CookieConfigurer as CookieConfigurer_2 } from '@backstage/plugin-auth-node'; import { decodeOAuthState } from '@backstage/plugin-auth-node'; +import { DiscoveryService } from '@backstage/backend-plugin-api'; import { encodeOAuthState } from '@backstage/plugin-auth-node'; import { Entity } from '@backstage/catalog-model'; import express from 'express'; import { GcpIapResult as GcpIapResult_2 } from '@backstage/plugin-auth-backend-module-gcp-iap-provider'; import { GcpIapTokenInfo as GcpIapTokenInfo_2 } from '@backstage/plugin-auth-backend-module-gcp-iap-provider'; +import { HttpAuthService } from '@backstage/backend-plugin-api'; import { LoggerService } from '@backstage/backend-plugin-api'; import { OAuth2ProxyResult as OAuth2ProxyResult_2 } from '@backstage/plugin-auth-backend-module-oauth2-proxy-provider'; import { OAuthEnvironmentHandler as OAuthEnvironmentHandler_2 } from '@backstage/plugin-auth-node'; @@ -117,7 +120,13 @@ export type BitbucketServerOAuthResult = { // @public export class CatalogIdentityClient { - constructor(options: { catalogApi: CatalogApi; tokenManager: TokenManager }); + constructor(options: { + catalogApi: CatalogApi; + tokenManager: TokenManager; + discovery: DiscoveryService; + auth?: AuthService; + httpAuth?: HttpAuthService; + }); findUser(query: { annotations: Record }): Promise; resolveCatalogMembership(query: { entityRefs: string[]; @@ -642,6 +651,8 @@ export const readState: typeof decodeOAuthState; // @public (undocumented) export interface RouterOptions { + // (undocumented) + auth?: AuthService; // (undocumented) catalogApi?: CatalogApi; // (undocumented) @@ -653,6 +664,8 @@ export interface RouterOptions { // (undocumented) discovery: PluginEndpointDiscovery; // (undocumented) + httpAuth?: HttpAuthService; + // (undocumented) logger: LoggerService; // (undocumented) providerFactories?: ProviderFactories; diff --git a/plugins/auth-backend/src/authPlugin.ts b/plugins/auth-backend/src/authPlugin.ts index 0522424a99..756d8b80a1 100644 --- a/plugins/auth-backend/src/authPlugin.ts +++ b/plugins/auth-backend/src/authPlugin.ts @@ -75,6 +75,10 @@ export const authPlugin = createBackendPlugin({ providerFactories: Object.fromEntries(providers), disableDefaultProviderFactories: true, }); + httpRouter.addAuthPolicy({ + path: '/', + allow: 'unauthenticated', + }); httpRouter.use(router); }, }); diff --git a/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.test.ts b/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.test.ts index 96cd8d32cb..3439578d5e 100644 --- a/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.test.ts +++ b/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.test.ts @@ -48,6 +48,7 @@ describe('CatalogIdentityClient', () => { catalogApi.getEntities.mockResolvedValueOnce({ items: [{} as UserEntity] }); tokenManager.getToken.mockResolvedValue({ token: 'my-token' }); const client = new CatalogIdentityClient({ + discovery: {} as any, catalogApi: catalogApi as Partial as CatalogApi, tokenManager, }); @@ -106,6 +107,7 @@ describe('CatalogIdentityClient', () => { tokenManager.getToken.mockResolvedValue({ token: 'my-token' }); const client = new CatalogIdentityClient({ + discovery: {} as any, catalogApi: catalogApi as Partial as CatalogApi, tokenManager, }); diff --git a/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts b/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts index ff983e27a5..51c72eeb63 100644 --- a/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts +++ b/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts @@ -14,7 +14,12 @@ * limitations under the License. */ -import { LoggerService } from '@backstage/backend-plugin-api'; +import { + AuthService, + DiscoveryService, + HttpAuthService, + LoggerService, +} from '@backstage/backend-plugin-api'; import { ConflictError, NotFoundError } from '@backstage/errors'; import { CatalogApi } from '@backstage/catalog-client'; import { @@ -24,7 +29,10 @@ import { stringifyEntityRef, UserEntity, } from '@backstage/catalog-model'; -import { TokenManager } from '@backstage/backend-common'; +import { + TokenManager, + createLegacyAuthAdapters, +} from '@backstage/backend-common'; /** * A catalog client tailored for reading out identity data from the catalog. @@ -33,11 +41,25 @@ import { TokenManager } from '@backstage/backend-common'; */ export class CatalogIdentityClient { private readonly catalogApi: CatalogApi; - private readonly tokenManager: TokenManager; + private readonly auth: AuthService; - constructor(options: { catalogApi: CatalogApi; tokenManager: TokenManager }) { + constructor(options: { + catalogApi: CatalogApi; + tokenManager: TokenManager; + discovery: DiscoveryService; + auth?: AuthService; + httpAuth?: HttpAuthService; + }) { this.catalogApi = options.catalogApi; - this.tokenManager = options.tokenManager; + + const { auth } = createLegacyAuthAdapters({ + auth: options.auth, + httpAuth: options.httpAuth, + discovery: options.discovery, + tokenManager: options.tokenManager, + }); + + this.auth = auth; } /** @@ -55,7 +77,11 @@ export class CatalogIdentityClient { filter[`metadata.annotations.${key}`] = value; } - const { token } = await this.tokenManager.getToken(); + const { token } = await this.auth.getPluginRequestToken({ + onBehalfOf: await this.auth.getOwnServiceCredentials(), + targetPluginId: 'catalog', + }); + const { items } = await this.catalogApi.getEntities({ filter }, { token }); if (items.length !== 1) { @@ -101,7 +127,12 @@ export class CatalogIdentityClient { 'metadata.namespace': ref.namespace, 'metadata.name': ref.name, })); - const { token } = await this.tokenManager.getToken(); + + const { token } = await this.auth.getPluginRequestToken({ + onBehalfOf: await this.auth.getOwnServiceCredentials(), + targetPluginId: 'catalog', + }); + const entities = await this.catalogApi .getEntities({ filter }, { token }) .then(r => r.items); diff --git a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts index a7e02c1c31..a4bd2203ae 100644 --- a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts +++ b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts @@ -24,7 +24,12 @@ import { stringifyEntityRef, } from '@backstage/catalog-model'; import { ConflictError, InputError, NotFoundError } from '@backstage/errors'; -import { LoggerService } from '@backstage/backend-plugin-api'; +import { + AuthService, + DiscoveryService, + HttpAuthService, + LoggerService, +} from '@backstage/backend-plugin-api'; import { TokenIssuer } from '../../identity/types'; import { CatalogIdentityClient } from '../catalog'; import { @@ -61,17 +66,24 @@ export class CatalogAuthResolverContext implements AuthResolverContext { catalogApi: CatalogApi; tokenIssuer: TokenIssuer; tokenManager: TokenManager; + discovery: DiscoveryService; + auth: AuthService; + httpAuth: HttpAuthService; }): CatalogAuthResolverContext { const catalogIdentityClient = new CatalogIdentityClient({ catalogApi: options.catalogApi, tokenManager: options.tokenManager, + discovery: options.discovery, + auth: options.auth, + httpAuth: options.httpAuth, }); + return new CatalogAuthResolverContext( options.logger, options.tokenIssuer, catalogIdentityClient, options.catalogApi, - options.tokenManager, + options.auth, ); } @@ -80,7 +92,7 @@ export class CatalogAuthResolverContext implements AuthResolverContext { public readonly tokenIssuer: TokenIssuer, public readonly catalogIdentityClient: CatalogIdentityClient, private readonly catalogApi: CatalogApi, - private readonly tokenManager: TokenManager, + private readonly auth: AuthService, ) {} async issueToken(params: TokenParams) { @@ -90,7 +102,10 @@ export class CatalogAuthResolverContext implements AuthResolverContext { async findCatalogUser(query: AuthResolverCatalogUserQuery) { let result: Entity[] | Entity | undefined = undefined; - const { token } = await this.tokenManager.getToken(); + const { token } = await this.auth.getPluginRequestToken({ + onBehalfOf: await this.auth.getOwnServiceCredentials(), + targetPluginId: 'catalog', + }); if ('entityRef' in query) { const entityRef = parseEntityRef(query.entityRef, { diff --git a/plugins/auth-backend/src/providers/router.ts b/plugins/auth-backend/src/providers/router.ts index 9971466ad4..8c927f52c6 100644 --- a/plugins/auth-backend/src/providers/router.ts +++ b/plugins/auth-backend/src/providers/router.ts @@ -18,7 +18,11 @@ import { PluginEndpointDiscovery, TokenManager, } from '@backstage/backend-common'; -import { LoggerService } from '@backstage/backend-plugin-api'; +import { + AuthService, + HttpAuthService, + LoggerService, +} from '@backstage/backend-plugin-api'; import { CatalogApi, CatalogClient } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; import { NotFoundError, assertError } from '@backstage/errors'; @@ -41,6 +45,8 @@ export function bindProviderRouters( config: Config; logger: LoggerService; discovery: PluginEndpointDiscovery; + auth: AuthService; + httpAuth: HttpAuthService; tokenManager: TokenManager; tokenIssuer: TokenIssuer; catalogApi?: CatalogApi; @@ -53,6 +59,8 @@ export function bindProviderRouters( config, logger, discovery, + auth, + httpAuth, tokenManager, tokenIssuer, catalogApi, @@ -69,10 +77,10 @@ export function bindProviderRouters( const provider = providerFactory({ providerId, appUrl, - baseUrl: baseUrl, + baseUrl, isOriginAllowed, globalConfig: { - baseUrl: baseUrl, + baseUrl, appUrl, isOriginAllowed, }, @@ -84,6 +92,9 @@ export function bindProviderRouters( catalogApi ?? new CatalogClient({ discoveryApi: discovery }), tokenIssuer, tokenManager, + discovery, + auth, + httpAuth, }), }); diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index 459c347835..5628e7be55 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -17,12 +17,17 @@ import express from 'express'; import Router from 'express-promise-router'; import cookieParser from 'cookie-parser'; -import { LoggerService } from '@backstage/backend-plugin-api'; +import { + AuthService, + HttpAuthService, + LoggerService, +} from '@backstage/backend-plugin-api'; import { defaultAuthProviderFactories } from '../providers'; import { PluginDatabaseManager, PluginEndpointDiscovery, TokenManager, + createLegacyAuthAdapters, } from '@backstage/backend-common'; import { NotFoundError } from '@backstage/errors'; import { CatalogApi } from '@backstage/catalog-client'; @@ -45,6 +50,8 @@ export interface RouterOptions { config: Config; discovery: PluginEndpointDiscovery; tokenManager: TokenManager; + auth?: AuthService; + httpAuth?: HttpAuthService; tokenFactoryAlgorithm?: string; providerFactories?: ProviderFactories; disableDefaultProviderFactories?: boolean; @@ -63,6 +70,9 @@ export async function createRouter( tokenFactoryAlgorithm, providerFactories = {}, } = options; + + const { auth, httpAuth } = createLegacyAuthAdapters(options); + const router = Router(); const appUrl = config.getString('app.baseUrl'); @@ -136,6 +146,8 @@ export async function createRouter( baseUrl: authUrl, tokenIssuer, ...options, + auth, + httpAuth, }); bindOidcRouter(router, {