Repository: backstage/community-plugins
Pull request #3469: Add Support for SonarQube Cloud to SonarQube Plugin
Author: login: RedlineTriad
Created: 2025-03-25T14:19:25Z | Merged: 2025-04-22T15:49:07Z

## Hey, I just made a Pull Request!

SonarQube [Cloud](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/web-api/#authenticate-to-api) and [Server](https://docs.sonarsource.com/sonarqube-server/10.8/extension-guide/web-api/#authenticate-to-api) both support bearer auth.

However SonarQube Server also supports [basic auth](https://docs.sonarsource.com/sonarqube-server/10.8/user-guide/managing-tokens/#using-a-token).

By switching to bearer auth we make it work with both.
I tested this locally with both SonarQube Server and SonarQube Cloud and existing Basic Auth tokens work without issues.

I also updated the docs to make it more obvious what changes are necessary for the new backend system.

The commits also include individual explanations for the changes.

#### :heavy_check_mark: Checklist

<!--- Please include the following in your Pull Request when applicable: -->

- [x] A changeset describing the change and affected packages. ([more info](https://github.com/backstage/community-plugins/blob/master/CONTRIBUTING.md#creating-changesets))
- [x] Added or updated documentation
- [x] Tests for new functionality and regression tests for bug fixes
- [ ] Screenshots attached (for UI changes)
- [x] All your commits have a `Signed-off-by` line in the message. ([more info](https://github.com/backstage/community-plugins/blob/master/CONTRIBUTING.md#developer-certificate-of-origin))



Diff:
diff --git a/workspaces/sonarqube/.changeset/proud-jobs-cross.md b/workspaces/sonarqube/.changeset/proud-jobs-cross.md
new file mode 100644
index 00000000000..efe40a843af
--- /dev/null
+++ b/workspaces/sonarqube/.changeset/proud-jobs-cross.md
@@ -0,0 +1,7 @@
+---
+'@backstage-community/plugin-sonarqube-backend': minor
+---
+
+Switch from basic auth to recommended<sup>[[1]](https://docs.sonarsource.com/sonarqube-cloud/advanced-setup/web-api/#authenticate-to-api), [[2]](https://docs.sonarsource.com/sonarqube-server/2025.1/extension-guide/web-api/#authenticate-to-api)</sup> bearer auth in order to also support SonarQube Cloud.
+
+Basic auth as used previously is exlusively [supported](https://docs.sonarsource.com/sonarqube-server/10.8/user-guide/managing-tokens/#using-a-token) by Sonarqube Server.
diff --git a/workspaces/sonarqube/README.md b/workspaces/sonarqube/README.md
index 53f0f0a26b8..9ec7cde5370 100644
--- a/workspaces/sonarqube/README.md
+++ b/workspaces/sonarqube/README.md
@@ -1,13 +1,5 @@
 # [Backstage](https://backstage.io)
 
-This is your newly scaffolded Backstage App, Good Luck!
-
-For testing sonarqube, set the relevant env variables:
-
-1. [Create an OAuth App](https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/) in the GitHub organization with the callback URL set to `http://localhost:7007/api/auth/github/handler/frame`.
-2. Take the Client ID and Client Secret from the newly created app's settings page and put them into `AUTH_GITHUB_CLIENT_ID` and `AUTH_GITHUB_CLIENT_SECRET` environment variables.
-3. Generate a personal access token and put it into the `GITHUB_TOKEN` enviornment variable. You can generate a personal access token from your GitHub settings. It will need the repo and workflow scopes.
-
 The [backstage example entity](./examples/entities.yaml) has a populated `sonarqube.org/project-key` annotation, but you can modify this to point anywhere your credentials have access to for testing purposes.
 
 To start the app, run:
@@ -16,3 +8,8 @@ To start the app, run:
 yarn install
 yarn dev
 ```
+
+Follow these links to learn more about this plugin:
+
+1. [Frontend](./plugins/sonarqube/README.md)
+2. [Backend](./plugins/sonarqube-backend/README.md)
diff --git a/workspaces/sonarqube/plugins/sonarqube-backend/README.md b/workspaces/sonarqube/plugins/sonarqube-backend/README.md
index 9f38c06e6bd..b851a94845a 100644
--- a/workspaces/sonarqube/plugins/sonarqube-backend/README.md
+++ b/workspaces/sonarqube/plugins/sonarqube-backend/README.md
@@ -2,6 +2,13 @@
 
 Welcome to the sonarqube-backend backend plugin!
 
+This plugin needs to be added to an existing backstage instance.
+
+```bash
+# From your Backstage root directory
+yarn --cwd packages/backend add @backstage-community/plugin-sonarqube-backend
+```
+
 ## New Backend System
 
 The Sonarqube backend plugin has support for the [new backend system](https://backstage.io/docs/backend-system/), here's how you can set that up:
@@ -16,14 +23,7 @@ In your `packages/backend/src/index.ts` make the following changes:
   backend.start();
 ```
 
-## Integrating into a backstage instance
-
-This plugin needs to be added to an existing backstage instance.
-
-```bash
-# From your Backstage root directory
-yarn --cwd packages/backend add @backstage-community/plugin-sonarqube-backend
-```
+## Old Backend System
 
 Typically, this means creating a `src/plugins/sonarqube.ts` file and adding a reference to it to `src/index.ts` in the backend package.
 
@@ -84,6 +84,8 @@ index 1942c36ad1..7fdc48ba24 100644
 
 ```
 
+## SonarqubeInfoProvider
+
 This plugin must be provided with a `SonarqubeInfoProvider`, this is a strategy object for finding Sonarqube instances in configuration and retrieving data from an instance.
 
 There is a standard one provided (`DefaultSonarqubeInfoProvider`), but the Integrator is free to build their own.
@@ -131,6 +133,10 @@ sonarqube:
       instanceKey: mySonarqube
       baseUrl: https://special-project-sonarqube.example.com
       apiKey: abcdef0123456789abcedf0123456789ab
+    - name: cloud
+      instanceKey: sonarcloud
+      baseUrl: https://sonarcloud.io
+      apiKey: 0123456789abcedf012123456789abcdef
 ```
 
 ##### Catalog
diff --git a/workspaces/sonarqube/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.test.ts b/workspaces/sonarqube/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.test.ts
index ac733eec418..ea596a6ad40 100644
--- a/workspaces/sonarqube/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.test.ts
+++ b/workspaces/sonarqube/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.test.ts
@@ -357,10 +357,10 @@ describe('DefaultSonarqubeInfoProvider', () => {
     const checkBasicAuthToken = (req: RestRequest<never>) => {
       if (req.headers && req.headers.has('Authorization')) {
         expect(req.headers.get('Authorization')).toEqual(
-          `Basic MTIzNDU2Nzg5YWJjZGVmMDEyMzQ1Njc4OWFiY2VkZjAxMjo=`,
+          `Bearer 123456789abcdef0123456789abcedf012`,
         );
       } else {
-        throw new Error('Basic auth token not provided');
+        throw new Error('Bearer token not provided');
       }
     };
 
diff --git a/workspaces/sonarqube/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.ts b/workspaces/sonarqube/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.ts
index 1f8fcd22271..5b73879ae43 100644
--- a/workspaces/sonarqube/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.ts
+++ b/workspaces/sonarqube/plugins/sonarqube-backend/src/service/sonarqubeInfoProvider.ts
@@ -298,17 +298,12 @@ export class DefaultSonarqubeInfoProvider implements SonarqubeInfoProvider {
     authToken: string,
     query: { [key in string]: any },
   ): Promise<T | undefined> {
-    // Sonarqube auth use basic with token as username and no password
-    // but standard dictate the colon (separator) need to stay here despite the
-    // lack of password
-    const encodedAuthToken = Buffer.from(`${authToken}:`).toString('base64');
-
     const fullUrl = `${url}/${path}?${new URLSearchParams(query).toString()}`;
 
     const response = await fetch(fullUrl, {
       headers: {
         'Content-Type': 'application/json',
-        Authorization: `Basic ${encodedAuthToken}`,
+        Authorization: `Bearer ${authToken}`,
       },
     });
     if (!response.ok) {
diff --git a/workspaces/sonarqube/plugins/sonarqube/README.md b/workspaces/sonarqube/plugins/sonarqube/README.md
index d2b93eeb12f..5f913d07cab 100644
--- a/workspaces/sonarqube/plugins/sonarqube/README.md
+++ b/workspaces/sonarqube/plugins/sonarqube/README.md
@@ -10,7 +10,7 @@ The SonarQube Plugin displays code statistics from [SonarCloud](https://sonarclo
 
 ```bash
 # From your Backstage root directory
-yarn --cwd packages/app add @backstage-community/plugin-sonarqube
+yarn --cwd packages/app add @backstage-community/plugin-sonarqube @backstage-community/plugin-sonarqube-react
 ```
 
 2. Add the `EntitySonarQubeCard` to the EntityPage:
@@ -18,6 +18,7 @@ yarn --cwd packages/app add @backstage-community/plugin-sonarqube
 ```diff
   // packages/app/src/components/catalog/EntityPage.tsx
 + import { EntitySonarQubeCard } from '@backstage-community/plugin-sonarqube';
++ import { isSonarQubeAvailable } from '@backstage-community/plugin-sonarqube-react';
 
  ...
 
@@ -26,9 +27,13 @@ yarn --cwd packages/app add @backstage-community/plugin-sonarqube
      <Grid item md={6}>
        <EntityAboutCard variant="gridItem" />
      </Grid>
-+    <Grid item md={6}>
-+      <EntitySonarQubeCard variant="gridItem" />
-+    </Grid>
++    <EntitySwitch>
++      <EntitySwitch.Case if={isSonarQubeAvailable}>
++        <Grid item md={6}>
++          <EntitySonarQubeCard variant="gridItem" />
++        </Grid>
++      </EntitySwitch.Case>
++    </EntitySwitch>
    </Grid>
  );
 ```


Reviews:
user: login: niallmccullagh
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T20:24:52Z
user: login: niallmccullagh
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T20:26:31Z
user: login: RedlineTriad
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T15:23:43Z
user: login: awanlin
body: Thanks for this @redlinetriad, over all this makes sense but would like feedback on this from current Adopters. I did leave a few comments.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T13:32:12Z
user: login: RedlineTriad
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T13:49:22Z
user: login: awanlin
body: I'm good with moving forward with this but would like to make sure we include some links in the changeset from the SonarQube docs showing that it's recommend to do.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T17:36:44Z
user: login: awanlin
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T17:37:39Z
user: login: RedlineTriad
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T15:40:04Z
user: login: awanlin
body: Alright, I think we should move forward. Thanks again @RedlineTriad 🙏 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T15:43:37Z

Review comments:
diff_hunk: @@ -0,0 +1,5 @@
+---
+'@backstage-community/plugin-sonarqube-backend': minor
path: workspaces/sonarqube/.changeset/proud-jobs-cross.md
user: login: niallmccullagh
body: This is a breaking change for people using Basic Auth and would need to be marked as one.
created_at: 2025-03-26T20:24:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+---
+'@backstage-community/plugin-sonarqube-backend': minor
path: workspaces/sonarqube/.changeset/proud-jobs-cross.md
user: login: niallmccullagh
body: Also is there any reason why both basic auth and bearer auth?
created_at: 2025-03-26T20:26:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+---
+'@backstage-community/plugin-sonarqube-backend': minor
path: workspaces/sonarqube/.changeset/proud-jobs-cross.md
user: login: RedlineTriad
body: > This is a breaking change for people using Basic Auth and would need to be marked as one.

I tested it and the same basic auth tokens work using bearer auth instead on both Server and Cloud, so this shouldn't be a breaking change unless you have some very strange proxies enforcing the type of Authorization header.

> Also is there any reason why both basic auth and bearer auth?

Not sure what you mean, only bearer auth would be used with this change, which is the recommended method.
created_at: 2025-03-27T15:23:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,18 +1,13 @@
 # [Backstage](https://backstage.io)
 
-This is your newly scaffolded Backstage App, Good Luck!
-
-For testing sonarqube, set the relevant env variables:
-
-1. [Create an OAuth App](https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/) in the GitHub organization with the callback URL set to `http://localhost:7007/api/auth/github/handler/frame`.
-2. Take the Client ID and Client Secret from the newly created app's settings page and put them into `AUTH_GITHUB_CLIENT_ID` and `AUTH_GITHUB_CLIENT_SECRET` environment variables.
-3. Generate a personal access token and put it into the `GITHUB_TOKEN` enviornment variable. You can generate a personal access token from your GitHub settings. It will need the repo and workflow scopes.
-
-The [backstage example entity](./examples/entities.yaml) has a populated `sonarqube.org/project-key` annotation, but you can modify this to point anywhere your credentials have access to for testing purposes.
path: workspaces/sonarqube/README.md
user: login: awanlin
body: This section about the annotation is still worthwhile to know about, is it somewhere else? If not we should keep it.
created_at: 2025-04-02T12:48:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,5 +1,12 @@
 # sonarqube-backend
 
+This plugin needs to be added to an existing backstage instance.
+
+```bash
+# From your Backstage root directory
+yarn --cwd packages/backend add @backstage-community/plugin-sonarqube-backend
+```
+
 Welcome to the sonarqube-backend backend plugin!
path: workspaces/sonarqube/plugins/sonarqube-backend/README.md
user: login: awanlin
body: This line probably needs to be above this new section, seems a bit out of place here.
created_at: 2025-04-02T12:49:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -10,14 +10,15 @@ The SonarQube Plugin displays code statistics from [SonarCloud](https://sonarclo
 
 ```bash
 # From your Backstage root directory
-yarn --cwd packages/app add @backstage-community/plugin-sonarqube
+yarn --cwd packages/app add @backstage-community/plugin-sonarqube @backstage-community/plugin-sonarqube-react
 ```
 
 2. Add the `EntitySonarQubeCard` to the EntityPage:
 
 ```diff
   // packages/app/src/components/catalog/EntityPage.tsx
 + import { EntitySonarQubeCard } from '@backstage-community/plugin-sonarqube';
++ import { isSonarQubeAvailable } from '@backstage-community/plugin-sonarqube-react';
path: workspaces/sonarqube/plugins/sonarqube/README.md
user: login: awanlin
body: It's really too bad this is deprecated in the frontend plugin and you need to add the `-react` plugin just for this one part for a essentially standard install and setup. Not your fault, this change makes sense to have 👍 
created_at: 2025-04-02T13:10:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -16,14 +23,7 @@ In your `packages/backend/src/index.ts` make the following changes:
   backend.start();
 ```
 
-## Integrating into a backstage instance
-
-This plugin needs to be added to an existing backstage instance.
-
-```bash
-# From your Backstage root directory
-yarn --cwd packages/backend add @backstage-community/plugin-sonarqube-backend
-```
+## Old Backend System
path: workspaces/sonarqube/plugins/sonarqube-backend/README.md
user: login: awanlin
body: Note to self: we should remove support for the legacy backend as it's been deprecated in this plugin, not needed to be done thin this PR though.
created_at: 2025-04-02T13:20:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,18 +1,13 @@
 # [Backstage](https://backstage.io)
 
-This is your newly scaffolded Backstage App, Good Luck!
-
-For testing sonarqube, set the relevant env variables:
-
-1. [Create an OAuth App](https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/) in the GitHub organization with the callback URL set to `http://localhost:7007/api/auth/github/handler/frame`.
-2. Take the Client ID and Client Secret from the newly created app's settings page and put them into `AUTH_GITHUB_CLIENT_ID` and `AUTH_GITHUB_CLIENT_SECRET` environment variables.
-3. Generate a personal access token and put it into the `GITHUB_TOKEN` enviornment variable. You can generate a personal access token from your GitHub settings. It will need the repo and workflow scopes.
-
-The [backstage example entity](./examples/entities.yaml) has a populated `sonarqube.org/project-key` annotation, but you can modify this to point anywhere your credentials have access to for testing purposes.
path: workspaces/sonarqube/README.md
user: login: RedlineTriad
body: Thanks, didn't actually notice the custom description part, will of course re-add that.
created_at: 2025-04-02T13:49:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+---
+'@backstage-community/plugin-sonarqube-backend': minor
+---
+
+Switch from basic auth to recommended bearer auth to support SonarQube Cloud too.
path: workspaces/sonarqube/.changeset/proud-jobs-cross.md
user: login: awanlin
body: Can we add a link to the docs showing that it's recommend for both on-prem and cloud, please?
created_at: 2025-04-19T17:31:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+---
+'@backstage-community/plugin-sonarqube-backend': minor
path: workspaces/sonarqube/.changeset/proud-jobs-cross.md
user: login: awanlin
body: @niallmccullagh, can you follow up? Can you test this? Would be good to get you feedback, I'm assuming you use this plugin.
created_at: 2025-04-19T17:37:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+---
+'@backstage-community/plugin-sonarqube-backend': minor
+---
+
+Switch from basic auth to recommended bearer auth to support SonarQube Cloud too.
path: workspaces/sonarqube/.changeset/proud-jobs-cross.md
user: login: RedlineTriad
body: Done, should be good now.
created_at: 2025-04-22T15:40:04Z
author_association: CONTRIBUTOR

PR comments:
user: login: backstage-goalie[bot]
created_at: 2025-03-25T14:27:20Z
body: ## Missing Changesets

The following package(s) are changed by this PR but do not have a changeset:

- **@backstage-community/plugin-sonarqube**

See [CONTRIBUTING.md](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md#creating-changesets) for more information about how to add changesets.


## Changed Packages

| Package Name | Package Path | Changeset Bump | Current Version |
|:-------------|:-------------|:--------------:|:----------------|
| @backstage-community/plugin-sonarqube-backend | workspaces/sonarqube/plugins/sonarqube-backend | **minor** | `v0.6.0` |
| @backstage-community/plugin-sonarqube | workspaces/sonarqube/plugins/sonarqube | **none** | `v0.11.2` |


<!-- changeset-feedback -->
author_association: CONTRIBUTOR
user: login: awanlin
created_at: 2025-04-02T12:46:05Z
body: Hi @dncrews, if you have some time, could you give this PR a look over? Would love a 2nd set of eyes from somone else using this plugin! 🙏 
author_association: CONTRIBUTOR
user: login: RedlineTriad
created_at: 2025-04-02T13:12:36Z
body: Also here is a local test of mine:
```sh
curl -o /dev/null -s -w "%{http_code}\n" -H "Authorization: Bearer $SONARCLOUD_TOKEN" $SONARCLOUD_URL
curl -o /dev/null -s -w "%{http_code}\n" -H "Authorization: Bearer $SONARQUBE_TOKEN" $SONARQUBE_URL
curl -o /dev/null -s -w "%{http_code}\n" -u $SONARQUBE_TOKEN: $SONARQUBE_URL
curl -o /dev/null -s -w "%{http_code}\n" -u $SONARCLOUD_TOKEN: $SONARCLOUD_URL
200
200
200
404
```
URL/API used is the one below, and just the domain and component switched for on-prem version:
```sh
SONARCLOUD_URL="https://sonarcloud.io/api/components/show?component=MY_COMPONENT_NAME"
```
author_association: CONTRIBUTOR
user: login: RedlineTriad
created_at: 2025-04-02T14:01:35Z
body: Thanks for the review @awanlin ! Definitely missed some things, and the plugin isn't in a great state overall, but things will get better one PR at a time :D

Adjusted all commits to incorporate feedback.
author_association: CONTRIBUTOR
user: login: RedlineTriad
created_at: 2025-04-16T11:48:15Z
body: @awanlin Did no-one respond to the call for testing in two weeks now?
Would like to get this merged somewhat soon as otherwise I guess I will have to maintain a local fork to be able to use this sonarcloud, which is rather cumbersome when having things upstream is much nicer.
author_association: CONTRIBUTOR
user: login: alombardo4
created_at: 2025-04-29T19:27:51Z
body: Hey folks, just an FYI that v0.7.0 did break our setup with Sonarqube Enterprise server. I raised https://github.com/backstage/community-plugins/pull/3870 to set the default back to the old behavior and still allow Bearer auth to be used optionally.
author_association: CONTRIBUTOR