diff --git a/.changeset/chatty-pens-try.md b/.changeset/chatty-pens-try.md new file mode 100644 index 0000000000..1c92bb82cb --- /dev/null +++ b/.changeset/chatty-pens-try.md @@ -0,0 +1,6 @@ +--- +'@backstage/integration': patch +--- + +Do not return a token rather than fail where the owner is not in the allowed installation owners +for a GitHub app. This allows anonymous access to public files in the organisation. diff --git a/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.test.ts b/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.test.ts index e5c6307338..246a859b7a 100644 --- a/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.test.ts +++ b/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.test.ts @@ -136,6 +136,44 @@ describe('SingleInstanceGithubCredentialsProvider tests', () => { expect(token).toEqual('secret_token'); }); + it('does not return a token where the organisation is not in the allowedInstallationsList', async () => { + github = SingleInstanceGithubCredentialsProvider.create({ + host: 'github.com', + apps: [ + { + appId: 1, + privateKey: 'privateKey', + webhookSecret: '123', + clientId: 'CLIENT_ID', + clientSecret: 'CLIENT_SECRET', + allowedInstallationOwners: ['backstage'], + }, + ], + }); + + octokit.apps.listInstallations.mockResolvedValue({ + headers: { + etag: '123', + }, + data: [ + { + id: 1, + repository_selection: 'all', + account: { + login: 'backstage', + }, + }, + ], + } as RestEndpointMethodTypes['apps']['listInstallations']['response']); + + const { token, headers } = await github.getCredentials({ + url: 'https://github.com/RoadiehHQ', + }); + + expect(headers).toEqual(undefined); + expect(token).toEqual(undefined); + }); + it('should not fail to issue tokens for an organization when the app is installed for a single repo', async () => { octokit.apps.listInstallations.mockResolvedValue({ headers: { diff --git a/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts b/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts index 408c5b1348..bfc360cbe2 100644 --- a/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts +++ b/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts @@ -92,13 +92,11 @@ class GithubAppManager { async getInstallationCredentials( owner: string, repo?: string, - ): Promise<{ accessToken: string }> { + ): Promise<{ accessToken: string | undefined }> { const { installationId, suspended } = await this.getInstallationData(owner); if (this.allowedInstallationOwners) { if (!this.allowedInstallationOwners?.includes(owner)) { - throw new Error( - `The GitHub application for ${owner} is not included in the allowed installation list (${installationId}).`, - ); + return { accessToken: undefined }; // An empty token allows anonymous access to public repos } } if (suspended) {