diff --git a/plugins/backstage-plugin-coder-backend/README.md b/plugins/backstage-plugin-coder-backend/README.md index 0c6efe34..bdcca169 100644 --- a/plugins/backstage-plugin-coder-backend/README.md +++ b/plugins/backstage-plugin-coder-backend/README.md @@ -6,12 +6,50 @@ ## Features - Management of OAuth2 state for requests sent from the Backstage backend. +- Provides OAuth callback endpoint at `/api/coder/oauth/callback` ## Installing the plugin to support oauth2 +### For the New Backend System + +If you're using Backstage's new backend system (v1.20+), follow these steps: + +1. Run the following command from your Backstage app to install the plugin: + ```bash + yarn --cwd packages/backend add @coder/backstage-plugin-coder-backend + ``` +2. Add the module to your backend in `packages/backend/src/index.ts`: + ```ts + backend.add(import('@coder/backstage-plugin-coder-backend')); + ``` +3. [If you haven't already, be sure to register Backstage as an oauth app through Coder](https://coder.com/docs/admin/integrations/oauth2-provider). + + When registering the OAuth application, use the following callback URL format: + ``` + https://backstage.example.com/api/coder/oauth/callback + ``` + Replace `backstage.example.com` with your actual Backstage domain. + +4. Add the following values to one of your `app-config.yaml` files: + ```yaml + coder: + deployment: + # Change the value to match your Coder deployment + accessUrl: https://dev.coder.com + oauth: + clientId: oauth2-client-id-goes-here + # The client secret isn't used by the frontend plugin, but the backend + # plugin needs it for oauth functionality to work + clientSecret: oauth2-secret-goes-here + ``` + +Note that the `clientSecret` value is given `secret`-level visibility, and will never be logged anywhere by Backstage. + +### For the Legacy Backend System + 1. Run the following command from your Backstage app to install the plugin: ```bash - yarn --cwd packages/app add @coder/backstage-plugin-coder + yarn --cwd packages/backend add @coder/backstage-plugin-coder-backend ``` 2. Import the `createRouter` function from the `@coder/backstage-plugin-coder` package: ```ts @@ -25,7 +63,7 @@ 4. Register the plugin's oauth route with Backstage from inside the same `main` function: ```ts apiRouter.use( - '/auth/coder', + '/coder', await createCoderRouter({ logger: coderEnv.logger, config: coderEnv.config, @@ -33,6 +71,13 @@ ); ``` 5. [If you haven't already, be sure to register Backstage as an oauth app through Coder](https://coder.com/docs/admin/integrations/oauth2-provider). + + When registering the OAuth application, use the following callback URL format: + ``` + https://backstage.example.com/api/coder/oauth/callback + ``` + Replace `backstage.example.com` with your actual Backstage domain. + 6. Add the following values to one of your `app-config.yaml` files: ```yaml coder: diff --git a/plugins/backstage-plugin-coder-backend/package.json b/plugins/backstage-plugin-coder-backend/package.json index 361d4468..70040cfc 100644 --- a/plugins/backstage-plugin-coder-backend/package.json +++ b/plugins/backstage-plugin-coder-backend/package.json @@ -24,6 +24,7 @@ }, "dependencies": { "@backstage/backend-common": "^0.20.1", + "@backstage/backend-plugin-api": "^0.6.0", "@backstage/config": "^1.1.1", "@backstage/errors": "^1.2.3", "@types/express": "*", diff --git a/plugins/backstage-plugin-coder-backend/src/index.ts b/plugins/backstage-plugin-coder-backend/src/index.ts index 9f158c79..d9c7e2eb 100644 --- a/plugins/backstage-plugin-coder-backend/src/index.ts +++ b/plugins/backstage-plugin-coder-backend/src/index.ts @@ -4,4 +4,8 @@ * @packageDocumentation */ +// Legacy API for old backend system export { createRouter } from './service/router'; + +// New backend system module +export { default } from './module'; diff --git a/plugins/backstage-plugin-coder-backend/src/module.ts b/plugins/backstage-plugin-coder-backend/src/module.ts new file mode 100644 index 00000000..eacb176f --- /dev/null +++ b/plugins/backstage-plugin-coder-backend/src/module.ts @@ -0,0 +1,50 @@ +import { + coreServices, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; +import { createRouter } from './service/router'; + +/** + * Coder OAuth backend plugin for the new Backstage backend system. + * + * This plugin provides OAuth authentication routes for Coder at /api/coder/oauth/callback. + * + * @public + */ +export default createBackendPlugin({ + pluginId: 'coder', + register(env) { + env.registerInit({ + deps: { + logger: coreServices.logger, + config: coreServices.rootConfig, + httpRouter: coreServices.httpRouter, + }, + async init({ logger, config, httpRouter }) { + logger.info('Initializing Coder OAuth plugin'); + + const router = await createRouter({ + logger: logger as any, + config, + }); + + // Allow unauthenticated access to OAuth routes + httpRouter.addAuthPolicy({ + path: '/oauth', + allow: 'unauthenticated', + }); + + httpRouter.addAuthPolicy({ + path: '/health', + allow: 'unauthenticated', + }); + + // Register the router (will be mounted at /api/coder) + httpRouter.use(router); + + logger.info('Coder OAuth plugin initialized at /api/coder'); + }, + }); + }, +}); + diff --git a/plugins/backstage-plugin-coder-backend/src/service/router.ts b/plugins/backstage-plugin-coder-backend/src/service/router.ts index afdbdde8..08869b24 100644 --- a/plugins/backstage-plugin-coder-backend/src/service/router.ts +++ b/plugins/backstage-plugin-coder-backend/src/service/router.ts @@ -34,7 +34,7 @@ export async function createRouter( const clientSecret = coderConfig?.getString('oauth.clientSecret') || ''; const redirectUri = `${req.protocol}://${req.get( 'host', - )}/api/auth/coder/oauth/callback`; + )}/api/coder/oauth/callback`; let tokenResponse: AxiosResponse<{ access_token?: string }, unknown>; try { diff --git a/plugins/backstage-plugin-coder/src/components/CoderAuthForm/CoderAuthInputForm.tsx b/plugins/backstage-plugin-coder/src/components/CoderAuthForm/CoderAuthInputForm.tsx index 32722eb3..f8198b7f 100644 --- a/plugins/backstage-plugin-coder/src/components/CoderAuthForm/CoderAuthInputForm.tsx +++ b/plugins/backstage-plugin-coder/src/components/CoderAuthForm/CoderAuthInputForm.tsx @@ -159,7 +159,7 @@ export const CoderAuthInputForm = () => { state: btoa(JSON.stringify({ returnTo: window.location.pathname })), response_type: 'code', client_id: clientId, - redirect_uri: `${backendUrl}/api/auth/coder/oauth/callback`, + redirect_uri: `${backendUrl}/api/coder/oauth/callback`, }); const oauthUrl = `${