feat: implement Bearer token based API access#632
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #632 +/- ##
============================================
+ Coverage 74.60% 75.14% +0.53%
- Complexity 315 325 +10
============================================
Files 33 34 +1
Lines 1280 1340 +60
Branches 176 180 +4
============================================
+ Hits 955 1007 +52
- Misses 241 247 +6
- Partials 84 86 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Code Coverage does not seem to update in the PR here: I fixed the missed lines. The only line that is still missed cannot be tested as it is guaranteed not to happen in the code, so not sure what I should do with it. Could remove it, however the current way is recommended by Nimbus JWT, so I leave it up to the reviewer to decide whether I will stay with the recommendation or go with code coverage |
|
@jtnord Since the OidcProperty change has been merged, this PR is incompatible... Is it worth for me to put in effort again to make/keep it compatible again? Until now, neither the linked issue nor this PR received any attention it looks like. Are there any plans to accept this enhancement? |
I had started to look at it, but had not finished my review. I was concerned about security, esp re-using the disabling of token validation. In 2025 I do struggle to see the need to disable TLS or disable signing. There are also some other bits that would presumably need tweaking (from Jenkins core)? IIUC this should only be used for API type access (ie to replace an APIToken). In which case it would fall foul of the Jenkins-Crumb so API calls would not work as they do with Bearer tokens compared to API Tokens. e.g. https://github.com/jenkinsci/jenkins/blob/jenkins-2.516.2/core/src/main/java/jenkins/security/ApiCrumbExclusion.java I'm not against the notion of accepting Bearer tokens, but the feature would need to maintain security. |
I am absolutely with you and I am happy to remove that setting. My goal submitting this request was to respect as many existing settings as possible. From my point of view, that settings can not only be removed from being validated for the Bearer token access, but can be removed from the plugin entirely...
My personal use case is to replace API Tokens, but probably there could be other use cases as well. Tbh in my testing I have not had any issues with Crumb Processing whatsoever. I am also not too far into that topic actually (first time I am touching a Jenkins Plugin), so if it comes to the fine details I might need some help to get that straight. |
|
Hi @herglotzmarco / @jtnord - I am not deeply into this topic but am curious if your feature solves something what I a missing (a user can use a "token" to trigger builds although it is an SSO user and their groups are always in sync with the IdP) My use case is: A user logging in with SSO should also be able to use "a" token to trigger builds. My questions are:
|
@eva-mueller-coremedia To be honest I have no idea where the groups are updated at all. The call that will be performed using the OIDC token will run with the permissions granted by the groups passed in the token, but I am not sure if this state is then also persisted within Jenkins. Could be that the "updated" groups will only appear in Jenkins UI when the user signs in "normally" the next time.
I guess that should in theory be possible, but is not done in this PR |
Thank you for you quick reply! My security concern is (and this is the reason why I can't use the Jenkins API token approach): Assume: A user has the right to trigger a pipeline A doing the something in production (since the pipeline is restricted to be executed by users in the group => It needs to be ensured that - although the JWT does not contains the group
Maybe I will create a feature request here. @jtnord is it worth it? |
Using the Bearer Token, the user will not be allowed to trigger pipeline A. Using Jenkins API Tokens I am not sure. I can imagin they would still work though |
Yes, it will work - unfortunately - this is the reason why I manually remove such tokens (akward way to forbid using these tokens right now - I know 😞 ) and the reason asking for
|
|
@jtnord anything missing from your side? Or can this PR be merged and released? |
michael-doubez
left a comment
There was a problem hiding this comment.
The ability to use token was been requested many time.
The vanilla case must at use the token as an access token and validate it with IdP (but in this case, I expect no idtoken to be present).
Additionally, (yet) another configuration parameter could allow using the access token as a JWT and perform validation and claim search on it.
That way, we could still have a compliant oidv implmentation but allowing the popular case of access token as JWT (which is more like OAuth2).
| String authHeader = httpRequest.getHeader("Authorization"); | ||
| if (authHeader != null && authHeader.startsWith("Bearer ")) { | ||
| try { | ||
| JWT jwt = JWTParser.parse(authHeader.substring(7)); |
There was a problem hiding this comment.
Auth token are not necessarily JWT (even in OAuth).
Under openid connect, only the idtoken should be validated.
Morerover, your are not validating it with the IDP which would be expected if it is configured with authorization code flow in mind.
I understand this is frustrating because many IDP uses such a scheme and although it is not strictly oidc compliant, it would be practical.
There was a problem hiding this comment.
I think I don't fully understand this point:
I am validating the signature of the JWT token using JWKS. Afaik this is the only requirement for validating with the IdP. I think JWT tokens are (in contrast to opaque tokens) explicitly meant to be validated "offline" without IdP interaction. What has auth code flow to do with this? At this point we have a token and where it comes from is totally unrelated isn't it?
I know that Auth Tokens are not necessarily JWT, but isn't accepting e.g. opaque tokens yet another feature? The description of the checkbox for accepting bearer tokens explicitly states JWT as requirement. I would really like to get this finished and tbh I don't feel like introducing yet another feature, especially one that according to my experience is probably not needed by 90% of usages. In all my experience I pretty much exclusively saw JWT tokens being used by various tools and providers.
I would prefer using JWT as it is now and opaque tokens can be implemented in a different PR. Right now we have nothing, after this PR we have the most common use cases covered already and we can expand on that later on. Feels wrong to make this more complicated than necessary for what feels to me like almost no benefit.
There was a problem hiding this comment.
Opaque token may be validated by an introspection endpoint, that's what I mean by code flow.
Access token may have a huge life time (I have seen1y). If there is no way to revoke them, you are letting the door open in case of leak.
IMO the secure version should be the default and not the convenient one. Using the access token as a verified credentials is the feature which should be carefully considered.
See https://datatracker.ietf.org/doc/html/rfc7662
In OAuth 2.0 [RFC6749], the contents of tokens are opaque to clients.
This means that the client does not need to know anything about the
content or structure of the token itself, if there is any. However,
there is still a large amount of metadata that may be attached to a
token, such as its current validity, approved scopes, and information
about the context in which the token was issued. These pieces of
information are often vital to protected resources making
authorization decisions based on the tokens being presented. Since
OAuth 2.0 does not define a protocol for the resource server to learn
meta-information about a token that it has received from an
authorization server, several different approaches have been
developed to bridge this gap. These include using structured token
formats such as JWT [RFC7519] or proprietary inter-service
communication mechanisms (such as shared databases and protected
enterprise service buses) that convey token information.
There was a problem hiding this comment.
Note that verifying access token, would open the usage of implicit flow (which is usually not recommended).
There was a problem hiding this comment.
Note that verifying access token, would open the usage of implicit flow (which is usually not recommended).
Not only implicit flow but all flows. You could also use client credential flow, CIBA flow or whatever and I consider this a feature and not a bug as we then delegate deciding about the flow to the IdP which is the right place for such decisions. If you don't want to allow implicit flow, disable it in the IdP and not in the jenkins plugin. Especially because validating an already existing Bearer token is explicitly meant for API calls where I usually do not have a browser than can easily run the redirect-based auth code flow.
If you insist, I will look into opaque tokens by default. That might take some time though as I thought this PR is pretty much done and just needs minor adjustments. I have no capacity for working on major changes currently.
Note: Just to underline my point about opaque tokens being a rare species in nature: THE most popular open-source IdP Keycloak does not even support them, at least not the way they are intended. They have "lightweigth access tokens" which somewhat behaves like oapque tokens but is not really the same IMO.
There was a problem hiding this comment.
The any flow from anywhere is precisely my issue with the token validation. You are assuming the user used a client credential and that it is intended for the jenkins instance.
In the current code, there is no check for :
- can we assume there is a token_type claim ? If not, does the admin agree to take the responsibility to allow "client credential" token without proof ?
- the aud claim is difficult to validate (IMO): you assume it is the same as the client id, is it the case ? It is supposed to be the service, somehow ? What about the client_id claim itself ?
- your code doesn't check for the additional
failedCheckOfTokenField()but can we assume they are present in the access token ?
Moreover, there are a lot of assumptions which deviate from the code flow and the (draft) RFC doesn't really answer those question except to say the token should be carefully verified and encode the same data. See https://datatracker.ietf.org/doc/html/rfc9068
All of that to say, I would rather the access token be another source of claims (same as idtoken or userInfo) and be used uniformly rather than being a specific case of client credentials flow + adding the introspect endpoint to this flow.
This is going back on what I said in #218 but well ... if it is accepted for one flow, why not the other.
There was a problem hiding this comment.
I am not assuming client credential flow. Actually my use-case is precisely NOT using a client credential flow. If I wanted only a hardcoded, non-expiring secret stored somewhere I could also just use the jenkins API Tokens instead of bothering with login flows at all.
To be honest I still don't understand what your issue is, but what I am hearing is that you don't like the feature, at least in it's current form, for security reasons. Little disappointend that I am told like 8 months after creating the PR and spending time for it already...
I don't understand why because even after all your explanations I still cannot see how you could bypass the token validations as an attacker, but clearly you seem to be a lot deeper into OIDC and OAuth than I am, so thats probably lacking knowledge on my side.
I'd suggest you either close the PR if you don't agree with the feature at all, or you finish it up yourself the way you think it can work in a secure manner. I don't want to take responsibility for accidentally introducing vulnerabilities because of maybe not understanding OIDC and OAuth correctly.
I can take responsiblity however for the company I am working for and I have deployed and used a custom build of this plugin for months now, so I will just continue doing that, occasionally merging upstream fixes into my fork.
There was a problem hiding this comment.
Would we be able to move this forward under the assumption that we are only working with JWT-structured tokens? Some things that publish opaque tokens (eg Entra ID v1 tokens) don't even provide an introspection endpoint. Maybe the code should say:
- Configure an optional introspection endpoint.
- Try to parse and validate it as a JWT. If it does, then proceed on to scope validation (step 4).
- If the parse to JWT fails, try the introspection endpoint, if it exists.
- If no introspection endpoint exists, fail here.
- Validate scopes etc.
|
|
||
| // check if this is a valid api token based request | ||
| String authHeader = httpRequest.getHeader("Authorization"); | ||
| if (authHeader != null && authHeader.startsWith("Basic ")) { |
| if (isValidApiTokenRequest(httpRequest, user)) { | ||
| // check if this is a valid Bearer token based request | ||
| String authHeader = httpRequest.getHeader("Authorization"); | ||
| if (authHeader != null && authHeader.startsWith("Bearer ")) { |
There was a problem hiding this comment.
Case insensitive as well
|
Any change we can resurrect this PR? I initially opened #632 for MCP access (I mean OAuth 2.0 MCP access is in 2026 the standard to avoid the usage of API keys) I don't have a strong opinion if it should be part of oic-auth-plugin or via an extension plugin (didn't read the code, not sure if even possible) Maybe I lack OAuth knowledge as well but many tool out platform/CI/CD tools already support Bearer JWT API access
|
|
@jonesbusy regarding MCP access. |
|
For this PR yes, it's tight to the OicSecurityRealm I personally don't need this PR anymore since I solved my use case with https://plugins.jenkins.io/jwt-auth/ which do not have any dep on any security realm |
This PR adds the option to authenticate against the Jenkins API using JWT Bearer tokens instead of Basic Auth (Jenkins API tokens). This closes #630
By default Bearer token based access is disabled. This needs to be explicitly enabled in the plugins configuration in the "security configuration" section.
This change respects other settings like ignoring token validation, expiration or clock skew.
Testing done
The change is tested extensively with automatic JUnit tests. Also manual tests have been performed to validate that existing features did not break and that the new feature works as intended. It has been validated that it is now possible to use Authorization: Bearer HTTP headers to authenticate against the Jenkins API. Is has also been validated that invalid tokens, either because they have been tampered with, they have been expired or any other kind of invalidity do not grant access to protected resources. I tried to respect all existing settings, e.g. to bypass JWT signature validation or expiration.
Submitter checklist