I'm not sure I'm right on track. Please give me a hint or direction.
I set up my Web service using Django and also made mobile app with React Native using Django REST framwork. Django uses the basic session authentication, but Django REST API uses token authentication to process the request from mobile app.
I want to implement small ReactJS app into my existing Django web. At this stage, I think my small react app will need auth token to communicate with REST api for itself.
So, my idea is that when user logs in web login page, user's API token needs to be received from API and save into cookie or localStorage while normal log in process is processing in Django Web service. Because I don't want to let users log in again to run react app on my web page to get auth token.
Am I right on track? if so, how can I make it works? Please refer to my code in Django login view.py Do i need to some code in order to get API auth token and save it into client side?
def Login(request):
if not request.user.is_authenticated:
if request.method == "POST":
email = request.POST['email']
password = request.POST['password']
user = authenticate(email = email, password = password)
if user is not None:
login(request, user)
messages.add_message(request, messages.SUCCESS, request.user.nickname + ' Welcome!')
return redirect('Search')
else:
messages.add_message(request, messages.WARNING, 'Please check Email / Password again')
return redirect('login')
else:
form = LoginForm()
return render(request, 'login.html', {'form': form })
else:
return redirect('main')
You have done some useless in your login function. you can use jwt. it has some good function for supporting login. In its login function, when send username and password with post, it return token to client.
http://getblimp.github.io/django-rest-framework-jwt/
You just need set urlpattern
from rest_framework_jwt.views import obtain_jwt_token
#...
urlpatterns = [
'',
# ...
url(r'^api-token-auth/', obtain_jwt_token),
]
It return token
$ curl -X POST -d "username=admin&password=password123" http://localhost:8000/api-token-auth/
In other request, if you need authentication, use following request
$ curl -H "Authorization: JWT <your_token>" http://localhost:8000/protected-url/
They both carrying out similar tasks with few differences.
Token
DRF's builtin Token Authentication
One Token for all sessions
No time stamp on the token
DRF JWT Token Authentication
One Token per session
Expiry timestamp on each token
Database access
DRF's builtin Token Authentication
Database access to fetch the user associated with the token
Verify user's status
Authenticate the user
DRF JWT Token Authentication
Decode token (get payload)
Verify token timestamp (expiry)
Database access to fetch user associated with the id in the payload
Verify user's status
Authenticate the user
Pros
DRF's builtin Token Authentication
Allows forced-logout by replacing the token in the database (ex: password change)
DRF JWT Token Authentication
Token with an expiration time
No database hit unless the token is valid
Cons
DRF's builtin Token Authentication
Database hit on all requests
Single token for all sessions
DRF JWT Token Authentication
Unable to recall the token without tracking it in the database
Once the token is issued, anyone with the token can make requests
Specs are open to interpretations, no consensus on how to do refresh
Reference: Django : DRF Token based Authentication VS JSON Web Token
I'd like to leave my answer after I solved in my way through my long research and study. My solution is quite simple.1. set DRF session authentication enable. Adding some code in setting.py
REST_FRAMEWORK = {
# ...
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
}
2. add 'credentials: "include"' into fetch code to use already logged in session cookie for authentication.
await fetch(API_URL, {
credentials: "include"
})
this solution solved my case.
Related
I have been given a task to authenticate admin login programmatically and logout as well.
I am able to to do login but on logged out when I check which user I am logging out it says AnonymousUser. How can I make sure I log out current user which is logged it.
I am using Django REST framework and testing it on Postman.
#api_view(["POST"])
def adminLogin(request):
if(request.method=="POST"):
username = request.data["username"]
password = request.data["password"]
authenticated_user = authenticate(request,username=username, password=password)
if authenticated_user != None:
if(authenticated_user.is_authenticated and authenticated_user.is_superuser):
login(request,authenticated_user)
return JsonResponse({"Message":"User is Authenticated. "})
else:
return JsonResponse({"message":"User is not authenticated. "})
else:
return JsonResponse({"Message":"Either User is not registered or password does not match"})
#api_view(["POST"])
def adminLogout(request):
print(request.user)
logout(request)
return JsonResponse({"message":"LoggedOut"})
Logging in/logging out with a REST API makes not much sense. The idea of logging in/logging out, at least how Django implements it, is by means of the session, so with a cookie that has the session id.
API clients like Postman usually do not work with cookies: each request is made more or less independent of the previous one. If you thus make the next request without a reference to the session, then the view will not link a user to that request. Clients like AJAX that runs on the browser of course can work with cookies, since these are embedded in the browser that manages cookies. You can work with cookies in postman as specified in this tutorial [learning postman], but this is usually not how an API is supposed to work.
This is why APIs usually work with a token, for example a JWT token. When authenticating, these are given a token that might be valid for a short amount of time, and subsequently it uses that token to make any other request that should be authorized.
As the Django REST framework documentation on TokenAuthentication [drf-doc] says, you can define views that create, and revoke tokens. The page also discusses session authentication that thus can be used for AJAX requests.
But likely you are thus using the wrong means to do proper authentication for your REST API, and you thus might want to work with a token like a JWT token instead.
I have the following
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
)
}
How does DRF get the request.user
def hello_world(request):
print(request.user)
return Response()
I am trying
curl --location --request GET "http://127.0.0.1:8000/hello-world/" \
--header "Authorization:Token ssssss28acbd550a9806c6ac9ce13f1bbc73137" \
--header 'Content-Type: application/json'
so in the output i see the request.user is printed as per the token supplied i.e eg: test
Then what is the use of using isAuthenticated
It only checks whether Authentication header is provided or not
Why cant that be checked by tokenauthentication itself
To my understanding, token authentication is not a custom permission. Authentication and permission are 2 different things.
If you check out the DRF site https://www.django-rest-framework.org/api-guide/authentication/, they say:
Authentication is the mechanism of associating an incoming request with a set of identifying credentials, such as the user the request came from, or the token that it was signed with. The permission and throttling policies can then use those credentials to determine if the request should be permitted.
So authentication is the underlying mechanism of how you authenticate users, e.g. token authentication, session authentication etc ... Permission regulates whether a request should be granted based on the identifying credentials which were retrieved from the authentication mechanism.
So in your example, the default authentication mechanism uses tokens. This applies to all your views (unless you override this explicitly in a particular view). You then send a http request to the endpoint handled by the hello_world view. In this http request you supply a token in the header. The authentication mechanism will attempt to match this token with a unique django user model instance in the database. This happens through DRF's Request class which extends the standard django HttpRequest, an instance of which is passed to the hello_world view. If you delve into the source code, you can see that user in request.user has a property decorator:
#property
def user(self):
"""
Returns the user associated with the current request, as authenticated
by the authentication classes provided to the request.
"""
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user
so that request.user triggers the authentication mechanism in the background if self._user hasn't been previously set and returns a django user model instance. I think this is roughly how DRF gets request.user.
To answer the second part of your question, again, token authentication only refers to the mechanism, it doesn't make any checks. So the use of isAuthenticated permission is to ensure only authenticated users, who have been authenticated by token authentication, are granted a successful response. Removing isAuthenticated would allow a user to receive a successful response without providing a token.
I am trying to send a JSON response from Django back-end to my angular front-end.
When I make the request I receive nothing in Postman or Angular but,opening the link in browser seems to be returning the correct result
My View is :
#api_view(['GET'])
def my_view(request):
print(request.user.username)
return JsonResponse({'username': request.user.username})
When I open http://127.0.0.1:8000/accounts/get_username/ in browser I receive
{"username": "aditya8010"} on the web page.
But when i do a get request using POSTMAN I recieve
{
"username": ""
}
Same with Angular
this.http.get("http://127.0.0.1:8000/accounts/get_username/").subscribe((res) => {
this.username = JSON.stringify(res["username"])
console.log(this.username," ", res)
})
this code also prints an empty username string.
Another thing I have noticed is that my print statement in the view does print anything random I put in there when called from POSTMAN or Browser but when I use request.user.username it doesnt print anything when called by POSTMAN.
And each time the response code is 200
What am I doing wrong.
When you're sending the request you are not providing authentication credentials (i.e. something that identifies the user that is sending the request). How do you obtain this credentials?
You need to establish an authentication method. There are several but I recommend using Token authentication with knox package. Basically, you have an endpoint that logins the user with his username and password (normal json post request) and that endpoint returns a token. This token is what identifies the user. You send this token in the header of each request you need to be authenticated. That means you probably should include an IsAuthenticated permission for the view. In postman:
API view:
from rest_framework.permissions import IsAuthenticated
#api_view(['GET'])
#authentication_classes([IsAuthenticated])
def my_view(request):
print(request.user.username)
return JsonResponse({'username': request.user.username})
When it is in a browser, your login information is remembered in the session. When using postman or Angular, you need to provide the user's information in the request header manually.
My app uses Django and rest framework backend, Vue frontend. I would like users to use session authentication and login via Django's allauth, with its email verification and password reset support. On login, users are redirected to a Vue component, and I can put the CSRF token into request headers.
I need to get user object (or user ID) into my Vuex store at this point. Are these my choices, or are there others?
give up using session authentication and use JWT
put some kind of session data or token into localstorage that Vue could then retrieve. (How could I do this?)
somehow get user ID from CSRF token
Any advice appreciated.
Duh. Sending request to view/endpoint for getting the authenticated user using Django's request.user worked like a charm, given that my Vue is also running on same localhost:8000 port as Django
in views.py:
class AuthUserView(APIView):
def get(self, request):
serializer = AuthUserSerializer(request.user)
return Response(serializer.data)
in Vue:
methods: {
getUser() {
fetch('/api/auth_user')
.then(response => response.json())
.then(data => console.log(data));
}
}
The API requests will be sent by anonymous users. No Login/register functionality is present.
I need to authenticate the API requests, one primitive way I tried was to send an auth key in each request. This auth key, I is saved in the Angular frontend as a constant.
There must be a better and more sophisticated way, kindly help!
Django REST framework largely assumes that requests are authenticated based on a user, but they do provide support for authentication anonymous requests. While this largely breaks from the assumption that "authentication" means "verifying a (Django) user is genuine", Django REST framework does allow it to happen and just substitutes the AnonymousUser instead.
Authentication in DRF can define both the request.user (the authenticated user) and request.auth (generally the token used, if applicable) properties on the request. So for your authentication, you would be holding on to tokens you have created (in a model or somewhere else) and those would be validated instead of the user credentials, and you would just end up not setting the user.
from django.contrib.auth.models import AnonymousUser
from rest_framework import authentication
from rest_framework import exceptions
class ExampleAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
auth = authentication.get_authorization_header(request)
if not auth or auth[0].lower() != b'token':
return None
if len(auth) == 1:
msg = _('Invalid token header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = _('Invalid token header. Credentials string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
try:
token = Token.objects.get(token=auth[1])
except Token.DoesNotExist:
raise exceptions.AuthenticationFailed('No such token')
return (AnonymousUser(), token)
This example assumes that you have a Token model which stores the tokens that will be authenticated. The token objects will be set to request.auth if the request was authenticated properly.
Read the rest api docs on authentication and their tutorial - they offer a solid intro to the options.