Project

Profile

Help

Story #2359

closed

As a user, I can use JWT tokens for authenticaton

Added by ttereshc over 7 years ago. Updated over 4 years ago.

Status:
CLOSED - CURRENTRELEASE
Priority:
Normal
Assignee:
Category:
-
Sprint/Milestone:
Start date:
Due date:
% Done:

100%

Estimated time:
Platform Release:
Groomed:
Yes
Sprint Candidate:
Yes
Tags:
Sprint:
Sprint 26
Quarter:

Description

Use djangorestframework-jwt to implement JWT support.

One should be able to:

  • acquire a JWT after some other initial authentication (e.g. basic auth)
  • use JWT for further authentication
  • invalidate JWT
  • by request
  • on user logout
  • on user delete
  • when password-like credentials used in the initial authentication were changed
  • configure expiration time for the JWT token (the default value - 1 week? 3 weeks?)

More explanations about use of JWT in Pulp could be found in pulp-dev list

One of the possible solutions for invalidating such tokens as JWT would be checking some timestamps on each request.
Even though Pulp does not expect tons of request per second it still looks a bit expensive to me to call db on every request, iiuc. Just a thought to keep in mind.


Related issues

Related to Pulp - Task #2090: Create a plan for user/auth in 3.0CLOSED - CURRENTRELEASEttereshc

Actions
Has duplicate Pulp - Story #2367: As a user, I can configure the expiration period for JWT tokensCLOSED - DUPLICATE

Actions
Blocked by Pulp - Story #2358: As a user, I can authenticate with username and password stored in PulpCLOSED - CURRENTRELEASEdkliban@redhat.com

Actions
Actions #1

Updated by ttereshc over 7 years ago

  • Blocked by Story #2358: As a user, I can authenticate with username and password stored in Pulp added
Actions #2

Updated by ttereshc over 7 years ago

  • Related to Task #2090: Create a plan for user/auth in 3.0 added
Actions #3

Updated by ttereshc over 7 years ago

  • Description updated (diff)
Actions #4

Updated by ttereshc over 7 years ago

  • Description updated (diff)
Actions #5

Updated by ttereshc over 7 years ago

  • Has duplicate Story #2367: As a user, I can configure the expiration period for JWT tokens added
Actions #6

Updated by bmbouter over 7 years ago

+1 to a default of 3 weeks.

Actions #7

Updated by mhrivnak over 7 years ago

  • Groomed changed from No to Yes
  • Sprint Candidate changed from No to Yes
Actions #8

Updated by fdobrovo almost 7 years ago

  • Status changed from NEW to ASSIGNED
Actions #9

Updated by mhrivnak almost 7 years ago

  • Assignee set to fdobrovo
Actions #10

Updated by mhrivnak almost 7 years ago

  • Sprint/Milestone set to 38
Actions #11

Updated by fdobrovo almost 7 years ago

After discussion with Brian it was suggested that it might be better and easier to adjust our requirements than to rewrite basicly whole rest_framework_jwt.

The MVP states about JWT requirements:

  1. As an API user, I can have documentation to generate a JSON Web Token (JWT) without the server being online. (Not dependent on JWT implementation)
  2. A user authenticated with HTTP/HTTPS "Basic" auth can acquire a non-expiring JWT to access the API.
  3. The JWT shall have a created timestamp which can be used to invalidate
  4. The JWT shall have a user identifier (its primary key)
  5. As an API user, I can authenticate any API call with a JWT.
  6. As an API user, I can invalidate all JWT tokens for a given user issued earlier than now.
  7. As an authenticated user, when deleting a user 'foo', all of user 'foo's JWTs are invalidated.
  • The rest_framework_jwt out of box support numbers 5, 7. and to some point number 4, but using pk is deprecated and they move on usernames instead.
  • To support number 2 own view have to be written. (Their "login" view handles auth itself via POST.)
  • Number 4 The payload of JWT token is public. Therefore anyone in case of using usernames as rest_framework_jwt does can read in some cases email address or kerberos id of user whose token it is. Not sure if it's issue or not.

Number 3 and 6:

  • The plugin does not use "iat" field. Instead it uses "exp" field and all tokens are for limited time. The expiration can be turned off by one boolean.
  • To support the ability to invalidate tokens of one user rest_framework_jwt provides JWT_GET_USER_SECRET_KEY Changing such secret_key on user module would lead to invalidation of all tokens issued before for such user. This have one essential benefit over storing timestamp of creation. for point 1. Because in order to be able to generate the JWT offline one have to know the secret key. If there would be one pulp-wide secret key everybody who knows it can make token for any user without any additional informations needed. So it's a security issue I think.

Using the way as it is now specified in MVP leads to almost completely rewriting the rest_framework_jwt. So I think we should discuss changing our requirements if it wouldn't be better to alter them a little bit.

Not finished Work in progress of implementaion by MVP here: https://github.com/BrnoPCmaniak/pulp/tree/JWT_token

Just one more idea I was thinking that the secret key in per user mode could me some sha of salted password like "pulp3_jwt_auth:<password>:pulp3_jwt_auth" so it could be generated anywhere without need to know anything more than just username + password. So it would be truly offline generation. Maybe even add few letters of some per pulp db install secret code to add another layer of security to password if the token would be compromited?

Actions #12

Updated by bmbouter almost 7 years ago

Thank you for such a detailed gap analysis. Here are some thoughts and questions:

+1 to adjusting use case 4 to use the username instead of the the pk. The username is unique so that should work perfectly.

When you say "payload of JWT token is public" what does that mean? I expect the client server connection is encrypted with SSL and that one user cannot request the JWT of another user with first providing basic auth creds.

For use case (2) are you saying that the user's credentials to get the JWT token via the normal way this library does it is to put the creds in the POST payload? Could you show an example of that payload to make it concrete for us? Maybe we should switch to that.

+1 to adjusting use case (3) to use the "exp" instead of the "iat"

+1 to using the JWT_GET_USER_SECRET_KEY to accomplish use case (6). For that use case I think we just use that feature and use case (6) is resolved, yes?

Actions #13

Updated by fdobrovo almost 7 years ago

bmbouter wrote:

When you say "payload of JWT token is public" what does that mean? I expect the client server connection is encrypted with SSL and that one user cannot request the JWT of another user with first providing basic auth creds.

What I meant was that you don't need to know the secret key to decipher the token's payload. So if you get hands on token from config in fs or something you can tell who it belongs. But as I'm thinking now it's not something one would worry about.

For use case (2) are you saying that the user's credentials to get the JWT token via the normal way this library does it is to put the creds in the POST payload? Could you show an example of that payload to make it concrete for us? Maybe we should switch to that.

The jwt serializer is here

It uses the USERNAME_FIELD on the user model as the name of the username field. So the payload would look like this:

The payload can be in these formats and any other supported by rest_framework for requests (more on it here):

$ curl -H "Content-Type: application/json" -X POST -d '{"username":"admin","password":"admin"}' https://localhost:8000/api/v3/token

or

$ curl -X POST -d "username=admin&password=admin" https://localhost:8000/api/v3/token

It has one rest_framework obstacle, to obtain the token the view would have to have set authentication classes to empty. Thanks to that we couldn't easily have under one endpoint more than just obtaining the token. (I think it might be done by dispatching anything else to proper view which would have the classes set, but I'm not sure when does rest_framework check for it.)

+1 to adjusting use case (3) to use the "exp" instead of the "iat"

+1 to using the JWT_GET_USER_SECRET_KEY to accomplish use case (6). For that use case I think we just use that feature and use case (6) is resolved, yes?

Yes it would solve the use case 6, but with that the use case 3 becomes redundant. We can set the expire time to 0. And thanks to that the token would have timestamp of creation on it and also thanks to presence of "exp" the tokens for one user wouldn't be all the same.

Actions #14

Updated by bmbouter almost 7 years ago

fdobrovo: I rewrote some of the use cases based on our discussion. I put my rewritten work here: http://pad-katello.rhcloud.com/p/pulp_JWT_use_cases

I think these changes will allow the JWT plugin to be used more easily. What do you think? I think once we agree on the adjustments to the use cases we can go to the mailing list or an MVP call to check in with others.

My one area of confusion is that one use case claims that it gets a non-expiring JWT and there is another use case that says we would have an 'exp' field. Is 'exp' optional? I put the word optional on there, but I don't know if it really does that or not. If it has to have an expiration then we probably can't also have non expiring tokens.

Actions #15

Updated by mhrivnak almost 7 years ago

  • Sprint/Milestone changed from 38 to 39
Actions #16

Updated by fdobrovo almost 7 years ago

The plugin adds the field "exp" no matter what. The time exp is actual time plus time from setting. Optionally we can check if the exp time is bigger than actual time or not. We could set the time difference to 0 so the "exp" would virtually by value become "iat". Other than that I think I agree.

But we should also discuss the way of obtaining the token. If it will be by base auth or any other rest framework activated auth or that user have to supply credentials in POST.

Actions #17

Updated by bmbouter almost 7 years ago

  • Sprint/Milestone changed from 39 to 38

fdobrovo wrote:

The plugin adds the field "exp" no matter what. The time exp is actual time plus time from setting. Optionally we can check if the exp time is bigger than actual time or not. We could set the time difference to 0 so the "exp" would virtually by value become "iat". Other than that I think I agree.

For ^ I think we should adjust the use case to have the JWT expire after a user specified amount of hours. That would be a systemwide setting. I've revised line 6 here: http://pad-katello.rhcloud.com/p/pulp_JWT_use_cases

But we should also discuss the way of obtaining the token. If it will be by base auth or any other rest framework activated auth or that user have to supply credentials in POST.

I think the alternate use case that aligns most heavily with that plugin is to have have an unauthenticated user supply the credentials in the POST body. Is that right? If so this could be highlighted and we can check in with others if replacing the JWT use cases with these alternates is a good idea.

Does the drf-jwt library easily support non expiring JWT? I think maybe it doesn't. If it doesn't then we should remove line 5 here: http://pad-katello.rhcloud.com/p/pulp_JWT_use_cases Related to that, I think lines 5 and 11 are redundant in the etherpad doc. What do you think?

Actions #18

Updated by fdobrovo almost 7 years ago

bmbouter wrote:

fdobrovo wrote:

The plugin adds the field "exp" no matter what. The time exp is actual time plus time from setting. Optionally we can check if the exp time is bigger than actual time or not. We could set the time difference to 0 so the "exp" would virtually by value become "iat". Other than that I think I agree.

For ^ I think we should adjust the use case to have the JWT expire after a user specified amount of hours. That would be a systemwide setting. I've revised line 6 here: http://pad-katello.rhcloud.com/p/pulp_JWT_use_cases

That's definitely an option that is easily achievable.

But we should also discuss the way of obtaining the token. If it will be by base auth or any other rest framework activated auth or that user have to supply credentials in POST.

I think the alternate use case that aligns most heavily with that plugin is to have have an unauthenticated user supply the credentials in the POST body. Is that right? If so this could be highlighted and we can check in with others if replacing the JWT use cases with these alternates is a good idea.

Yes it is. But it brings additional struggle. I expect that it may be difficult to achieve basic auth and POST auth under one endpoint, if we would like to support both.

Does the drf-jwt library easily support non expiring JWT? I think maybe it doesn't. If it doesn't then we should remove line 5 here: http://pad-katello.rhcloud.com/p/pulp_JWT_use_cases Related to that, I think lines 5 and 11 are redundant in the etherpad doc. What do you think?

The JWT_VERIFY_EXPIRATION option in Additional Options provides ability to turn off checking if the token is expired or not.

Actions #19

Updated by bmbouter almost 7 years ago

  • Sprint/Milestone changed from 38 to 39
Actions #20

Updated by bmbouter almost 7 years ago

fdobrovo wrote:

Yes it is. But it brings additional struggle. I expect that it may be difficult to achieve basic auth and POST auth under one endpoint, if we would like to support both.

I want to get more input from others, but I think for that one endpoint we could only support POST auth for the purposes of getting a JWT token. I think adjusting the use cases to match would be good. That would let us use the dependencies default behavior without modification.

The JWT_VERIFY_EXPIRATION option in Additional Options provides ability to turn off checking if the token is expired or not.

I updated the revised use cases once again in three ways:

  • allow the configuration to disable JWT expiration
  • allow the configuration to specify the expiration time
  • A few small clarifications that the URL to acquire a JWT only accepts POST auth.

I'm going to email pulp-dev to hopefully bring up these use cases on the list and switch to them on this Tuesday's MVP use cases call. Sound OK?

Actions #21

Updated by mhrivnak almost 7 years ago

  • Sprint/Milestone changed from 39 to 40
Actions #22

Updated by mhrivnak almost 7 years ago

  • Sprint/Milestone changed from 40 to 41
Actions #23

Updated by mhrivnak over 6 years ago

  • Sprint/Milestone changed from 41 to 42
Actions #24

Updated by fdobrovo over 6 years ago

  • Status changed from ASSIGNED to POST
Actions #25

Updated by mhrivnak over 6 years ago

  • Sprint/Milestone changed from 42 to 43
Actions #26

Updated by jortel@redhat.com over 6 years ago

  • Sprint/Milestone changed from 43 to 44
Actions #27

Updated by mhrivnak over 6 years ago

  • Sprint/Milestone changed from 44 to 45

Added by bmbouter over 6 years ago

Revision 7bb1b3c4 | View on GitHub

Add JWT dependency to docs builders

Pulp3 will depend on a DRF plugin called djangorestframework-jwt. When building the docs it needs to be able to import this dependency. This will update both the PR and regular docs builders.

re #2359 https://pulp.plan.io/issues/2359

Added by bmbouter over 6 years ago

Revision 64ce18a1 | View on GitHub

Add pulp dependencies to docs builders

When building the docs we need a full pulp3 environment for the docs to build correctly. This is due to the importing that occurs during docs building.

re #2359 https://pulp.plan.io/issues/2359

Actions #28

Updated by fdobrovo over 6 years ago

  • Status changed from POST to MODIFIED
  • % Done changed from 0 to 100
Actions #29

Updated by bmbouter about 6 years ago

  • Sprint set to Sprint 26
Actions #30

Updated by bmbouter about 6 years ago

  • Sprint/Milestone deleted (45)
Actions #31

Updated by daviddavis almost 5 years ago

  • Sprint/Milestone set to 3.0.0
Actions #32

Updated by bmbouter almost 5 years ago

  • Tags deleted (Pulp 3)
Actions #33

Updated by bmbouter over 4 years ago

  • Status changed from MODIFIED to CLOSED - CURRENTRELEASE

Also available in: Atom PDF