jenkinsci / oic-auth-plugin

A Jenkins plugin which lets you login to Jenkins using your own, self-hosted or public openid connect server.
https://plugins.jenkins.io/oic-auth
MIT License
71 stars 94 forks source link

Implement Token Expiry Check and Refresh using Refresh Tokens #310

Closed krezovic closed 3 months ago

krezovic commented 6 months ago

This merge request adds support for checking expiration of issued access tokens and token refresh using refresh tokens

Strict Token Expiration will result in a 401 when access token has been expired and either no refresh token has been provided or refresh token itself has expired or has been revoked. This option is not enabled by default. I am not sure if the user should be automatically logged out in this case and I'm even more unsure on how to achieve this via code besides HTTP 302 to /logout or whatever the endpoint is.

Access Token refresh is automatically enabled when using well known configuration and the server supports "refresh_token" grant. Otherwise, it has to be manually enabled via checkbox toggle. In addition to refreshing the access token, the id token and user info is also refreshed and any new or updated groups are added for the currently logged in user.

Fixes: #100

Testing done

### Submitter checklist
- [x] Make sure you are opening from a **topic/feature/bugfix branch** (right side) and not your main branch!
- [x] Ensure that the pull request title represents the desired changelog entry
- [x] Please describe what you did
- [x] Link to relevant issues in GitHub or Jira
- [x] Link to relevant pull requests, esp. upstream and downstream changes
- [x] Ensure you have provided tests - that demonstrates feature works or fixes the issue
codecov[bot] commented 6 months ago

Codecov Report

Attention: Patch coverage is 62.58503% with 55 lines in your changes missing coverage. Please review.

Project coverage is 71.81%. Comparing base (bca3705) to head (f7dcfd0). Report is 11 commits behind head on master.

Files Patch % Lines
...va/org/jenkinsci/plugins/oic/OicSecurityRealm.java 63.02% 30 Missing and 14 partials :warning:
...java/org/jenkinsci/plugins/oic/OicCredentials.java 64.00% 8 Missing and 1 partial :warning:
...gins/oic/WellKnownOpenIDConfigurationResponse.java 33.33% 1 Missing and 1 partial :warning:
Additional details and impacted files ```diff @@ Coverage Diff @@ ## master #310 +/- ## ============================================ - Coverage 72.46% 71.81% -0.65% - Complexity 201 233 +32 ============================================ Files 9 11 +2 Lines 839 990 +151 Branches 119 143 +24 ============================================ + Hits 608 711 +103 - Misses 170 201 +31 - Partials 61 78 +17 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

krezovic commented 6 months ago

Hi @michael-doubez, this is a working draft where the most basic and common functionality works:

Now, before I move on to update documentation and write some tests, I'd like to get some feedback if the approach is correct. In addition, I have additional points that I have noticed but not sure if I should handle or how to handle at all.

Just for fun, without any activity I keep my jenkins tab opened and UI triggers a lot of requests every x seconds, resulting in at least 2 token refresh requests without any activity

image

Any additional feedback is welcome.

michael-doubez commented 6 months ago

Hello,

Thanks for the work. You can ignore the warnings about secret leaking, they are mostly irrelevant. I'll check the concurrency issue but if the information is local to a user context, there shouldn't be any issue.

There are a lot of features in the PR. I would break them down into:

I should be able to review in depth and (hopefuly) provide relevant feedback next Sunday.

krezovic commented 6 months ago

Hello,

I believe I have handled most of the use-cases except the logout. The question remains on how do we proceed.

Answering the points in the order they were raised

Back to the first point, we can implement additional features if needed, namely

michael-doubez commented 5 months ago

@krezovic the change looks good to me.

What bothers me most is the idtoken kept in the session information. It should be removed and kept only in the credentials but that can be ironed out later.

krezovic commented 4 months ago

Thanks for the feedback @michael-doubez. I have removed IdToken from Session and moved it strictly to SecurityContext.

Thank you @jglick for spotting missing Serializable and for bumping this MR as I completely forgot about it.

With that said and done, the MR is now ready for review and hopefully merging.

krezovic commented 4 months ago

Thanks to @jglick for brief explanation on what I've missed and the original purpose of issue #100.

The plugin will now serialize OIDC tokens into User object so they are valid when accessing the instance with API Keys.

Logging with an API Key will also trigger token refresh if AT has expired. If RT has also expired or has been revoked, the request made with API Key will correctly return 401.

Downside of this is that all API Keys will automatically become invalid when "Log out from OIDC Provider" option has been selected. For this scenario, I have nullified all token values in this scenario so no token refresh will be attempted but the expiration logic will correctly detect an expired token.

Edit: I am not sure how to write a test for this scenario. I am unable to find any documentation for public REST API that may return 401/403 for API Tokens with Jenkins Test Instance (I'd expect at least a "whoami" request to exist). Any help on this topic is welcome.

mikecirioli commented 4 months ago

I've been playing with this in a local cluster and the easiest way i've found is to:

krezovic commented 4 months ago

I've been playing with this in a local cluster and the easiest way i've found is to:

  • Create a job or folder that your test user should be able to access
  • set the AT expiration to something short (60s)
  • use the cli command list-jobs (using an access token) - verify that you can see the job
  • wait 60 seconds (or manually invalidate the access token
  • use the cli command list-jobs (using an access token) - verify that you now get a 403 type response You can also use the HTTP api to do the same list-jobs check

Hi. I was more asking in context of "integration test" jenkins instance (in PluginTest, annotated with @JenkinsRule). Which is why I said I'd prefer a REST API and that I do not have to configure or enable security manually on the test instance - not sure what the consequences will be.

Do note that I can easily call the HTML page users/testUser (which is the uid of the test user that's created), but this works both with authentication and without any authentication - so I'm kinda in conflict.

mikecirioli commented 4 months ago

I'll try to take a look later today or this weekend and see if i can help contribute a test. So far, i have not been able to successfully get the plugin to refresh my users authorities after i have removed them from the IDP, and access token remains working.

Strict token expiration does successfully revoke jenkins api token access when the primary token expires.

UPDATE:

I can now see that the granted authorities change when i remove the user from the org. I need to tweak my auth strategy in jenkins to fully test though because the token requests still show authenticated which i'm not sure is what i expect.

mikecirioli commented 4 months ago

@krezovic what else needs to be done in order to merge this PR? Is there something you need a hand with or are you just waiting for me feedback?

michael-doubez commented 4 months ago

@krezovic conflicts needs to be resolved.

IMHO we can merge it as it is.

michael-doubez commented 3 months ago

@krezovic I have been working on the merge, I should be able to contribute it before the end of the week

michael-doubez commented 3 months ago

See https://github.com/michael-doubez/oic-auth-plugin/tree/token_refresh

Note: some unit test still to adapt/fix

krezovic commented 3 months ago

See https://github.com/michael-doubez/oic-auth-plugin/tree/token_refresh

Note: some unit test still to adapt/fix

Hi. Thanks for doing the heavy lifting. I was already halfway through and was hoping to finish it over the weekend. Your changes to test really helped and now I have finished fixing the tests and took a bit different approach than you in passing the nullable refresh token.