-
Notifications
You must be signed in to change notification settings - Fork 95
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
Implement Token Expiry Check and Refresh using Refresh Tokens #310
Conversation
e737987
to
1a5e4f2
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ 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 ☔ View full report in Codecov by Sentry. |
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
Any additional feedback is welcome. |
Hello, Thanks for the work. You can ignore the warnings about secret leaking, they are mostly irrelevant. 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. |
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
|
@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. |
fdea1a2
to
063ec59
Compare
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. |
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. |
I've been playing with this in a local cluster and the easiest way i've found is to:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Hi. I was more asking in context of "integration test" jenkins instance (in PluginTest, annotated with 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. |
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 |
...main/resources/org/jenkinsci/plugins/oic/OicSecurityRealm/help-logoutFromOpenidProvider.html
Show resolved
Hide resolved
if (this.logoutFromOpenidProvider && !Strings.isNullOrEmpty(this.endSessionEndpoint)) { | ||
// This ensures that token will be expired at the right time with API Key calls, but no refresh can be | ||
// made. | ||
user.addProperty(new OicCredentials(null, null, null, credentials.getExpiresAtMillis())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invalidating the refresh token here seems to cause jenkins to fall back to lastGrantedAuthorities
for the user when doing token based access. This means (afaict in my testing) that tokens will still be effectively valid even if that access was removed at the idp level. The new authorities are only propagated to jenkins if the user (whose access was removed) attempts to log back in again.
If the refreshToken
is left persisted in the user properties then it will be used to refresh the API token (once the id token as expired) and at that point it will get the updated authorities for the user associated with the token. For example, if their access to the particular jenkins instance was updated at the IdP then this will correctly get reflected in their jenkins user's grantedAuthorities
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, it seems that if the refresh token is saved then the next time the user tried to log directly into the instance then that token will be used and the credentials refreshed and the user will not be prompted to log back in at the idp, which doesn't really seem ideal either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invalidating the refresh token here seems to cause jenkins to fall back to
lastGrantedAuthorities
for the user when doing token based access. This means (afaict in my testing) that tokens will still be effectively valid even if that access was removed at the idp level. The new authorities are only propagated to jenkins if the user (whose access was removed) attempts to log back in again.If the
refreshToken
is left persisted in the user properties then it will be used to refresh the API token (once the id token as expired) and at that point it will get the updated authorities for the user associated with the token. For example, if their access to the particular jenkins instance was updated at the IdP then this will correctly get reflected in their jenkins user'sgrantedAuthorities
This is only true if "strict expiration check" is disabled - which is default at the moment. Once it's changed to require refresh token by default, user will be greeted with 403.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, it seems that if the refresh token is saved then the next time the user tried to log directly into the instance then that token will be used and the credentials refreshed and the user will not be prompted to log back in at the idp, which doesn't really seem ideal either.
I'm not sure why do you see this a problem. This is exactly the purpose of refresh token. However, if the user logs out at IdP, the refresh token will be invalidated and the user will be greeted with 403/redirect to login.
75ee08a
to
b7cf91c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, thanks for the discussion
@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? |
@krezovic conflicts needs to be resolved. IMHO we can merge it as it is. |
@krezovic I have been working on the merge, I should be able to contribute it before the end of the week |
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
…quired In a CloudBees CI HA setup, I recently upgraded to 4.297.vcddb_d8a_e4694 (including jenkinsci#310). The various changes to OicSecurityRealm broke serialization due to usage of an anonymous inner class (extending OicSession) that gets stored in session. Storing `OicSession` in session is actually only required for a brief amount of time, only between doCommenceLogin and doFinishLogin. Once the user is logged in, it is no longer necessary to store the whole object. The only thing that needs to be persisted is the state, as it gets used later for logging out.
…quired In a CloudBees CI HA setup, I recently upgraded to 4.297.vcddb_d8a_e4694 (including jenkinsci#310). The various changes to OicSecurityRealm broke serialization due to usage of an anonymous inner class (extending OicSession) that gets stored in session. Storing `OicSession` in session is actually only required for a brief amount of time, only between doCommenceLogin and doFinishLogin. Once the user is logged in, it is no longer necessary to store the whole object. The only thing that needs to be persisted is the state, as it gets used later for logging out.
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