5069: Forgejo integration is broken

enbyted

What version are you running?

Reviewboard 8.0 (docker latest on 10.06.2026)
Forgejo 14.0.5

What's the URL of the page containing the problem?

/admin/db/scmtools/repository/add/

What steps will reproduce the problem?

  • Try to add fogejo repository by providing URL, username and password, OR
  • Create a hosting account with pre-created PAT and try to create a forgejo repository with it

What is the expected output? What do you see instead?

I expect to be able to add the repository via the UI

Instead: linking fails, and Forgejo returns 403 {"message":"Only signed in user is allowed to call APIs."}. This message is Forgejo's response to an anonymous API request — i.e. Review Board sent no credentials.

What operating system are you using? What browser?

macOS Tahoe, Safari

Please provide any additional information below.

It seems that the Authorization headers are not passed correctly resulting in Forgejo responding with (for the first issue - creating the PAT failing):

2026-06-10 20:51:58,680 - DEBUG -  - reviewboard.hostingsvcs.base.http - [3f8b7855-ff12-4415-9472-d3869a2e4811] Begin: Performing HTTP POST request for Forgejo at https://<forgejo>/api/v1/users/bot.reviewboard/tokens
2026-06-10 20:51:58,723 - DEBUG -  - reviewboard.hostingsvcs.base.http - [3f8b7855-ff12-4415-9472-d3869a2e4811] End: Performing HTTP POST request for Forgejo at https://<forgejo>/api/v1/users/bot.reviewboard/tokens
2026-06-10 20:51:58,724 - DEBUG -  - reviewboard.hostingsvcs.base.http - [3f8b7855-ff12-4415-9472-d3869a2e4811] Performing HTTP POST request for Forgejo at https://<forgejo>/api/v1/users/bot.reviewboard/tokens took 0.043106 seconds
2026-06-10 20:51:58,724 - ERROR -  - reviewboard.hostingsvcs.forgejo.client - [be3bdcb7-507e-4281-a2b2-d8c2e78139d6] HTTP error (code=403) creating API token with POST https://<forgejo>/api/v1/users/bot.reviewboard/tokens: {"message":"Only signed in user is allowed to call APIs."}
2026-06-10 20:51:58,725 - ERROR -  - reviewboard.hostingsvcs.forgejo.client - [be3bdcb7-507e-4281-a2b2-d8c2e78139d6] Unexpected error (HTTP 403) when creating API token for user bot.reviewboard on server https://<forgejo>: {"message":"Only signed in user is allowed to call APIs."}
2026-06-10 20:51:58,726 - ERROR -  - reviewboard.hostingsvcs.base.forms - Unknown error linking hosting account ID=None for hosting service='forgejo', username='bot.reviewboard', LocalSite=None
Traceback (most recent call last):
  File "/venv/lib/python3.11/site-packages/reviewboard/hostingsvcs/forgejo/client.py", line 296, in create_api_token
    error = api.APIError.model_validate_json(data)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/venv/lib/python3.11/site-packages/pydantic/main.py", line 810, in model_validate_json
    return cls.__pydantic_validator__.validate_json(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pydantic_core._pydantic_core.ValidationError: 1 validation error for APIError
url
  Field required [type=missing, input_value={'message': 'Only signed ... allowed to call APIs.'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.14/v/missing
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/venv/lib/python3.11/site-packages/reviewboard/hostingsvcs/base/forms.py", line 534, in authorize
    hosting_account.service.authorize(username=username,
  File "/venv/lib/python3.11/site-packages/reviewboard/hostingsvcs/forgejo/service.py", line 151, in authorize
    api_token = self.client.create_api_token(
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/venv/lib/python3.11/site-packages/reviewboard/hostingsvcs/forgejo/client.py", line 306, in create_api_token
    raise HostingServiceError(
reviewboard.hostingsvcs.errors.HostingServiceError: Unknown error while creating Forgejo API token. See the Review Board server logs for more information (error ID be3bdcb7-507e-4281-a2b2-d8c2e78139d6).

For the second issue (fetching the repository failing):

[2026-06-10 21:19:52 +0000] [20] [DEBUG] POST /admin/db/scmtools/repository/add/
2026-06-10 21:19:52,708 - DEBUG -  - reviewboard.hostingsvcs.forgejo.client - Making GET request to https://<forgejo>/api/v1/repos/Elspace/tot2-core
2026-06-10 21:19:52,712 - DEBUG -  - reviewboard.hostingsvcs.base.http - [544b44fa-9610-4f46-a9f4-f126fc99c4dc] Begin: Performing HTTP GET request for Forgejo at https://<forgejo>/api/v1/repos/Elspace/tot2-core
2026-06-10 21:19:52,754 - DEBUG -  - reviewboard.hostingsvcs.base.http - [544b44fa-9610-4f46-a9f4-f126fc99c4dc] End: Performing HTTP GET request for Forgejo at https://<forgejo>/api/v1/repos/Elspace/tot2-core
2026-06-10 21:19:52,755 - DEBUG -  - reviewboard.hostingsvcs.base.http - [544b44fa-9610-4f46-a9f4-f126fc99c4dc] Performing HTTP GET request for Forgejo at https://<forgejo>/api/v1/repos/Elspace/tot2-core took 0.042300 seconds
2026-06-10 21:19:52,756 - ERROR -  - reviewboard.hostingsvcs.forgejo.client - HTTP error in API GET https://<forgejo>/api/v1/repos/Elspace/tot2-core for repository None: HTTP Error 403: Forbidden
2026-06-10 21:19:52,757 - ERROR -  - reviewboard.hostingsvcs.forgejo.client - Unable to parse error response for API GET https://<forgejo>/api/v1/repos/Elspace/tot2-core (repository=None): 1 validation error for APIError
url
  Field required [type=missing, input_value={'message': 'Only signed ... allowed to call APIs.'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.14/v/missing
2026-06-10 21:19:52,757 - WARNING -  - reviewboard.hostingsvcs.forgejo.client - Unexpected error when checking repository with API URL https://<forgejo>/api/v1/repos/Elspace/tot2-core: Unknown response from Forgejo: {"message":"Only signed in user is allowed to call APIs."}

[2026-06-10 21:19:53 +0000] [20] [DEBUG] Closing connection.

Having no clue how this software works I thought I was doing something wrong and fed the issues I was facing through Claude. It came up with the following patch for docker, for the second issue:

docker exec <container name> sed -i "s/'header': {/'headers': {/"   /venv/lib/python3.11/site-packages/reviewboard/hostingsvcs/forgejo/client.py

This allowed me to workaround the problem and create the hosting account like this:

# docker exec -it <container name> rb-site manage /site shell
>>> from reviewboard.hostingsvcs.models import HostingServiceAccount
>>> from reviewboard.scmtools.crypto_utils import encrypt_password
>>> 
>>> account = HostingServiceAccount.objects.create(
>>>     service_name='forgejo',
>>>     username='bot.reviewboard',
>>>     hosting_url='https://<forgejo>',
>>> )
>>> account.data['api_token'] = encrypt_password('PASTE_RAW_PAT_HERE')
>>> account.save()
>>> 
>>> # sanity check
>>> print(account.service.is_authorized())   # -> True

And then add the repository using that account.

Since I already had Claude look into this I asked it to generate analysis + suggested fixes in case that is of interest to you.

Thanks for making this software! Though I haven't had a chance of actually using it yet it seems like it will solve a lot of my issues.

Forgejo hosting service — root cause analysis and proposed fixes

Affected: Review Board 8.0, reviewboard/hostingsvcs/forgejo/client.py.

Both repository-linking failures (HTTP 403 {"message":"Only signed in user is allowed to call APIs."}) originate in the same method, ForgejoClient.get_http_credentials(). Forgejo returns that message for anonymous API requests — i.e. Review Board sends no Authorization header. There are two distinct bugs, plus one cosmetic issue that masks the real error.

Bug 1 — token-auth header returned under the wrong key (breaks every token-authenticated call)

get_http_credentials() returns the auth header under the key header (singular):

python return { 'header': { # singular 'Authorization': f'token {token}', }, }

But HostingServiceClient.build_http_request() in reviewboard/hostingsvcs/base/client.py reads the key headers (plural):

```python auth_headers = credentials.get('heade