# 關於 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)