auth-backend: add plugin export for new backend system

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-08-18 13:10:27 +02:00
parent c80013badd
commit 7944d43f47
12 changed files with 203 additions and 9 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-backend': patch
---
Added `authPlugin` export for the new backend system. The plugin does not include any built-in auth providers, they must instead be added by installing additional modules, for example `authModuleGoogleProvider` from `@backstage/plugin-auth-backend-module-google-provider`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-backend': patch
---
Added the ability to disable the built-in auth providers by passing `disableDefaultProviderFactories` to `createRouter`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-backend': patch
---
The algorithm used when generating Backstage tokens can be configured via `auth.identityTokenAlgorithm`.
+6
View File
@@ -10,6 +10,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 { BackendFeature } from '@backstage/backend-plugin-api';
import { BackstageSignInResult } from '@backstage/plugin-auth-node';
import { CacheService } from '@backstage/backend-plugin-api';
import { CatalogApi } from '@backstage/catalog-client';
@@ -51,6 +52,9 @@ export type AuthHandlerResult = {
profile: ProfileInfo;
};
// @public
export const authPlugin: () => BackendFeature;
// @public @deprecated (undocumented)
export type AuthProviderConfig = AuthProviderConfig_2;
@@ -658,6 +662,8 @@ export interface RouterOptions {
// (undocumented)
database: PluginDatabaseManager;
// (undocumented)
disableDefaultProviderFactories?: boolean;
// (undocumented)
discovery: PluginEndpointDiscovery;
// (undocumented)
logger: LoggerService;
+10
View File
@@ -31,6 +31,16 @@ export interface Config {
secret?: string;
};
/**
* JWS "alg" (Algorithm) Header Parameter value. Defaults to ES256.
* Must match one of the algorithms defined for IdentityClient.
* When setting a different algorithm, check if the `key` field
* of the `signing_keys` table can fit the length of the generated keys.
* If not, add a knex migration file in the migrations folder.
* More info on supported algorithms: https://github.com/panva/jose
*/
identityTokenAlgorithm?: string;
/** To control how to store JWK data in auth-backend */
keyStore?: {
provider?: 'database' | 'memory' | 'firestore';
+26
View File
@@ -0,0 +1,26 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createBackend } from '@backstage/backend-defaults';
import { authPlugin } from '../src';
import { authModuleGoogleProvider } from '@backstage/plugin-auth-backend-module-google-provider';
const backend = createBackend();
backend.add(authPlugin);
backend.add(authModuleGoogleProvider);
backend.start();
+2
View File
@@ -41,6 +41,7 @@
"@backstage/plugin-auth-backend-module-gcp-iap-provider": "workspace:^",
"@backstage/plugin-auth-backend-module-google-provider": "workspace:^",
"@backstage/plugin-auth-node": "workspace:^",
"@backstage/plugin-catalog-node": "workspace:^",
"@backstage/types": "workspace:^",
"@davidzemon/passport-okta-oauth": "^0.0.5",
"@google-cloud/firestore": "^6.0.0",
@@ -80,6 +81,7 @@
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/backend-defaults": "workspace:^",
"@backstage/backend-test-utils": "workspace:^",
"@backstage/cli": "workspace:^",
"@types/body-parser": "^1.19.0",
@@ -0,0 +1,45 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { mockServices, startTestBackend } from '@backstage/backend-test-utils';
import request from 'supertest';
import { authPlugin } from './authPlugin';
describe('authPlugin', () => {
it('should provide an OpenID configuration', async () => {
const { server } = await startTestBackend({
features: [
authPlugin,
mockServices.rootConfig.factory({
data: {
app: {
baseUrl: 'http://localhost:3000',
},
},
}),
],
});
const res = await request(server).get(
'/api/auth/.well-known/openid-configuration',
);
expect(res.status).toBe(200);
expect(res.body).toMatchObject({
claims_supported: ['sub'],
issuer: `http://localhost:${server.port()}/api/auth`,
});
});
});
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
coreServices,
createBackendPlugin,
} from '@backstage/backend-plugin-api';
import {
AuthProviderFactory,
authProvidersExtensionPoint,
} from '@backstage/plugin-auth-node';
import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha';
import { createRouter } from './service/router';
/**
* Auth plugin
*
* @public
*/
export const authPlugin = createBackendPlugin({
pluginId: 'auth',
register(reg) {
const providers = new Map<string, AuthProviderFactory>();
reg.registerExtensionPoint(authProvidersExtensionPoint, {
registerProvider({ providerId, factory }) {
if (providers.has(providerId)) {
throw new Error(
`Auth provider '${providerId}' was already registered`,
);
}
providers.set(providerId, factory);
},
});
reg.registerInit({
deps: {
httpRouter: coreServices.httpRouter,
logger: coreServices.logger,
config: coreServices.rootConfig,
database: coreServices.database,
discovery: coreServices.discovery,
tokenManager: coreServices.tokenManager,
catalogApi: catalogServiceRef,
},
async init({
httpRouter,
logger,
config,
database,
discovery,
tokenManager,
catalogApi,
}) {
const router = await createRouter({
logger,
config,
database,
discovery,
tokenManager,
catalogApi,
providerFactories: Object.fromEntries(providers),
disableDefaultProviderFactories: true,
});
httpRouter.use(router);
},
});
},
});
+1
View File
@@ -20,6 +20,7 @@
* @packageDocumentation
*/
export { authPlugin } from './authPlugin';
export * from './service/router';
export type { TokenParams } from './identity';
export * from './providers';
+14 -9
View File
@@ -51,6 +51,7 @@ export interface RouterOptions {
tokenManager: TokenManager;
tokenFactoryAlgorithm?: string;
providerFactories?: ProviderFactories;
disableDefaultProviderFactories?: boolean;
catalogApi?: CatalogApi;
}
@@ -65,7 +66,7 @@ export async function createRouter(
database,
tokenManager,
tokenFactoryAlgorithm,
providerFactories,
providerFactories = {},
catalogApi,
} = options;
const router = Router();
@@ -85,7 +86,9 @@ export async function createRouter(
keyStore,
keyDurationSeconds,
logger: logger.child({ component: 'token-factory' }),
algorithm: tokenFactoryAlgorithm,
algorithm:
tokenFactoryAlgorithm ??
config.getOptionalString('auth.identityTokenAlgorithm'),
});
const secret = config.getOptionalString('auth.session.secret');
@@ -113,19 +116,21 @@ export async function createRouter(
router.use(express.urlencoded({ extended: false }));
router.use(express.json());
const allProviderFactories = {
...defaultAuthProviderFactories,
...providerFactories,
};
const providersConfig = config.getConfig('auth.providers');
const configuredProviders = providersConfig.keys();
const allProviderFactories = options.disableDefaultProviderFactories
? providerFactories
: {
...defaultAuthProviderFactories,
...providerFactories,
};
const providersConfig = config.getOptionalConfig('auth.providers');
const isOriginAllowed = createOriginFilter(config);
for (const [providerId, providerFactory] of Object.entries(
allProviderFactories,
)) {
if (configuredProviders.includes(providerId)) {
if (providersConfig?.has(providerId)) {
logger.info(`Configuring auth provider: ${providerId}`);
try {
const provider = providerFactory({
+2
View File
@@ -4570,6 +4570,7 @@ __metadata:
resolution: "@backstage/plugin-auth-backend@workspace:plugins/auth-backend"
dependencies:
"@backstage/backend-common": "workspace:^"
"@backstage/backend-defaults": "workspace:^"
"@backstage/backend-plugin-api": "workspace:^"
"@backstage/backend-test-utils": "workspace:^"
"@backstage/catalog-client": "workspace:^"
@@ -4580,6 +4581,7 @@ __metadata:
"@backstage/plugin-auth-backend-module-gcp-iap-provider": "workspace:^"
"@backstage/plugin-auth-backend-module-google-provider": "workspace:^"
"@backstage/plugin-auth-node": "workspace:^"
"@backstage/plugin-catalog-node": "workspace:^"
"@backstage/types": "workspace:^"
"@davidzemon/passport-okta-oauth": ^0.0.5
"@google-cloud/firestore": ^6.0.0