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

User ID method handling #169

Open
ethanjahn opened this issue Oct 25, 2019 · 7 comments
Open

User ID method handling #169

ethanjahn opened this issue Oct 25, 2019 · 7 comments

Comments

@ethanjahn
Copy link

Hi, thanks for the great work on this project!

I am using Auth0 and have been using the now deprecated django-rest-framework-jwt library but would like to switch to simplejwt. Part of the implementation I was using used a function to alter a username from the token and authenticate the user in Django's User model.

The relevant function is:

# auth0authorization/user.py

from django.contrib.auth import authenticate

def jwt_get_username_from_payload_handler(payload):
    username = payload.get('sub').replace('|', '.')
    authenticate(remote_user=username)
    return username

and the setting in django-rest-framework-jwt is:

JWT_AUTH = {
    'JWT_PAYLOAD_GET_USERNAME_HANDLER':
        'auth0authorization.user.jwt_get_username_from_payload_handler'
   ...
}

This is necessary because Auth0 uses usernames that look like "auth0|username" and they recommend storing as "auth0.username" in the User model (not fully sure why) and also because I need to authenticate the remote user before the token user_id claim means anything.

Do you know if there is a way to use a function upon receiving the JWT token in simplejwt? Alternatively, if there is a workaround happy to implement it!

@Andrew-Chen-Wang
Copy link
Member

I’m planning on creating a signal handler (with lots of warnings for when to use them, but this scenario seems fine) for post authentication. I can add a pre_authenticate handler as well. @ethanjahn

@Andrew-Chen-Wang
Copy link
Member

The other method is to add the username on token creation. So you'd include it in the refresh and access token payload.

@jrsmartyn
Copy link

jrsmartyn commented Jul 24, 2020

@Andrew-Chen-Wang To further detail this issue, I recently opened a new issue with the auth0 Django API tutorial. I've linked it back to this discussion.

@Andrew-Chen-Wang
Copy link
Member

Hi @asoundlife You'll have to give me until next week, possible Monday to re-visit the tutorial to see why this is necessary. For now, I'm gonna ask some questions so I may better understand this issue (I've been running out of time lately to maintain some of my repositories with college comin' up).

  1. Does auth0 use their own library ? AFAIK, they just use Django's built-in authentication middlewear and rest-framework-jwt.
  2. The callback to a new authentication method is for customizing login I suppose. Is it absolutely necessary to have Auth0 authenticate first? If anything, imo, you should be handling authentication first. A user on Auth0 does not mean your user necessarily exists in the first place, not to say the shabby record of Auth0 due to its flexibility for developers.
  3. What does this mean?

This is necessary because Auth0 uses usernames that look like "auth0|username" and they recommend storing as "auth0.username" in the User model (not fully sure why)

Are we putting the username in the payload? If so, that can be done in the serializer in the "validate" method. Is the username the same as your database's username or Auth0's? Doe the payload look like "auth0|Andrew-Chen-Wang" or "auth0|BASE64_BLAHS_IN_AUTH0"?

Storing "auth0.username" in the User model? I must've passed it in the tutorial. Mind sharing a blockquote?

I do see the RemoteAuthentication middlewear. Again, adding a callback would help for additional authentication measures, I suppose. If anyone does make a callback, only make a PRE-authentication callback. If it were POST/after, then people will think they can use it for last_login, which they should NOT.

Thanks for helping me understand the issue!

@jrsmartyn
Copy link

jrsmartyn commented Jul 25, 2020

Thanks @Andrew-Chen-Wang ,

To illustrate the answers to your questions, consider the scenario of having a Django backend serving an API to a frontend like Vue, using auth0 to serve as the authentication provider.

The auth0 team recommends setting up an authentication system in the following way:

1. Configure a new auth0 application & API with the auth0 dashboard (their website)

This gives the basic structure for managing your users and the rules behind authentication. For our example scenario above, the dashboard serves as scaffolding until a user management tool is created for the Vue frontend, which interacts with the auth0 API via JWT.

2. User authenticates via a "universal" (customizable) login screen

This is hosted by auth0, which is enabled by default and mandatory if you're not on a paid subscription. The universal login screen processes the form with the auth0 backend and then redirects the user to our example scenario site, along with an (RS256) JWT authentication token.

3. Auth0 provides an SDK for SPAs:

This SDK allows you to create a Vue component that manages the connection between the Vue frontend and provides all the necessary methods to connect and interact with the authentication pieces of the auth0 API. I hope this clarifies question 1 for this particular scenario.

One key note before moving on:

For all but the "Enterprise" version of auth0, Auth0 stores your user accounts and profiles. In the Enterprise version, you can configure your own database to manage your user profiles. However, they still recommend migrating your user database to auth0.

I hope this adds some clarity to your question 2. Auth0 actually recommends that you shouldn't manage your own authentication, generally speaking. This also means that in many use cases you won't store user account information on the backend Django database, though, auth0 does provide methods to do this.

Next

In keeping with our example, for Vue to consume the backend Django API it sends an axios request with an authentication header that embeds the JWT token managed by Vue/Auth0 SDK. The token includes basic user information and user permissions.

In the Django API tutorials posted by auth0, the auth0 team rely's on the unmaintained drf-jwt package to manage the communication between the Vue frontend and the Django API backend. This solution still works, but users such as myself are looking to migrate away from it, given the status of that package. SimpleJWT appears to be the best solution :).

See my linked issue for details regarding the auth0 implementation with drf-jwt and where I've taken things with SimpleJWT.

In auth0's recommended implementation, drf-jwt decodes the token and automagically authenticates the user to the backend server (using authenticate(remote_user=username)). The username is grabbed from the decoded JWT token and a new entry is automatically created in the user database table on the backend. This is probably best reviewed on the Django API tutorial under the "Validate Access Tokens" section.

In this regard, I'm not sure how to answer your question 3. Is the username part of the payload? Technically yes, but it could literally be anything. The user is being authenticated via authenticate(remote_user) and is not relying on any specific username from auth0, especially since authenticate(remote_user) will authenticate any username provided, and add it to the user database if it doesn't already exist. Incidentally, this provides an avenue to "clone" the database of users onto your Django backend database. Here's an example entry of a username created via this method in the Django database:

auth0.5f13fca1de3b59asdf924f993

I hope this provides more detail. Let me know if I can provide anything further! I've attached my backend.py and tokens.py files that I forked from SimpleJWT. (auth0simple.gz) With these edits, the token decode method recommended by auth0 works with simpleJWT and you can print(token) to view a valid JWT token. However, the user authentication step is still missing, and this is where many users appear to be stuck. Myself included.

@Andrew-Chen-Wang
Copy link
Member

I see. I'm quickly going through my GitHub mail atm, so you'll still have to give me a little bit of time before big reviewing. There isn't a PR for this yet, but #227 may be a solution. Take a look at it for now.

Thank you for all the info @asoundlife! Looking forward to getting y'all a solution hopefully within this week.

@jrsmartyn
Copy link

Awesome! After a quick glance of #227, it looks like some of the work done there could be applicable to implementing a solution here. If that's the case, it might make sense to merge these issues once things become a bit more clear. Thanks again @Andrew-Chen-Wang . I'll work through #227 and see if I can get the authentication side working for auth0.

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

No branches or pull requests

3 participants