jojo/models
Mathieu Fenniak 48218c654b feat: authorized integrations DB models and authentication implementation (#12261)
Authorized Integrations is a new feature to allow users to define external systems which can generate JSON Web Tokens (JWTs) that Forgejo will trust in order to perform API access on behalf of that user.  This is an authentication mechanism that requires zero preconfiguration of shared secrets, and instead establishes trust through short-lived secrets (JWTs) that are signed by the issuer, signatures are validated by comparison with published public keys, and a public-keys retrieved through well-known HTTP endpoints secured with TLS verification.

The primary goal of Authorized Integrations is to support a mechanism for Forgejo Actions to receive elevated, but controlled, additional access to Forgejo.  More details as to what the end result will look like are available in the [design proposal](https://codeberg.org/forgejo/forgejo/issues/3571#issuecomment-13268004) on #3571.

This PR adds the core database storage and authentication verification for Authorized Integrations, with these capabilities:
- An Authorized Integration is resolved by a unique key of an "issuer" and an "audience".  The value of "issuer" is defined by the remote integration, and the value of "audience" will incorporate a unique identifier generated by Forgejo.
    - Example issuer: `https://token.actions.githubusercontent.com/` is the issuer for GitHub JWTs
    - Example audience: `https://forgejo.example.org/-/mfenniak/authorized-integration/6cc55ba0` is the expected format for a random audience field that Forgejo will generate.
- JWTs can contain any number of claims, which are represented as a JSON object; Forgejo can validate these with a flexible policy.
    - eg. a claim may be `{"sub": "repo:coolguy/forgejo-runner-testrepo:pull_request"}` indicating that an OIDC token was received from an Actions execution in a specific repo on a specific event.
    - Authorized Integrations support a `ClaimRules` system which allows claim equal, glob, and nested object inspection.
    - `{"claim":"sub","comparison":"eq","value":"repo:mfenniak/forgejo-runner-testrepo:pull_request"}` -- would validate that `sub` exactly equals the specific value
    - `{"claim":"sub","comparison":"glob","value":"repo:mfenniak/forgejo-runner-testrepo:*"}` -- would validate that `sub` matches the given string prefix but allow any event
- When a JWT is received on an incoming API call, Forgejo retrieves the Authorized Integration from the DB (if present), validates the token signature against a remote JWKS, validates the claims, and grants API access as the user with a permission scope defined on the Authorized Integration.

In addition to the unit testing provided here, this PR has been manually integration tested against three JWT issuing systems: Forgejo Actions, GitHub Actions, and AWS STS GetWebIdentityToken.

Careful consideration has been made of these security concerns:
- SSRF attacks against Forgejo are prevented by:
    - having a blocklist on remote HTTP validation requests which prevent access to internal network resources,
    - ensuring that authorized integrations are created by users with matching issuers, before attempting to validate tokens
- Resource utilization attacks against Forgejo are reduced by limiting the possible size of external metadata requests; when fetching `/.well-known/openid-configuration` and `jkws_uri`'s from remote, untrusted servers, a maximum response size of 16 kB is enforced
- Only well-known secure assymmetric JWT signing algorithms are supported -- in particular, the sketchy `none` JWT algorithm isn't supported.
- JWT validation is covered by extensive unit tests, covering validation of all JWT timestamps, validation of the issuers, validation of the issuer's documented supported signing algorithms.

This PR serves as a core, and many enhancements are required for this to be a usable system for users.

## Checklist

The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. All work and communication must conform to Forgejo's [AI Agreement](https://codeberg.org/forgejo/governance/src/branch/main/AIAgreement.md). There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).

### Tests for Go changes

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I ran...
  - [ ] `make pr-go` before pushing

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
    - Documentation updates for new config entries will be authored.
- [ ] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change.
- [x] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change.
    - Marking not visible as there's no mechanism to interact with this backend yet.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/12261
Reviewed-by: Andreas Ahlenstorf <aahlenst@noreply.codeberg.org>
2026-04-26 20:52:42 +02:00
..
actions fix: allow viewing Actions run triggered by deleted user (#12271) 2026-04-26 15:13:32 +02:00
activities feat: Follow remote users; feed tab (#10380) 2026-04-12 03:31:03 +02:00
admin feat: cache derived keys for faster keying (#10114) 2025-11-16 14:29:14 +01:00
asymkey chore: flag suspicious OwnerID comparisons (#12184) 2026-04-19 04:24:09 +02:00
auth feat: authorized integrations DB models and authentication implementation (#12261) 2026-04-26 20:52:42 +02:00
avatars feat(perf): remove unused size url parameter for local avatars (#10932) 2026-01-20 04:59:15 +01:00
db refactor: reduce code duplication when accessing DefaultMaxInSize (#11999) 2026-04-05 22:03:45 +02:00
dbfs chore: add modernizer linter (#11936) 2026-04-02 03:29:37 +02:00
fixtures Exclude SSH certificate principals from output when viewing user's SSH keys (#12079) 2026-04-17 17:17:29 +02:00
forgefed chore(federation): re-enable nilnil lint (#11253) 2026-04-13 22:05:29 +02:00
forgejo/semver chore: move all test blank imports in a single package (#10662) 2026-01-02 05:32:32 +01:00
forgejo_migrations feat: authorized integrations DB models and authentication implementation (#12261) 2026-04-26 20:52:42 +02:00
forgejo_migrations_legacy fix: normalize secrets consistently, display accurate help (#11052) 2026-02-09 17:02:18 +01:00
git chore: add modernizer linter (#11936) 2026-04-02 03:29:37 +02:00
gitea_migrations chore: add modernizer linter (#11936) 2026-04-02 03:29:37 +02:00
issues feat(api): add base and head query filters to list pull requests endpoint (#12104) 2026-04-16 19:51:46 +02:00
moderation feat: render a link to poster profile next to the ID within shadow copy details (#10194) 2025-12-09 15:19:10 +01:00
organization fix: add missing deleting beans for organizations (#11699) 2026-03-17 09:11:52 +01:00
packages fix: duplicate key violates unique constraint in concurrent debian package uploads (#11776) 2026-03-26 21:50:25 +01:00
perm chore: add modernizer linter (#11936) 2026-04-02 03:29:37 +02:00
project chore: flag suspicious OwnerID comparisons (#12184) 2026-04-19 04:24:09 +02:00
pull chore: add modernizer linter (#11936) 2026-04-02 03:29:37 +02:00
quota chore: move all test blank imports in a single package (#10662) 2026-01-02 05:32:32 +01:00
repo chore: flag suspicious OwnerID comparisons (#12184) 2026-04-19 04:24:09 +02:00
secret fix: secret name-prefix regex (#12213) 2026-04-21 19:55:16 +02:00
shared/types chore: branding import path (#7337) 2025-03-27 19:40:14 +00:00
system chore: move all test blank imports in a single package (#10662) 2026-01-02 05:32:32 +01:00
unit chore: add modernizer linter (#11936) 2026-04-02 03:29:37 +02:00
unittest fix: mark code comments as Outdated based upon line-of-code existence in current PR commit (#12054) 2026-04-11 23:10:34 +02:00
user chore(federation): re-enable nilnil lint (#11253) 2026-04-13 22:05:29 +02:00
webhook chore: add modernizer linter (#11936) 2026-04-02 03:29:37 +02:00
error.go fix: don't allow credentials in migrate/push mirror URL 2025-08-30 08:07:23 +02:00
main_test.go chore: move all test blank imports in a single package (#10662) 2026-01-02 05:32:32 +01:00
org.go chore: branding import path (#7337) 2025-03-27 19:40:14 +00:00
org_team.go chore: split AddRepository and AddTeamMember to return the inserted value (#11342) 2026-03-11 03:40:32 +01:00
org_team_test.go chore: split AddRepository and AddTeamMember to return the inserted value (#11342) 2026-03-11 03:40:32 +01:00
org_test.go Update module github.com/golangci/golangci-lint/cmd/golangci-lint to v2 (forgejo) (#7367) 2025-03-28 22:22:21 +00:00
repo.go fix: possible cause of invalid issue counts; cache invalidation occurs before a active transaction is committed (#10130) 2025-11-17 01:07:29 +01:00
repo_test.go Update module github.com/golangci/golangci-lint/cmd/golangci-lint to v2 (forgejo) (#7367) 2025-03-28 22:22:21 +00:00
repo_transfer.go chore: fix typos throughout the codebase (#10753) 2026-01-26 22:57:33 +01:00
repo_transfer_test.go chore: fix typos throughout the codebase (#10753) 2026-01-26 22:57:33 +01:00