I am building a RESTAPI in Django using the following library called drf-firebase-token-auth. The client sends a get request with the token_id available in the Authorization header, and the library is expected to handle authentication in Firebase automatically.
But the problem is if I do a simple get request without headers like curl <url+endpoint> the library doesn't verify if there's a an Authorization header and try to get a token for verification as if the request happens successfully as if drf-firebase-token-auth didn't raised it to be a problem. So I handled an exception whereas if the get request doesn't have an an authorization header, then raise an exception. But is it secure?
from datetime import datetime
from rest_framework import status
from rest_framework.decorators import (
api_view,
)
from rest_framework.response import Response
# Create your views here.
#api_view(["GET"])
def check_token(request):
# if token is valid response with current server time
try:
if request.META["HTTP_AUTHORIZATION"]:
date = datetime.now()
return Response(
data="Server time is: " + str(date), status=status.HTTP_200_OK
)
except:
return Response(data="Authorization header unavailable")
Related
I've got a tiny function that just looks to get a response from my DRF API Endpoint.
My DRF settings look like this:
"DEFAULT_AUTHENTICATION_CLASSES": [
# Enabling this it will require Django Session (Including CSRF)
"rest_framework.authentication.SessionAuthentication"
],
"DEFAULT_PERMISSION_CLASSES": [
# Globally only allow IsAuthenticated users access to API Endpoints
"rest_framework.permissions.IsAuthenticated"
],
I'm using this to try and hit the endpoint:
def get_car_details(car_id):
headers = {"X-Requested-With": "XMLHttpRequest"}
api_app = "http://localhost:8000/"
api_model = "cars/"
response = requests.get(api_app + api_model + str(car_id), headers=headers)
json_response = response.json()
return json_response
I keep getting 'detail': 'Authentication credentials were not provided'
Do I need to generate a CSRF token and include it in a GET request? The only time this gets hit is when a user goes to a view that requires they are logged in. Is there a way to pass that logged-in user to the endpoint??
When you make your request from the get_car_details function you need to be sure that the request is authenticated. This does not look like a CSRF issue.
When using session based authentication a session cookie is passed back after logging in. So before you call get_car_details you would need to first make a request to login, keep that session, and use that session when calling the API.
Requests has a session class requests.Session() that you can use for this purpose. Details here: https://docs.python-requests.org/en/latest/user/advanced/
Many people find token based authentication easier partly a session cookie does not need to be maintained.
So I have a function based view that I am using along with Django rest framework which looks like this :
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import permission_classes
#api_view(['GET'])
#permission_classes((IsAuthenticated, ))
def example_view(request):
.....
<business logic>
.....
This works as expected and gives a HTTP 401 if a user with insufficient privileges tries to access a URL bound to this view .However,due to how the front end angular is set up,what I need is to display a HTTP_403(forbidden).I went through the DRF documentation and could not see any already defined permission class that I can use as a decorator..What would be the best way of implementing this ?
So I found a solution to this.The IsAuthenticated class has very specific rules about emitting a 401 versus a 403 and they are as follows:
The request was successfully authenticated, but permission was denied. — An HTTP 403 Forbidden response will be returned.
The request was not successfully authenticated, and the highest priority authentication class does not use WWW-Authenticate headers. — An HTTP 403 Forbidden response will be returned.
The request was not successfully authenticated, and the highest priority authentication class does use WWW-Authenticate headers. — An HTTP 401 Unauthorized response, with an appropriate WWW-Authenticate header will be returned.
So inorder to force DRF to emit a 403 always just use an authentication class which does not use WWW-Authenticate headers.This is what i finally ended up with :
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import permission_classes
from rest_framework.decorators import authentication_classes
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
#api_view(['GET'])
#authentication_classes((SessionAuthentication, BasicAuthentication))
#permission_classes((IsAuthenticated, ))
def example_view(request):
.....
<business logic>
.....
I have page with login_required decorator which I want to test whether correct template is used. On stackoverflow I've found authorization method for unit test but for me it do not work for some reason. Here is my test:
from django.test import TestCase
from django.test import Client
import base64
class TestUsingCorrectTemplates(TestCase):
def test_correct_classroom_template_used(self):
auth_headers = {'HTTP_AUTHORIZATION': 'Basic '+base64.b64encode('admin#dot.com:admin')}
c = Client()
response = c.get('/classroom/', **auth_headers)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response,'classroom.html')
Also would like to mention that authorization handled with OpenId/AllAuth and there is no /login page, user logs in from start page /
Content of response variable is following:
Vary: Cookie
X-Frame-Options: SAMEORIGIN
Content-Type: text/html; charset=utf-8
Location: http://testserver/?next=/classroom/
Test error:
self.assertEqual(response.status_code, 200)
AssertionError: 302 != 200
What am I doing wrong?
The HTTP code 302 means that your server is sending a redirect response. You should tell your client to follow redirects so that you deal with the actual login page. You can change your get call like this:
response = c.get('/classroom/', follow=True, **auth_headers)
If you want to check the intermediate redirection steps you can inspect response.redirect_chain. It is all documented here.
Did you try creating a user and calling the login method of your Client instance?
import base64
from django.test import TestCase
class TestUsingCorrectTemplates(TestCase):
def setUp(self):
# Create your user here
# self.user = ...
def test_correct_classroom_template_used(self):
self.client.login('admin#dot.com', 'admin')
response = self.client.get('/classroom/') # XXX: You should use url reversal here.
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'classroom.html')
Authentication is failing
I'm trying without success to get my users signed into LinkedIn via Oauth authentication in Python. I'm using Django in python and not using any third party social authentication. I'm using the Guide to gain access to the API using Python and Django. However I am having trouble getting the Access Token. I can get the user logged in and get the Authentication Code. I have placed a new request as the earlier question was far too convoluted. You can see that here: Performing POST on a URL string in Django
Nothing was resolved and still unsure if this is an issue with LinkedIn or the code. LinkedIn have not been particularly helpful here, sadly.
but after getting the Author code, I simply cannot get the access token. I'm getting a 400 error for everything and despite getting the author code, on posting as the documentation suggests, I get the following:
u'oauth_problem=parameter_absent&oauth_parameters_absent=oauth_consumer_key%26oauth_signature%26oauth_signature_method%26oauth_token%26oauth_timestamp%26oauth_verifier'
I'm enclosing my Python code here in its entirety in the hope that someone can spot what is going wrong.
import oauth2 as oauth
import httplib2
import time, os, simplejson
import urllib
import urllib2
import pycurl
from django.http import HttpResponse
from django.http import HttpResponseRedirect
from django.core.urlresolvers import resolve
#from django.core.shortcuts import render, redirect
from django import forms
from django.utils import timezone
import urlparse
import requests
consumer_key = 'Yours'
consumer_secret = 'Yours'
user_token = 'Yours'
user_secret = 'Yours'
consumer = oauth.Consumer(consumer_key, consumer_secret)
access_token = oauth.Token(key=user_token,secret=user_secret)
client = oauth.Client(consumer, access_token)
request_token_url = 'https://api.linkedin.com/uas/oauth/requestToken'
access_token_url = 'https://api.linkedin.com/uas/oauth/accessToken'
authorize_url = 'https://www.linkedin.com/uas/oauth/authenticate'
def login(request):
redirect_uri = urllib2.quote('http://127.0.0.1:9000/loginsuccess')
codeURL = "https://www.linkedin.com/uas/oauth2/authorization?response_type=code&client_id=c3skrqz5wqmm&scope=r_fullprofile&state=DCEEFWF45453sdffef425&redirect_uri=" + redirect_uri
# Fill the keys and secrets you retrieved after registering your app
# Use your API key and secret to instantiate consumer object
#resp,content = client.request("http://api.linkedin.com/v1/people/~?format=json", "GET", "")
#resp, content = client.request(request_token_url, "POST")
#request_token = dict(urlparse.parse_qsl(content))
#return HttpResponse(access_token)
return HttpResponseRedirect(codeURL)
def loginsuccess(request):
authcode = request.GET.get('code')
redirect_uri = 'http://www.jelt.com'
#redirect_succ = 'http://www.127.0.0.1:8080/manage'
postdata = {
'grant_type': 'authorization_code',
'code': authcode,
'redirect_uri': redirect_uri,
'client_id': consumer_key,
'client_secret': consumer_secret,
}
r = requests.post(access_token_url, data=postdata)
#return HttpResponse(r.text)
#return HttpResponse(r.status_code)
return HttpResponseRedirect(redirect_uri)
def Manage(request):
return HttpResponseRedirect('http://www.xyz.com')
def success(request):
redirect_uri = urllib2.quote('http://www.xyz.com')
redirect_uri = "http://www.xyz.com"
return HttpResponseRedirect(redirect_uri)
Your login code is redirecting to the OAuth 2.0 endpoint https://www.linkedin.com/uas/oauth2/authorization but your callback loginsuccess is trying to fetch the OAuth 1.0a token from https://api.linkedin.com/uas/oauth/accessToken. You need to update your access_token_url to the OAuth 2.0 endpoint https://www.linkedin.com/uas/oauth2/accessToken per the LinkedIn docs.
Maybe a stupid question here:
Is Requests(A python HTTP lib) support Django 1.4 ?
I use Requests follow the Official Quick Start like below:
requests.get('http://127.0.0.1:8000/getAllTracks', auth=('myUser', 'myPass'))
but i never get authentication right.(Of course i've checked the url, username, password again and again.)
The above url 'http://127.0.0.1:8000/getAllTracks' matches an url pattern of the url.py of a Django project, and that url pattern's callback is the 'getAllTracks' view of a Django app.
If i comment out the authentication code of the 'getAllTracks' view, then the above code works OK, but if i add those authentication code back for the view, then the above code never get authenticated right.
The authentication code of the view is actually very simple, exactly like below (The second line):
def getAllTracks(request):
if request.user.is_authenticated():
tracks = Tracks.objects.all()
if tracks:
# Do sth. here
Which means if i delete the above second line(with some indents adjustments of course), then the requests.get() operation do the right thing for me, but if not(keep the second line), then it never get it right.
Any help would be appreciated.
In Django authentication works in following way:
There is a SessionMiddleware and AuthenticationMiddleware. The process_request() of both these classes is called before any view is called.
SessionMiddleware uses cookies at a lower level. It checks for a cookie named sessionid and try to associate this cookie with a user.
AuthenticationMiddleware checks if this cookie is associated with an user then sets request.user as that corresponding user. If the cookie sessionid is not found or can't be associated with any user, then request.user is set to an instance of AnonymousUser().
Since Http is a stateless protocol, django maintains session for a particular user using these two middlewares and using cookies at a lower level.
Coming to the code, so that requests can work with django.
You must first call the view where you authenticate and login the user. The response from this view will contain sessionid in cookies.
You should use this cookie and send it in the next request so that django can authenticate this particular user and so that your request.user.is_authenticated() passes.
from django.contrib.auth import authenticate, login
def login_user(request):
user = authenticate(username=request.POST.get('username'), password=request.POST.get('password'))
if user:
login(request, user)
return HttpResponse("Logged In")
return HttpResponse("Not Logged In")
def getAllTracks(request):
if request.user.is_authenticated():
return HttpResponse("Authenticated user")
return HttpResponse("Non Authenticated user")
Making the requests:
import requests
resp = requests.post('http://127.0.0.1:8000/login/', {'username': 'akshar', 'password': 'abc'})
print resp.status_code
200 #output
print resp.content
'Logged In' #output
cookies = dict(sessionid=resp.cookies.get('sessionid'))
print cookies
{'sessionid': '1fe38ea7b22b4d4f8d1b391e1ea816c0'} #output
response_two = requests.get('http://127.0.0.1:8000/getAllTracks/', cookies=cookies)
Notice that we pass cookies using cookies keyword argument
print response_two.status_code
200 #output
print response_two.content
'Authenticated user' #output
So, our request.user.is_authenticated() worked properly.
response_three = requests.get('http://127.0.0.1:8000/hogwarts/getAllTracks/')
Notice we do not pass the cookies here.
print response_three.content
'Non Authenticated user' #output
I guess, auth keyword for Requests enables HTTP Basic authentication which is not what is used in Django. You should make a POST request to login url of your project with username and password provided in POST data, after that your Requests instance will receive a session cookie with saved authentication data and will be able to do successful requests to auth-protected views.
Might be easier for you to just set a cookie on initial authentication, pass that back to the client, and then for future requests expect the client to send back that token in the headers, like so:
r = requests.post('http://127.0.0.1:8000', auth=(UN, PW))
self.token = r.cookies['token']
self.headers = {'token': token}
and then in further calls you could, assuming you're in the same class, just do:
r = requests.post('http://127.0.0.1:8000/getAllTracks', headers=self.headers)