Django-rest-framework: TokenAuthentication -- Why can there be only one token per user?

Created on 20 Jan 2013  Â·  24Comments  Â·  Source: encode/django-rest-framework

in authtoken/models.py the User <-> Token association is a OneToOneField. Is there a specific reason for it not to be a simple ForeignKey?

With sessions, users can have multiple opened sessions. With APIs, people can have multiple devices / apps talking to a service and it'd be preferable for them not to share the same token.

It's very simple to write a custom TokenAuthentication that allows multiple tokens (I need something similar to GitHub's API, with a note / note_url associated to each token) but I'm wondering if it'd not be a saner default to allow multiple tokens per user. I'm happy to write a patch for this, otherwise I'll just roll my own TokenAuthentication.

Opinions?

Most helpful comment

I find the token=password comparison to be silly. Do you know any services that require you to change your password and invalidate every other session you have open every time you log out?

All 24 comments

imagine the token to be like a password. it's something that authenticates you against the system. do you have any service with more than one password associated with your account? i guess not ;)

@brutasse - Both options are reasonable, for different reasons. I think we started with FK, and then ended up with 1-1 in the end because there's a view that does get-or-create for a user's authentication token, and it's not obvious what the behavior should be if there are multiple tokens.

One option would be if you decide to package up your token auth implementation on PyPI, then we could link to it from the docs as an alternative, which would be great. I'd recommend this: https://github.com/dabapps/django-reusable-app for getting something up on PyPI quickly if you want to do that.

@tomchristie -- thanks, I'll probably end up doing that. I'll let you know if/when I have something.

@brutasse That'd be ace - thanks!

I found this limitation confusing given the docs:

This authentication scheme uses a simple token-based HTTP Authentication scheme. Token authentication is appropriate for client-server setups, such as native desktop and mobile clients.

I think especially when this feature is used for mobile clients that single users would have more than one token, one for each device they access from.

@brutasse There's something like this yet?

The third party JWT auth package is the best alternative token auth implemention. Worth looking at.

+1

I find the token=password comparison to be silly. Do you know any services that require you to change your password and invalidate every other session you have open every time you log out?

@jonathan-golorry :+1: Exactly! There should be no relation/comparison between token and password.

Comparation token eq password is total misunderstanding. Tokens by definition short-term authentications mechanism, and password is not. Having multiple tokens per user is not bad idea at all.

Guys is there any 3rd pary token authorization mechanism which will produce tokens per mobile client ? (mobile device) ?

@rwoloszyn Yes, you may want to look into Django OAuth Toolkit. I use it via Django REST framwork Social OAuth2 for both social and non-social use cases.

@JockeTF thanks. I will look into it, however as far as I check now I cand find anything about generating multiple token per device.

Ah, I was interpreting...

Guys is there any 3rd pary token authorization mechanism which will produce tokens per mobile client ? (mobile device) ?

... As there being several client _types_ for which users should have different tokens.

With the OAuth library users will get a new access and refresh token every time they submit their username and password. Tokens will be tied to a specific client ID (application). Each user can have several valid tokens for several different clients at the same point in time. Access tokens are usually short-lived, but new ones can be retrived using the refresh token.

@JockeTF ok. I get that. But I want to accomplish other thing.
I would like to allow user to login/register by facebook and for now only facebook and when login or registartion completes generate my own backend token which will be used betwenn client<-> backend
communication. I would like to also have different tokens for different mobile client.
Ex. User have 2 mobile phones I would like to generate token per client and also associate tokens with this specifif device, so when user next tile log in I would know which device he/she is using.
I do not want to user provide any username/password staff. I would like to authenticate with faceboook, retreive my unique backend token per device. Is there any ready to go solution which could do that ? If not I am going to make one :)

@rwoloszyn Django REST framwork Social OAuth2 will do most of what you want though the convert-token view. You may have to learn a little bit about how Python Social Auth works though, specifically about how to customize the pipeline.

I'm not sure if using different clients IDs for the different devices makes sense in your case, so you should look into that. If you don't want to use a different client ID per device, then you'll have to make something for keeping track of which device the tokens belong to. However, things pretty much work the way you want it to out-of-the-box if you do use a unique client ID per device.

@JockeTF that seems realy good. Thanks for good advices. I will share my results as soon as I get it working. Thanks again.

Related package:

Knox authentication is token based, similar to the
TokenAuthentication built in to DRF. However, it overcomes some
problems present in the default implementation:

  • DRF Tokens are generated with os.urandom, which is not
    cryptographically secure.

Knox uses OpenSSL to provide tokens.

  • DRF tokens are limited to one per user. This does not facilitate
    securely signing in from multiple devices, as the token is shared. It
    also requires _all_ devices to be logged out if a server-side logout
    is required (i.e. the token is deleted).

Knox provides one token per call to the login view - allowing each
client to have its own token which is deleted on the server side when
the client logs out.

Knox also provides an option for a logged in client to remove _all_
tokens that the server has - forcing all clients to re-authenticate.

  • DRF tokens are stored unencrypted in the database. This would allow
    an attacker unrestricted access to an account with a token if the
    database were compromised.

Knox tokens are only stored in an encrypted form. Even if the
database were somehow stolen, an attacker would not be able to log in
with the stolen credentials.

  • DRF tokens track their creation time, but have no inbuilt mechanism for tokens
    expiring. Knox tokens can have an expiry configured in the app settings (default is
    10 hours.)

@brutasse It's been more then three years, is there any solution to solve this problem, Please let me know , thanks !!!

@thisismsreddy nothing directly usable but you can have a look at https://github.com/feedhq/feedhq/blob/master/feedhq/reader/models.py -- multiple tokens per user, stored in the database with cache lookups to speed things up and spare SQL queries.

why not use FK here https://github.com/encode/django-rest-framework/blob/master/rest_framework/authtoken/models.py#L16? Then it allows more than one session per user? Of course it needs then more modifications to other things like examples as well. Thinking how the new token should be created, like if we use get_or_create it does not create new token. Instead it will use current behaviour.

Anyways it is not difficult to override TokenAuthentication get_model function https://github.com/encode/django-rest-framework/blob/master/rest_framework/authentication.py#L160 and use different model there.

5 years later, and this is still a problem. Most products/services require multiple sessions per user. Imagine logging into your account from the web app and your phone notifies you that you are logged out from the mobile app. Fix it by logging in again on the phone only to find out that you're logged out of the web application, _ad infinitum_.

logging into your account from the web app and your phone notifies you that you are logged out

This shouldn't be the case. You are supposed to use the same token for each device, not revoke the token for the old device and create a new token for the new device. The only time this is an issue is if you want to specifically end a single device's session. Let's say you're using a public computer and logout. If the logout revokes the token, all devices are logged out. If the logout doesn't revoke the token, then anyone who stole your token now has perpetual access to your account (if logouts don't revoke the token, what does?).

I could see your login/logout loop if you were somehow using token rotation, but by that point you're already using a 3rd party package for tokens.

Would oauth2 be a solution? No session state per-se, just Bearer tokens.

On Sun, Oct 21, 2018 at 5:42 PM Rahul Soibam notifications@github.com
wrote:

5 years later, and this is still a problem. Most products/services require
multiple sessions per user. Imagine logging into your account from the web
app and your phone notifies you that you are logged out from the mobile
app. Fix it by logging in again on the phone only to find out that you're
logged out of the web application ad infinitum.

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/encode/django-rest-framework/issues/601#issuecomment-431706649,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AEJ5dzQy2VU-qvvRUaW14cpuvOv5Bv2Tks5unOosgaJpZM4AX2yZ
.

Was this page helpful?
0 / 5 - 0 ratings