Merge pull request #9190 from backstage/catalog-identity-client-token
Use server token for catalog identity client
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
---
|
||||
'@backstage/plugin-auth-backend': minor
|
||||
---
|
||||
|
||||
**BREAKING** Added `tokenManager` as a required property for the auth-backend `createRouter` function. This dependency is used to issue server tokens that are used by the `CatalogIdentityClient` when looking up users and their group membership during authentication.
|
||||
|
||||
These changes are **required** to `packages/backend/src/plugins/auth.ts`:
|
||||
|
||||
```diff
|
||||
export default async function createPlugin({
|
||||
logger,
|
||||
database,
|
||||
config,
|
||||
discovery,
|
||||
+ tokenManager,
|
||||
}: PluginEnvironment): Promise<Router> {
|
||||
return await createRouter({
|
||||
logger,
|
||||
config,
|
||||
database,
|
||||
discovery,
|
||||
+ tokenManager,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**BREAKING** The `CatalogIdentityClient` constructor now expects a `TokenManager` instead of a `TokenIssuer`. The `TokenManager` interface is used to generate a server token when [resolving a user's identity and membership through the catalog](https://backstage.io/docs/auth/identity-resolver). Using server tokens for these requests allows the auth-backend to bypass authorization checks when permissions are enabled for Backstage. This change will break apps that rely on the user tokens that were previously used by the client. Refer to the ["Backend-to-backend Authentication" tutorial](https://backstage.io/docs/tutorials/backend-to-backend-auth) for more information on server token usage.
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Added `tokenManager` as a required property for the auth-backend `createRouter` function. This dependency is used to issue server tokens that are used by the `CatalogIdentityClient` when looking up users and their group membership during authentication.
|
||||
|
||||
These changes are **required** to `packages/backend/src/plugins/auth.ts`:
|
||||
|
||||
```diff
|
||||
export default async function createPlugin({
|
||||
logger,
|
||||
database,
|
||||
config,
|
||||
discovery,
|
||||
+ tokenManager,
|
||||
}: PluginEnvironment): Promise<Router> {
|
||||
return await createRouter({
|
||||
logger,
|
||||
config,
|
||||
database,
|
||||
discovery,
|
||||
+ tokenManager,
|
||||
});
|
||||
}
|
||||
```
|
||||
@@ -23,6 +23,13 @@ export default async function createPlugin({
|
||||
database,
|
||||
config,
|
||||
discovery,
|
||||
tokenManager,
|
||||
}: PluginEnvironment): Promise<Router> {
|
||||
return await createRouter({ logger, config, database, discovery });
|
||||
return await createRouter({
|
||||
logger,
|
||||
config,
|
||||
database,
|
||||
discovery,
|
||||
tokenManager,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,6 +7,13 @@ export default async function createPlugin({
|
||||
database,
|
||||
config,
|
||||
discovery,
|
||||
tokenManager,
|
||||
}: PluginEnvironment): Promise<Router> {
|
||||
return await createRouter({ logger, config, database, discovery });
|
||||
return await createRouter({
|
||||
logger,
|
||||
config,
|
||||
database,
|
||||
discovery,
|
||||
tokenManager,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { Logger as Logger_2 } from 'winston';
|
||||
import { PluginDatabaseManager } from '@backstage/backend-common';
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import { Profile } from 'passport';
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
import { TokenSet } from 'openid-client';
|
||||
import { UserEntity } from '@backstage/catalog-model';
|
||||
import { UserinfoResponse } from 'openid-client';
|
||||
@@ -85,6 +86,7 @@ export type AuthProviderFactoryOptions = {
|
||||
globalConfig: AuthProviderConfig;
|
||||
config: Config;
|
||||
logger: Logger_2;
|
||||
tokenManager: TokenManager;
|
||||
tokenIssuer: TokenIssuer;
|
||||
discovery: PluginEndpointDiscovery;
|
||||
catalogApi: CatalogApi;
|
||||
@@ -205,7 +207,7 @@ export const bitbucketUsernameSignInResolver: SignInResolver<BitbucketOAuthResul
|
||||
//
|
||||
// @public
|
||||
export class CatalogIdentityClient {
|
||||
constructor(options: { catalogApi: CatalogApi; tokenIssuer: TokenIssuer });
|
||||
constructor(options: { catalogApi: CatalogApi; tokenManager: TokenManager });
|
||||
// Warning: (ae-forgotten-export) The symbol "UserQuery" needs to be exported by the entry point index.d.ts
|
||||
findUser(query: UserQuery): Promise<UserEntity>;
|
||||
// Warning: (ae-forgotten-export) The symbol "MemberClaimQuery" needs to be exported by the entry point index.d.ts
|
||||
@@ -671,6 +673,8 @@ export interface RouterOptions {
|
||||
//
|
||||
// (undocumented)
|
||||
providerFactories?: ProviderFactories;
|
||||
// (undocumented)
|
||||
tokenManager: TokenManager;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import {
|
||||
RELATION_MEMBER_OF,
|
||||
UserEntity,
|
||||
UserEntityV1alpha1,
|
||||
} from '@backstage/catalog-model';
|
||||
import { TokenIssuer } from '../../identity';
|
||||
import { CatalogIdentityClient } from './CatalogIdentityClient';
|
||||
|
||||
describe('CatalogIdentityClient', () => {
|
||||
@@ -36,19 +36,19 @@ describe('CatalogIdentityClient', () => {
|
||||
refreshEntity: jest.fn(),
|
||||
getEntityAncestors: jest.fn(),
|
||||
};
|
||||
const tokenIssuer: jest.Mocked<TokenIssuer> = {
|
||||
issueToken: jest.fn(),
|
||||
listPublicKeys: jest.fn(),
|
||||
const tokenManager: jest.Mocked<TokenManager> = {
|
||||
getToken: jest.fn(),
|
||||
authenticate: jest.fn(),
|
||||
};
|
||||
|
||||
afterEach(() => jest.resetAllMocks());
|
||||
|
||||
it('findUser passes through the correct search params', async () => {
|
||||
catalogApi.getEntities.mockResolvedValueOnce({ items: [{} as UserEntity] });
|
||||
tokenIssuer.issueToken.mockResolvedValue('my-token');
|
||||
tokenManager.getToken.mockResolvedValue({ token: 'my-token' });
|
||||
const client = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
await client.findUser({ annotations: { key: 'value' } });
|
||||
@@ -62,11 +62,7 @@ describe('CatalogIdentityClient', () => {
|
||||
},
|
||||
{ token: 'my-token' },
|
||||
);
|
||||
expect(tokenIssuer.issueToken).toHaveBeenCalledWith({
|
||||
claims: {
|
||||
sub: 'backstage.io/auth-backend',
|
||||
},
|
||||
});
|
||||
expect(tokenManager.getToken).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('resolveCatalogMembership resolves membership', async () => {
|
||||
@@ -114,35 +110,39 @@ describe('CatalogIdentityClient', () => {
|
||||
},
|
||||
];
|
||||
catalogApi.getEntities.mockResolvedValueOnce({ items: mockUsers });
|
||||
tokenManager.getToken.mockResolvedValue({ token: 'my-token' });
|
||||
|
||||
const client = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const claims = await client.resolveCatalogMembership({
|
||||
entityRefs: ['inigom', 'User:default/imontoya', 'User:reality/mpatinkin'],
|
||||
});
|
||||
|
||||
expect(catalogApi.getEntities).toHaveBeenCalledWith({
|
||||
filter: [
|
||||
{
|
||||
kind: 'user',
|
||||
'metadata.namespace': 'default',
|
||||
'metadata.name': 'inigom',
|
||||
},
|
||||
{
|
||||
kind: 'user',
|
||||
'metadata.namespace': 'default',
|
||||
'metadata.name': 'imontoya',
|
||||
},
|
||||
{
|
||||
kind: 'user',
|
||||
'metadata.namespace': 'reality',
|
||||
'metadata.name': 'mpatinkin',
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(catalogApi.getEntities).toHaveBeenCalledWith(
|
||||
{
|
||||
filter: [
|
||||
{
|
||||
kind: 'user',
|
||||
'metadata.namespace': 'default',
|
||||
'metadata.name': 'inigom',
|
||||
},
|
||||
{
|
||||
kind: 'user',
|
||||
'metadata.namespace': 'default',
|
||||
'metadata.name': 'imontoya',
|
||||
},
|
||||
{
|
||||
kind: 'user',
|
||||
'metadata.namespace': 'reality',
|
||||
'metadata.name': 'mpatinkin',
|
||||
},
|
||||
],
|
||||
},
|
||||
{ token: 'my-token' },
|
||||
);
|
||||
|
||||
expect(claims).toMatchObject([
|
||||
'user:default/inigom',
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
stringifyEntityRef,
|
||||
UserEntity,
|
||||
} from '@backstage/catalog-model';
|
||||
import { TokenIssuer } from '../../identity';
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
|
||||
type UserQuery = {
|
||||
annotations: Record<string, string>;
|
||||
@@ -40,11 +40,11 @@ type MemberClaimQuery = {
|
||||
*/
|
||||
export class CatalogIdentityClient {
|
||||
private readonly catalogApi: CatalogApi;
|
||||
private readonly tokenIssuer: TokenIssuer;
|
||||
private readonly tokenManager: TokenManager;
|
||||
|
||||
constructor(options: { catalogApi: CatalogApi; tokenIssuer: TokenIssuer }) {
|
||||
constructor(options: { catalogApi: CatalogApi; tokenManager: TokenManager }) {
|
||||
this.catalogApi = options.catalogApi;
|
||||
this.tokenIssuer = options.tokenIssuer;
|
||||
this.tokenManager = options.tokenManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,10 +60,7 @@ export class CatalogIdentityClient {
|
||||
filter[`metadata.annotations.${key}`] = value;
|
||||
}
|
||||
|
||||
// TODO(Rugvip): cache the token
|
||||
const token = await this.tokenIssuer.issueToken({
|
||||
claims: { sub: 'backstage.io/auth-backend' },
|
||||
});
|
||||
const { token } = await this.tokenManager.getToken();
|
||||
const { items } = await this.catalogApi.getEntities({ filter }, { token });
|
||||
|
||||
if (items.length !== 1) {
|
||||
@@ -106,8 +103,9 @@ export class CatalogIdentityClient {
|
||||
'metadata.namespace': ref.namespace,
|
||||
'metadata.name': ref.name,
|
||||
}));
|
||||
const { token } = await this.tokenManager.getToken();
|
||||
const entities = await this.catalogApi
|
||||
.getEntities({ filter })
|
||||
.getEntities({ filter }, { token })
|
||||
.then(r => r.items);
|
||||
|
||||
if (entityRefs.length !== entities.length) {
|
||||
|
||||
@@ -197,6 +197,7 @@ export const createAtlassianProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -208,7 +209,7 @@ export const createAtlassianProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> =
|
||||
|
||||
@@ -220,6 +220,7 @@ export const createAuth0Provider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -231,7 +232,7 @@ export const createAuth0Provider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> = options?.authHandler
|
||||
|
||||
@@ -241,7 +241,7 @@ export type AwsAlbProviderOptions = {
|
||||
export const createAwsAlbProvider = (
|
||||
options?: AwsAlbProviderOptions,
|
||||
): AuthProviderFactory => {
|
||||
return ({ config, tokenIssuer, catalogApi, logger }) => {
|
||||
return ({ config, tokenIssuer, catalogApi, logger, tokenManager }) => {
|
||||
const region = config.getString('region');
|
||||
const issuer = config.getOptionalString('iss');
|
||||
|
||||
@@ -253,7 +253,7 @@ export const createAwsAlbProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<AwsAlbResult> = options?.authHandler
|
||||
|
||||
@@ -273,6 +273,7 @@ export const createBitbucketProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -283,7 +284,7 @@ export const createBitbucketProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<BitbucketOAuthResult> =
|
||||
|
||||
@@ -102,7 +102,7 @@ export class GcpIapProvider implements AuthProviderRouteHandlers {
|
||||
export function createGcpIapProvider(
|
||||
options: GcpIapProviderOptions,
|
||||
): AuthProviderFactory {
|
||||
return ({ config, tokenIssuer, catalogApi, logger }) => {
|
||||
return ({ config, tokenIssuer, catalogApi, logger, tokenManager }) => {
|
||||
const audience = config.getString('audience');
|
||||
|
||||
const authHandler = options.authHandler ?? defaultAuthHandler;
|
||||
@@ -111,7 +111,7 @@ export function createGcpIapProvider(
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
return new GcpIapProvider({
|
||||
|
||||
@@ -245,6 +245,7 @@ export const createGithubProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -270,7 +271,7 @@ export const createGithubProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<GithubOAuthResult> = options?.authHandler
|
||||
|
||||
@@ -227,6 +227,7 @@ export const createGitlabProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -239,7 +240,7 @@ export const createGitlabProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> =
|
||||
|
||||
@@ -259,6 +259,7 @@ export const createGoogleProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -269,7 +270,7 @@ export const createGoogleProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> = options?.authHandler
|
||||
|
||||
@@ -267,6 +267,7 @@ export const createMicrosoftProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -281,7 +282,7 @@ export const createMicrosoftProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> = options?.authHandler
|
||||
|
||||
@@ -182,12 +182,12 @@ export const createOauth2ProxyProvider =
|
||||
<JWTPayload>(
|
||||
options: Oauth2ProxyProviderOptions<JWTPayload>,
|
||||
): AuthProviderFactory =>
|
||||
({ catalogApi, logger, tokenIssuer }) => {
|
||||
({ catalogApi, logger, tokenIssuer, tokenManager }) => {
|
||||
const signInResolver = options.signIn.resolver;
|
||||
const authHandler = options.authHandler;
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
return new Oauth2ProxyAuthProvider<JWTPayload>({
|
||||
logger,
|
||||
|
||||
@@ -233,6 +233,7 @@ export const createOAuth2Provider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -249,7 +250,7 @@ export const createOAuth2Provider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> = options?.authHandler
|
||||
|
||||
@@ -257,6 +257,7 @@ export const createOidcProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -272,7 +273,7 @@ export const createOidcProvider = (
|
||||
const prompt = envConfig.getOptionalString('prompt');
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OidcAuthResult> = options?.authHandler
|
||||
|
||||
@@ -268,6 +268,7 @@ export const createOktaProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -286,7 +287,7 @@ export const createOktaProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> = _options?.authHandler
|
||||
|
||||
@@ -219,6 +219,7 @@ export const createOneLoginProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -230,7 +231,7 @@ export const createOneLoginProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> = options?.authHandler
|
||||
|
||||
@@ -187,12 +187,13 @@ export const createSamlProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) => {
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<SamlAuthResult> = options?.authHandler
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import {
|
||||
PluginEndpointDiscovery,
|
||||
TokenManager,
|
||||
} from '@backstage/backend-common';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
@@ -124,6 +127,7 @@ export type AuthProviderFactoryOptions = {
|
||||
globalConfig: AuthProviderConfig;
|
||||
config: Config;
|
||||
logger: Logger;
|
||||
tokenManager: TokenManager;
|
||||
tokenIssuer: TokenIssuer;
|
||||
discovery: PluginEndpointDiscovery;
|
||||
catalogApi: CatalogApi;
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
import {
|
||||
PluginDatabaseManager,
|
||||
PluginEndpointDiscovery,
|
||||
TokenManager,
|
||||
} from '@backstage/backend-common';
|
||||
import { assertError, NotFoundError } from '@backstage/errors';
|
||||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
@@ -41,13 +42,21 @@ export interface RouterOptions {
|
||||
database: PluginDatabaseManager;
|
||||
config: Config;
|
||||
discovery: PluginEndpointDiscovery;
|
||||
tokenManager: TokenManager;
|
||||
providerFactories?: ProviderFactories;
|
||||
}
|
||||
|
||||
export async function createRouter(
|
||||
options: RouterOptions,
|
||||
): Promise<express.Router> {
|
||||
const { logger, config, discovery, database, providerFactories } = options;
|
||||
const {
|
||||
logger,
|
||||
config,
|
||||
discovery,
|
||||
database,
|
||||
tokenManager,
|
||||
providerFactories,
|
||||
} = options;
|
||||
const router = Router();
|
||||
|
||||
const appUrl = config.getString('app.baseUrl');
|
||||
@@ -105,6 +114,7 @@ export async function createRouter(
|
||||
globalConfig: { baseUrl: authUrl, appUrl, isOriginAllowed },
|
||||
config: providersConfig.getConfig(providerId),
|
||||
logger,
|
||||
tokenManager,
|
||||
tokenIssuer,
|
||||
discovery,
|
||||
catalogApi,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import {
|
||||
createServiceBuilder,
|
||||
loadBackendConfig,
|
||||
ServerTokenManager,
|
||||
SingleHostDiscovery,
|
||||
useHotMemoize,
|
||||
} from '@backstage/backend-common';
|
||||
@@ -58,6 +59,7 @@ export async function startStandaloneServer(
|
||||
},
|
||||
},
|
||||
discovery,
|
||||
tokenManager: ServerTokenManager.noop(),
|
||||
});
|
||||
|
||||
const service = createServiceBuilder(module)
|
||||
|
||||
Reference in New Issue
Block a user