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

Add RefreshOIDCAccessToken middleware #377

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

diurnalist
Copy link

Note: this was originally proposed as #301. I took the nice work by @GermanoGuerrini and compressed it into a single commit, removed the HISTORY.rst change, and also changed one thing: now, the id_token is dropped from the session, because it cannot be verified securely. This is called out in the documentation. In my use-case, and perhaps many others, this is not a problem, as the access_token is the most important thing to preserve, as it's used to authenticate on behalf of the user to other services.


The OP can provide a refresh_token to the client on authentication. This
can later be used to get a new access_token. Typically refresh_tokens
have a longer TTL than access_tokens and represent the total allowed
session length. As a bonus, the refresh happens in the background and
does not require taking the user to a new location (which also makes it
more compatible with e.g., XHR).

This adds a new middleware to perform the refreshing of the access token
in the background.

@diurnalist
Copy link
Author

@akatsoulas , I think the other PR had been sitting for quite some time, if you get a chance to review this (and maybe @GermanoGuerrini as well?), maybe it can move forward this time.

Of course, feel free to close this and address #301 instead. I just wanted this functionality for my own deploy, saw it was already done (woo!) and then took the liberty of cleaning it up in hopes that it could make a merge soon.

👋 🚪 🚶‍♂️

@diurnalist diurnalist force-pushed the master branch 3 times, most recently from fbb7235 to c304785 Compare October 7, 2020 17:40
@diurnalist
Copy link
Author

I've also added error handling for when the refresh fails... prior, it would bomb the entire request, which to me is not expected.

@tevariou
Copy link

tevariou commented Feb 7, 2021

@akatsoulas Any update on that? mozilla-django-oidc is almost unusable with an SPA without it.

mozilla_django_oidc/middleware.py Outdated Show resolved Hide resolved
mozilla_django_oidc/middleware.py Show resolved Hide resolved
Copy link

@julwin julwin left a comment

Choose a reason for hiding this comment

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

lgtm!

@julwin
Copy link

julwin commented May 4, 2021

Can we have a look at this PR? This is a much needed feature..

@oar-spease
Copy link

What is needed for this PR to be looked at? It's kind of a crucial piece of any integration of OIDC into django unless you like constantly logging in over and over, and it's just been sitting here.

It would be nice to get this merged so I can stop maintaining my own branch- what exactly needs done here?

@diurnalist
Copy link
Author

diurnalist commented Oct 21, 2021

Hey all, saw there was some chatter on this and decided to freshen up the PR. I did a few things:

  1. Rebased against latest master branch.
  2. Updated to support basic auth per @tevariou's comment
  3. Now, the middleware will attempt to refresh your session online via the same mechanism as the SessionRefresh mixin, if there is no refresh token, or if the refresh operation failed with a 401. XHR clients will get the refresh_url included in the JSON response instead.
  4. Fixed up some broken tests.

Hopefully now that it is rebased and the tests (should) pass, it will be in better shape for merging.

@akatsoulas, I would be thankful for your time in review.

@diurnalist diurnalist force-pushed the master branch 2 times, most recently from 956fed2 to 075e232 Compare October 21, 2021 19:40
The OP can provide a refresh_token to the client on authentication. This
can later be used to get a new access_token. Typically refresh_tokens
have a longer TTL than access_tokens and represent the total allowed
session length. As a bonus, the refresh happens in the background and
does not require taking the user to a new location (which also makes it
more compatible with e.g., XHR).

If there is no refresh token stored, making refreshing impossible, OR
the refresh request fails with a 401, indicating the OP session has
terminated, the user is taken through a refresh flow similar to the
SessionRefresh middleware.

If any error occurs during refresh, the middleware aborts, but does not
perform any cleanup on the session.

Co-Authored-By: Jason Anderson <[email protected]>
@diurnalist
Copy link
Author

For those interested in (or even using) this patch, I pushed a fix for an issue we found, where it was not prompting for reauth if the refresh token request returned a 400 error. I had assumed incorrectly that a 401 would be returned, but that is not in the OAuth 2.0 spec.

@MrAlexBailey
Copy link

lgtm!

+1, what can we help with maintainers?

@jryantz
Copy link

jryantz commented Aug 30, 2023

lgtm!

+1, what can we help with maintainers?

Any progress? Would love to see this merged!

Copy link
Contributor

@GermanoGuerrini GermanoGuerrini left a comment

Choose a reason for hiding this comment

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

You did an excellent job. I'd like to see it merged too.

Copy link

@AndrejZbin AndrejZbin left a comment

Choose a reason for hiding this comment

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

Great job with this, it's very useful to us. We would like to see this merged.

I noticed one issue with the implementation when using this for Amazon Cognito.

# [1]: https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokenResponse
id_token = None
access_token = token_info.get('access_token')
refresh_token = token_info.get('refresh_token')

Choose a reason for hiding this comment

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

The refresh token is optional.
https://www.ietf.org/rfc/rfc6749.txt

So this doesn't work, for example, for amazon cognito, as they don't return new refresh token.
https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html

I think thee refresh_token should only be stored when it's contained in the response, otherwise keep the old one.

# Handle old configuration value
self.get_settings('OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS', 60 * 15)
)
self.request.session['oidc_token_expiration'] = time.time() + expiration_interval

Choose a reason for hiding this comment

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

Suggested change
self.request.session['oidc_token_expiration'] = time.time() + expiration_interval
self.request.session['oidc_id_token_expiration'] = time.time() + expiration_interval

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants