I have implemented Token Authentication with django rest framework and I can post username and password to /api-token-auth/ and get the token.
url(r'^api-token-auth/', token_views.obtain_auth_token)
In addition to the token, I want to get the User object related to the returned token.
How can I override/add to this view and also return the actual User object?
You can find the relevant view here:
https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/authtoken/views.py#L21
Assuming you've created some sort of User serializer already, you can basically take the user instance there and shove it into your UserSerializer. then add it to the response, something like the below.
...
user_serializer = UserSerializer(user)
return Response({'token': token.key, 'user': user_serializer.data})
Related
How Can I Bind userType with jwt token??
because in the frontend needs to do some operations with type of user(hide some menus if userType is different)
in laravel.. Does it possible?
The way Laravel (and you most likely using https://github.com/tymondesigns/jwt-auth), is that the JWT should probably not carry user types or really other kind of user information than maybe a name or an id. After the token is generated, you are supposed to query another endpoint that will return the user information that you are looking for.
So essentially, what you want is 2 routes, let's say:
POST /auth/login
POST /auth/me
To the first route, you are supposed to provide the username and password, to which you'll get a token if credentials are correct. Then, you take the token you were just given, and call the second endpoint, which will return all user information you might want or need. You don't specify which kind of frontend you are using, but here's an example with Nuxt.js's Auth module: https://auth.nuxtjs.org/providers/laravel-jwt/
First of all, I hope the title of this question is clear enough. The following lines will make things clearer if it is not the case.
I have different users in my database. Some are part of the staff and some are just regular users.
In my API, I have a /users route. My idea is that when a staff member requests it (GET), he will see only the staff users and when a regular user will request it, he will only see the regular users.
Whether or not the user which makes the request is a member of the staff or not is an information which is stored in the JWT token.
Therefore, I used the following code:
class CustomUserList(generics.ListCreateAPIView):
serializer_class = CustomUserSerializer
def get_queryset(self):
token = self.request.META.get('HTTP_AUTHORIZATION')[7:] # [7:] to get rid of 'Bearer '
is_staff = jwt.decode(token, None, None)['is_staff']
print(is_staff)
queryset = CustomUser.objects.filter(is_staff=is_staff)
return queryset
This code works but is there a more direct / logical way to accomplish this ?
It feels a bit off storing what is essentially a server-side permission/setting on the client side (even though some people do use JWT for that). What if a user is promoted to/demoted from staff? You'd have to revoke all such client tokens? Also, what if you need to attach more conditions to these queries?
I think a slightly more flexible approach is to store some kind of a user id in the JWT token, then use a TokenAuthentication class with JWT to establish the identity of the user, attach it to something like request.user and then in your get_queryset method filter by request.user.is_staff. That way, you can attach any context/filters/permissions to the authenticated users on the server side and don't need to rely on the client to present explicit claim(s) to filter out the objects they can access. You'd end up with an extra call to the database to populate request.user, but you might end up needing that anyway.
Authenticating with a JWT token in the header retrieves the user so your view will have access to request.user and all of the user's attributes in every method after the dispatch() method (the first method that is run after .as_view() is triggered when sending a request to an endpoint).
If you're using djangorestframework-simplejwt, the DRF recommended JWT package, all you need to do is this:
class CustomUserList(generics.ListCreateAPIView):
queryset = CustomUser.objects.all()
serializer_class = CustomUserSerializer
def get_queryset(self):
queryset = super().get_queryset()
if self.request.user.is_staff:
queryset = queryset.filter(is_staff=True)
return queryset
I successfully implemented JWT as a authentication filter in my web application. When user's login is successful, I am creating a new JWT and assigning userName in the sub field of JWT.
In the subsequent request's I am using userName in the JWT sub field to identify the user. But what if the user changes his userName in the update section of the application. Is there way, I can update the value of sub field in JWT ?
What I am thinking!
I am thinking of getting the existing JWT in the RestController and after updating the userName, I will update the JWT with new userName and again send back to the client. Is this fine or is there a better approach?
I think I should refresh the token after update is done and send back the refreshed token back to client.
#RequestMapping( value = "/account", method = RequestMethod.POST )
public ResponseEntity<?> updateAccount( #RequestBody UserDetailsBean userDetailsBean, HttpServletRequest request,
HttpServletResponse response )
{
try
{
UserAccessDetails accessDetails = getLoggedInUser();
UserDetailsBean updatedUserBean = userService.updateAccount(userDetailsBean, accessDetails);
// send updated jwt incase of mobile number update by user
response.addHeader(SecurityConstants.HEADER_STRING,
SecurityConstants.TOKEN_PREFIX + refreshJWT(updatedUserBean.getMobileNumber()));
return buildResponse(updatedUserBean);
}
catch( DataException e )
{
return buildError(e);
}
}
private String refreshJWT( String subject )
{
return Jwts.builder().setSubject((subject))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SecurityConstants.SECRET).compact();
}
This is working. If anyone has a cleaner and industry standard approach please specify.
If you allow your users to change their usernames, they should also have an immutable user id that can be used to identify any data or activity associated with a given user. Otherwise, any time a user changes his or her name, you will either lose the ability to audit the user's past actions or you will have to update all references to that username in the database. What's worse is if there are references to an old username in the database and another user takes that username -- now you have data from one user now being associated with another due to incorrect handling of user identification.
Now with that said, the sub claim should contain this immutable user id. You can create a separate claim for the mutable username. When a username is changed, you now only need to change a single field in the database (assuming that only the users table references this mutable username). You could then use the refresh token retrieve a new token that would contain the latest username that could then be used by your API as needed.
Using this approach, you should be careful to only use the username claim for display purposes, not for identifying the logged in user due to the fact that it is mutable. The sub claim containing the user id would serve the purpose of identifying a user.
It is also important to note that this solution requires no special logic for "updating the sub claim." You would be using the same logic that you're already using to generate a token for a supplied refresh token.
Having worked my way through this tutorial:
http://bitoftech.net/2015/02/16/implement-oauth-json-web-tokens-authentication-in-asp-net-web-api-and-identity-2/
I now have the solution standing upright and I can issue JWT tokens (what I think of as 'login') and authenticate requests by passing in those tokens during subsequent calls.
What I'm not clear on is how the [Authorize] attribute is:
Recognising a user as authenticated
Retrieving a user from the database
Making that user available to my code
How I would add to the authentication process if I wanted to (perhaps including extra authentication logic after the exiting logic)
[EDIT] I understand that JWT tokens are being used to identify the user but I don't understand 'how' this is taking place. I also understand the middleware is doing it, but the workings of this are not clear.
with the [Authorize] attribute an AuthorizationFilter will added to the filter chain before the controller is called. This article illustrates that.
With the call to ConfigureOAuthTokenConsumption (Step 6 in the tutorial) you give the middleware the information it needs to validate and process tokens.
the authentication, i.e. check username and password, happens only before the token is issued in
public override async Task
GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) {
...
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
...
}
the AuthorizationFilter will only see the token and rely on the information in the token itself
this blog post gives you an example how you can access the username:
var currentPrincipal = ClaimsPrincipal.Current;
string userName = "Anonymous";
if (currentPrincipal.Identity.IsAuthenticated)
{
userName = currentPrincipal.Identity.Name;
}
the middleware gets the information from the token
you can add you own logic either before the token is issued in GrantResourceOwnerCredentials or add your own AuthorizationFilter if you need additonal logic when you receive the token. The blog post linked under 3. shows an example for that.
When my authentication handler passes successfully (userId is taken from decrypted token) I want to add the userId somewhere to the current request object.
Later in the pipeline when the user is authenticated the authorization is executed and here I need the userId again because I have to disallow authenticated users manipulating data from other users having a certain userId.
What would be the best way to save temporarely the userId? Should I misuse the request.headers to add there the userId?
cmon Darrel...please don't re-invent the wheel ;)
The authentication handler outcome should be a ClaimsPrincipal (set it on HttpAuthenticationContext.Principal).
This way you make the user id available to an authorization filter - as well as ApiController.User.
HttpRequestMesssage.Properties. It is a dictionary designed for storing request scoped data.