Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix OIDC introspection authentication #31632

Merged
merged 13 commits into from
Jul 23, 2024

Conversation

slingamn
Copy link
Contributor

@slingamn slingamn commented Jul 15, 2024

See discussion on #31561 for some background.

The introspect endpoint was using the OIDC token itself for
authentication. This fixes it to use basic authentication with the
client ID and secret instead:

  • Applications with a valid client ID and secret should be able to
    successfully introspect an invalid token, receiving a 200 response
    with JSON data that indicates the token is invalid
  • Requests with an invalid client ID and secret should not be able
    to introspect, even if the token itself is valid

Unlike #31561 (which just future-proofed the current behavior against future changes to DISABLE_QUERY_AUTH_TOKEN), this is a potential compatibility break (some introspection requests without valid client IDs that would previously succeed will now fail). Affected deployments must begin sending a valid HTTP basic authentication header with their introspection requests, with the username set to a valid client ID and the password set to the corresponding client secret.

The introspect endpoint was using the OIDC token itself for
authentication. This fixes it to use basic authentication with the
client ID and secret instead:

* Applications with a valid client ID and secret should be able to
  successfully introspect an invalid token, receiving a 200 response
  with JSON data that indicates the token is invalid
* Requests with an invalid client ID and secret should not be able
  to introspect, even if the token itself is valid
@pull-request-size pull-request-size bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Jul 15, 2024
@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Jul 15, 2024
@github-actions github-actions bot added the modifies/go Pull requests that update Go code label Jul 15, 2024
routers/web/auth/oauth.go Outdated Show resolved Hide resolved
@GiteaBot GiteaBot added lgtm/blocked A maintainer has reservations with the PR and thus it cannot be merged and removed lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. labels Jul 15, 2024
@slingamn slingamn requested a review from lafriks July 15, 2024 06:41
@pull-request-size pull-request-size bot added size/M Denotes a PR that changes 30-99 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Jul 15, 2024
routers/web/auth/oauth.go Outdated Show resolved Hide resolved
@techknowlogick techknowlogick added the pr/breaking Merging this PR means builds will break. Needs a description what exactly breaks, and how to fix it! label Jul 15, 2024
routers/web/auth/oauth.go Outdated Show resolved Hide resolved
tests/integration/oauth_test.go Show resolved Hide resolved
@pull-request-size pull-request-size bot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Jul 16, 2024
@slingamn slingamn requested a review from lafriks July 16, 2024 05:16
routers/web/auth/oauth.go Outdated Show resolved Hide resolved
@slingamn slingamn requested a review from bohde July 16, 2024 14:34
@GiteaBot GiteaBot added lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. and removed lgtm/blocked A maintainer has reservations with the PR and thus it cannot be merged labels Jul 17, 2024
@slingamn
Copy link
Contributor Author

@techknowlogick re. your question on #31632 about integration testing, I think the automated integration test added here is a pretty good description of the flow: first you issue a token from /login/oauth/access_token, then you pass it to /login/oauth/introspect and get back the JSON introspection response.

I did a successful end-to-end test of an IRCv3 integration (it needed one additional patch, to add the username field to the introspection response, that I plan to submit separately later).

@delvh
Copy link
Member

delvh commented Jul 19, 2024

Why is this PR breaking?
Please describe in the PR description why this PR is breaking, and what affected users need to do.

@slingamn
Copy link
Contributor Author

I updated the description. tl;dr after this change, clients using the introspection endpoint will need to start sending a valid basic auth header.

I could also remove the basic auth check. Okta's rationale for it doesn't make sense to me:

If the introspection endpoint is left open and un-throttled, it presents a means for an attacker to poll the endpoint fishing for a valid token. To prevent this, the server must either require authentication of the clients using the endpoint, or only make the endpoint available to internal servers through other means such as a firewall. Note that the resources servers are also a potential target of a fishing attack, and should take countermeasures such as rate limiting to prevent this.

The tokens here are JWTs so they can't be brute-forced, and I don't think DoS is a real concern either. But I would like someone with more OIDC expertise to weigh in on this :-)

@techknowlogick
Copy link
Member

While rate-limiting is definitely a concern, it can be considered as out-of-scope for this PR. I also am not sure we need to be concerned with this opening up a venue for malicious users to see if a token is valid or not, as that can essentially be done with any endpoint.
Please let me know if I've misunderstood your latest post.
Thanks for your focus on improving the oauth experience :)

@lafriks
Copy link
Member

lafriks commented Jul 20, 2024

Usually introspection tokens are not jwt tokens so they cannot be validated and introspection endpoint is used to do that and that's why it requires separate auth so that only valid clients can do that. On the other hand even if jwt token is used introspection endpoint can be used to check if session is still valid even if jwt token is not expired itself

@slingamn
Copy link
Contributor Author

Just checking if there are still any open issues blocking a merge?

@6543 6543 enabled auto-merge (squash) July 23, 2024 12:13
@6543 6543 added the reviewed/wait-merge This pull request is part of the merge queue. It will be merged soon. label Jul 23, 2024
@6543 6543 merged commit 2f1cb1d into go-gitea:main Jul 23, 2024
26 checks passed
@GiteaBot GiteaBot added this to the 1.23.0 milestone Jul 23, 2024
@GiteaBot GiteaBot removed the reviewed/wait-merge This pull request is part of the merge queue. It will be merged soon. label Jul 23, 2024
silverwind added a commit to techknowlogick/gitea that referenced this pull request Jul 23, 2024
* origin/main: (59 commits)
  fix OIDC introspection authentication (go-gitea#31632)
  Enable direnv (go-gitea#31672)
  [skip ci] Updated translations via Crowdin
  [skip ci] Updated translations via Crowdin
  fix redis dep (go-gitea#31662)
  add skip secondary authorization option for public oauth2 clients (go-gitea#31454)
  Fix a branch divergence cache bug (go-gitea#31659)
  [skip ci] Updated translations via Crowdin
  Remove unneccessary uses of `word-break: break-all` (go-gitea#31637)
  [skip ci] Updated translations via Crowdin
  Allow searching issues by ID (go-gitea#31479)
  allow synchronizing user status from OAuth2 login providers (go-gitea#31572)
  Enable `no-jquery/no-class-state` (go-gitea#31639)
  Added default sorting milestones by name (go-gitea#27084)
  Code editor theme enhancements (go-gitea#31629)
  Add option to change mail from user display name (go-gitea#31528)
  Upgrade xorm to v1.3.9 and improve some migrations Sync (go-gitea#29899)
  Issue Templates: add option to have dropdown printed list (go-gitea#31577)
  Fix update flake (go-gitea#31626)
  [skip ci] Updated translations via Crowdin
  ...
zjjhot added a commit to zjjhot/gitea that referenced this pull request Jul 24, 2024
* giteaofficial/main:
  use nolyfill to remove some polyfills (go-gitea#31468)
  Properly filter issue list given no assignees filter (go-gitea#31522)
  Run `detectWebAuthnSupport` only on sign-in page (go-gitea#31676)
  fix OIDC introspection authentication (go-gitea#31632)
  Enable direnv (go-gitea#31672)
  [skip ci] Updated translations via Crowdin
  [skip ci] Updated translations via Crowdin
@@ -326,10 +325,29 @@ func getOAuthGroupsForUser(ctx go_context.Context, user *user_model.User) ([]str
return groups, nil
}

func parseBasicAuth(ctx *context.Context) (username, password string, err error) {
authHeader := ctx.Req.Header.Get("Authorization")
if authType, authData, ok := strings.Cut(authHeader, " "); ok && authType == "Basic" {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe auth scheme "Basic" should be compare case insensitively

"Note that both scheme and parameter names are matched case-insensitively."
https://www.rfc-editor.org/rfc/rfc7617.html#section-2

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. @slingamn was that a deliberate change or we just missed it in reviewing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't intentional :-) I just refactored the original implementation (#6293) which was case-sensitive. So there's no behavior change, but I agree that a case-insensitive comparison would be more correct.

techknowlogick pushed a commit that referenced this pull request Jul 26, 2024
@kylef pointed out on #31632 that
[RFC7617](https://www.rfc-editor.org/rfc/rfc7617.html#section-2)
mandates case-insensitive comparison of the scheme field `Basic`. #31632
copied a case-sensitive comparison from
#6293. This PR fixes both
comparisons.

The issue only affects OIDC, since the implementation for normal Gitea
endpoints is already correct:


https://github.com/go-gitea/gitea/blob/930ca92d7ce80e8b0bdaf92e495026baf2a1d419/services/auth/basic.go#L55-L58
DennisRasey pushed a commit to DennisRasey/forgejo that referenced this pull request Aug 8, 2024
These are the three conflicted changes from #4716:

* go-gitea/gitea#31632
* go-gitea/gitea#31688
* go-gitea/gitea#31706

cc @earl-warren; as per discussion on go-gitea/gitea#31632 this involves a small compatibility break (OIDC introspection requests now require a valid client ID and secret, instead of a valid OIDC token)

## Checklist

The [developer guide](https://forgejo.org/docs/next/developer/) contains information that will be helpful to first time contributors. 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

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

### Documentation

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

### Release notes

- [ ] I do not want this change to show in the release notes.
- [ ] I want the title to show in the release notes with a link to this pull request.
- [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title.

<!--start release-notes-assistant-->

## Draft release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Breaking features
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/4724): <!--number 4724 --><!--line 0 --><!--description T0lEQyBpbnRlZ3JhdGlvbnMgdGhhdCBQT1NUIHRvIGAvbG9naW4vb2F1dGgvaW50cm9zcGVjdGAgd2l0aG91dCBzZW5kaW5nIEhUVFAgYmFzaWMgYXV0aGVudGljYXRpb24gd2lsbCBub3cgZmFpbCB3aXRoIGEgNDAxIEhUVFAgVW5hdXRob3JpemVkIGVycm9yLiBUbyBmaXggdGhlIGVycm9yLCB0aGUgY2xpZW50IG11c3QgYmVnaW4gc2VuZGluZyBIVFRQIGJhc2ljIGF1dGhlbnRpY2F0aW9uIHdpdGggYSB2YWxpZCBjbGllbnQgSUQgYW5kIHNlY3JldC4gVGhpcyBlbmRwb2ludCB3YXMgcHJldmlvdXNseSBhdXRoZW50aWNhdGVkIHZpYSB0aGUgaW50cm9zcGVjdGlvbiB0b2tlbiBpdHNlbGYsIHdoaWNoIGlzIGxlc3Mgc2VjdXJlLg==-->OIDC integrations that POST to `/login/oauth/introspect` without sending HTTP basic authentication will now fail with a 401 HTTP Unauthorized error. To fix the error, the client must begin sending HTTP basic authentication with a valid client ID and secret. This endpoint was previously authenticated via the introspection token itself, which is less secure.<!--description-->
<!--end release-notes-assistant-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/4724
Reviewed-by: Earl Warren <[email protected]>
Co-authored-by: Shivaram Lingamneni <[email protected]>
Co-committed-by: Shivaram Lingamneni <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lgtm/done This PR has enough approvals to get merged. There are no important open reservations anymore. modifies/go Pull requests that update Go code pr/breaking Merging this PR means builds will break. Needs a description what exactly breaks, and how to fix it! size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants