-
Notifications
You must be signed in to change notification settings - Fork 23
Added support for client_credentials grant type #25
Conversation
FYI @bderusha, this will aid in authentication for Course Discovery's refresh management command. |
@clintonb given the nature of this change, I think it would be appropriate to bump the version of this package. |
scope = data.get('scope') | ||
|
||
if constants.SINGLE_ACCESS_TOKEN: | ||
at = self.get_access_token(request, client.user, scope, client) |
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.
Nitpick: Looks like this is indented too far.
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.
Fixed.
👍 once version incremented. |
cb116b6
to
05673d9
Compare
Having spent a couple hours trying to increase test coverage, specifically focusing on the case when the endpoint should not issue a refresh token, I am giving up. Given that we do use refresh tokens, there is no point in focusing on ensuring that path is covered. The source of the test failure has to do with the |
@@ -597,6 +597,25 @@ def test_access_token_response_valid_token_type(self): | |||
token = self._login_authorize_get_token() | |||
self.assertEqual(token['token_type'], constants.TOKEN_TYPE, token) | |||
|
|||
def test_client_credentials_grant(self): |
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.
Nit:
- Separating the tests here into individual test methods.
- For comprehensiveness, add a test for invalid client_id with client_credentials grant_type.
- So, in the end, you would have:
test_client_credentials_grant_success
,test_client_credentials_grant_invalid_secret
,test_client_credentials_grant_invalid_client_id
.
f7db444
to
199b0ed
Compare
@nasthagiri it turns out refresh tokens were being created whenever access tokens were created, regardless of whether the refresh token was needed/requested. I have removed that functionality, and updated the tests appropriately. Please take another look. @rlucioni please take another look as well. |
except AccessToken.DoesNotExist: | ||
# None found... make a new one! | ||
at = self.create_access_token(request, user, scope, client) | ||
self.create_refresh_token(request, user, scope, at, client) |
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.
This change makes sense since not all grant types should create a refresh token.
However, what about the grant types that DO require a refresh token?
Specifically, authorization_code and the non-Public password grants also call get_access_token
- expecting to have created a refresh token.
In fact, looking at the code now, there's an unintentional bug with Public clients using the Password Grant with constants.SINGLE_ACCESS_TOKEN enabled - the code generated refresh tokens for them.
My suggestion to you is to use the recommendation in the Wrong Abstraction article. Have each of the 3 callers of the get_access_token
method handle the DoesNotExist
exception themselves. So, authorization_code
and password
for private Clients will create refresh tokens, while client_credentials
and password
for public Clients won't. (Alternatively, pass a boolean into get_access_token
- ignoring the article.)
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.
get_access_token
is only used if constants.SINGLE_ACCESS_TOKEN
. To my knowledge, we do not have that functionality enabled. All of the handlers have a conditional similar to that seen at https://github.com/edx/django-oauth2-provider/blob/edx/provider/views.py#L569-L573. I don't think we (edX) actually use get_access_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.
I see we are making 2 assumptions here:
- All operators of open edX will never enable
constants.SINGLE_ACCESS_TOKEN
. - We will be removing this library in favor of django-oauth-toolkit so this is a temporary fix, anyway.
Not knowing what future lies ahead of us, in the meantime, why have broken functionality in this common library? My concern is that (A) current (if any open edX instance has enabled constants.SINGLE_ACCESS_TOKEN) and (B) future uses of this library would not be doing the correct thing.
(By the way, the Mobile team was, in fact, considering to enable constants.SINGLE_ACCESS_TOKEN on edx.org since the access control table continues to grow in the multi-device and multi-session use cases.)
So either: (1) remove support for constants.SINGLE_ACCESS_TOKEN entirely - with notification to edx-code, (2) pass in a boolean to this method on whether or not a refresh_token is expected, or (3) refactor the abstraction as suggested above. I believe, 2 or 3 should be reasonable to implement. What do you say?
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.
I've implemented a combination of 2 and 3. Please have a look at get_access_and_refresh_tokens()
.
@clintonb I have completed my latest pass. This is looking good! A few remaining comments. |
Looks nice @clintonb, 👍. |
887f3f6
to
ccbeefa
Compare
ccbeefa
to
97abf4f
Compare
'scope': data.get('scope'), | ||
'client': client, | ||
'create_refresh_token': False, | ||
} |
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.
Doesn't this also need 'reuse_existing_access_token': constants.SINGLE_ACCESS_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.
Yes. Added.
👍 |
- Removed creation of refresh token alongside access token - Mocking constants.SINGLE_ACCESS_TOKEN instead of changing its value, thereby breaking other tests
97abf4f
to
5993f8d
Compare
5993f8d
to
a636a72
Compare
Added support for client_credentials grant type
I am adding this functionality so that our services can get access tokens for service accounts. This is preferable to hacking around the issue or creating a long-lived access token. Adapted from caffeinehit#47.
FYI @nasthagiri @jcdyer @douglashall @rlucioni @jimabramson
TODO