# 關於 DRF 的 Token Authentication ###### tags: `Django` `REST` `Token` `Authentication` DRF 在 Token, User 之間是定調在兩個 Package之下: ``` from rest_framework.authtoken.models import Token from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, UserManager, User ``` ``Token`` 與 ``User`` 之間的 Relation 是 ``OneToOneField`` 產生 Token 的程式呼叫: ``` token, _ = Token.objects.get_or_create(user=user) ``` 可以看出基本上,即使是自定義 User Model,但仍會需要在 ``settings.py``中宣告 ``` AUTH_USER_MODEL = "<app-name>.User" ``` 我猜測,User一定要被指定到 ``auth_user``,如此 DRF 的 Token 機制才能作用 使用這個指令,可以刪除token ``` # simply delete the token to force a login request.user.auth_token.delete() ``` ----- ## [Multiple Token Authentication in Django Rest Framework](https://stackoverflow.com/questions/35840734/multiple-token-authentication-in-django-rest-framework) ### Question: How can a user login in multiple devices because what we have is just a single Token Authentication on our django app. As an authenticated user when I login on Google Chrome it works fine but when I visit at the mozilla time and I _logged out_ at the chrome the token that has been created has been deleted upon logout so when I login at mozilla, the token is already gone and we can not log-in on mozilla and throws a Forbidden response on the console. > I'm confused.. are you talking about authentication tokens or session cookies? DRF doesn't have "multiple" tokens for authentication, it has only one token per user, and cerrtanly auth tokens are not destroyed on logout – pleasedontbelong Mar 7 '16 at 10:48 > Tokens,, can you provide an alternative for this one? – Dean Christian Armada Mar 8 '16 at 15:34 ### Answer: You're question is a little convoluted, but I think you are getting at the problem referenced here: https://github.com/tomchristie/django-rest-framework/issues/601 The official token authentication does not support (and unfortunately has no intention of supporting) multiple tokens, but you may be able to use [django-rest-knox](https://github.com/James1345/django-rest-knox), available here: > _Edit_: I previously recommended ``django-rest-multitoken``, but ``django-rest-knox`` seems to be more actively maintained. ----- ## [TokenAuthentication -- Why can there be only one token per user? #601](https://github.com/tomchristie/django-rest-framework/issues/601) 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``. sane (a) someone who is sane is able to think and speak in a reasonable way and to behave normally ----- @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. 只有一個view 會執行 Token.objects.get_or_create(user=user), 通常是 Login/ signin之類的 View 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. ----- 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. Token 與某個 clientID 相綁定 ----- @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 between 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](https://github.com/PhilipGarnero/django-rest-framework-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](https://github.com/omab/python-social-auth) works though, specifically about how to customize the pipeline. >Python Social Auth is deprecated 2016-12-03 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. ----- ----- ## [django rest framework - understanding authentication and logging in](https://django-oauth-toolkit.readthedocs.io/en/latest/rest-framework/getting_started.html) ### Question I am a beginner to django rest framework (and to REST in general) and I have a server side which (for now) has a ``UserViewSet`` which allows to register new users and I can ``POST`` to the url from my android app just fine (I get 201 CREATED). I read a lot about it, but I don't seem to fully the understand the concept of Login and Authentication in REST frameworks and specifically in django rest framework, and how it works. Do you "Log in" (like in facebook for example) and then you can make requests? What I understand\heard off: you can Login to a API\website using your username and password (assuming off course that you have registered as a user and you are in the user database). After you are Logged in - you will be able to make requests to views that allow access only to logged in\authenticated users. Is that somewhat correct? I mean, is there a "Log in" url where you login and that's it? you are authenticated? Also read somewhere that there isn't actually a login url, and you have to add your username and password to each request and then the request has to check if your details are in the User database? To sum up, I am not really sure how does authentication/logging in (same thing?) happens in django REST framework... and would really appreciate a good explanation or an example.. Thanks a lot! ### Answer: In a normal web application (removing the API from the question), a user would "log" in with their credentials (username/password, social tokens, etc.) and would receive a session cookie (assigned by Django) that allows them to authenticate in future requests on behalf of a user (realistically, themselves). This session cookie stays on their system for a limited period of time (two weeks by default) and allows them to freely use the website without authenticating again. If the session cookie needs to be removed, such that the person can no longer authenticate, the web application typically destroys the session cookie (or clears the session) which effectives "logs them out". >輸入帳密,換取 session cookie -->代表 user,之後不用再輸入帳密 若要刪除 session cookie,就要登出 (一般行為) In the case of an API, it all depends on how the authentication works. - ``SessionAuthentication`` works just like as described above, as it uses Django's internal session system. >SessionAuthentication 和 web application 一樣機制 - ``TokenAuthentication`` remembers the authentication information through a _database-backed token_ (which is transmitted in the Authorization header) instead of a session cookie. >``TokenAuthentication`` - toke 記在 database 上 - ``BasicAuthentication`` authenticates on every session (no persistent session) by passing the username and password on every request (base64 encoded through the Authorization header). ``BasicAuthentication`` 要每次都輸入帳密 - Other authentication methods generally work in the same way as ``TokenAuthentication``. So, here are some answers to specific questions which were raised: > Do you "Log in" (like in facebook for example) and then you can make requests? Using BasicAuthentication, you "log in" on every request by providing your credentials. With token-based authentication (TokenAuthentication, OAuth 2, JWT, etc.), you "log in" to receive the initial token and then your authorization is confirmed on every request. > Also read somewhere that there isn't actually a login url, and you have to add your username and password to each request and then the request has to check if your details are in the User database? ----- ## [django rest framework - token authentication logout](https://stackoverflow.com/questions/30739352/django-rest-framework-token-authentication-logout) ### Question I have implemented the Token Authentication according to the django rest framework Docs. Form what I read, the Token Authentication of DRF is quite simple - one token per user, the token doesn't expire and is valid for use always (am I right?). I understand that there are better practices out there, but for now the DRF token authentication is fine for me. my question is- what is the best practice for logout with the normal DRF token authentication? I mean, when the user logs out, should I delete the token from the client side? and then on login get the token again? should I delete the token and generate a new one? Anyone with experience with this? ### Answer: #### 1 Here's a simple view that I'm using to log out: ``` from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView class Logout(APIView): def post(self, request, format=None): # simply delete the token to force a login request.user.auth_token.delete() return Response(status=status.HTTP_200_OK) ``` Then add it to your ``urls.py``: ``` urlpatterns = [ ... url(r'^logout/', Logout.as_view()), ] ``` Nice !!! thank you! – Rodriguez David Oct 2 '17 at 16:56 Sorry but what queryset is used for? – dangsonbk Jan 3 '18 at 9:56 it does nothing, in this case. – ScottishUser Nov 13 '18 at 21:15 Nit: the logout should be a ``post`` instead of a ``get``: [stackoverflow.com/questions/3521290/logout-get-or-post](https://stackoverflow.com/questions/3521290/logout-get-or-post) – erikreed Nov 17 '18 at 15:31 #### 2 WHOLE IDEA OF TOKEN AUTHENTICATION: Normally in authentication services, there is a lifetime associated with a token. After a specific time, the token will get expired. Here, we get an access token which has an expiry time sent along with it by the server. Now the client needs to send this token everytime in the request header so that the server can identify who the user is. Either we can keep track of when it expires or we can just keep using it until we get an INVALID_TOKEN error. In that case we would have to again get the token from the server. > 一般來講,token 會有有效時間,經一段時間後就過期! token 會放在 Header 中來去作認證 token 存活時間與 Login時的認證機制 如OAuth2獨立的 token 代表一個 user,是 unique token 在web 一般可以保存在 cookie 中,但 server 不需刪除它 一旦 token 過期,client 要重新登入去要求一組新的 token The lifetime of the access_token is independent of the login session of a user who grants access to a client. _OAuth2_, lets say, has no concept of a user login or logout, or a session. The token is just used to identify the user if he is who he says he is. The token is unique for a user and client. You may save it to cookies to enable something like remember me but on the server you don't need to delete it. Whenever the token expires, the client need to send a request to the server to obtain the token again. Token Expiry in DRF Token Authetication: Currently, DRF Token authentication does not support this functionality. You would have to implement it yourself or use a third party package which provides this functionality. It should check for token expiry and raise an exception if the token has expired. > DRF 並不支援 Token 過期機制, 要達成這件事,要嘛自己搞 --> 自己 subclass DRF Token Authentication來處理 不然就是用第3方套件 它們會自己檢查 token 是否過期,若是有丟 exception To implement it yourself, you can subclass from the _DRF Token Authentication class_ and add your logic. You can even use a third-party package django-rest-framework-expiring-tokens. Some References: 1. [Token Authentication for RESTful API: should the token be periodically changed?] (https://stackoverflow.com/questions/14567586/token-authentication-for-restful-api-should-the-token-be-periodically-changed) 2. [How to Logout of an Application Where I Used OAuth2 To Login With Google?] (https://stackoverflow.com/questions/12909332/how-to-logout-of-an-application-where-i-used-oauth2-to-login-with-google)