I tried to use Django AllAuth to make a registration user by Facebook.
I have to make a REST API so I would to use Django REST Framework.
I found this simple tutorial to make the first user registration but probably there are some difference from current implementation:
I tried this code:
def post(self, request):
data = JSONParser().parse(request)
access_token = data.get('access_token', '')
try:
app = SocialApp.objects.get(provider="facebook")
token = SocialToken(app=app, token=access_token)
# check token against facebook
login = fb_complete_login(app, token)
login.token = token
login.state = SocialLogin.state_from_request(request)
# add or update the user into users table
ret = complete_social_login(request, login)
# if we get here we've succeeded
return Response(status=200, data={
'success': True,
'username': request.user.username,
'user_id': request.user.pk,
})
except:
traceback.print_exc()
return Response(status=401, data={
'success': False,
'reason': "Bad Access Token",
})
but now I see that fb_complete_login take 3 parameters: request, app and token.
So, I tried to put also the request like this function parameter but some lines later I have an error on login = fb_complete_login(app, token).
TypeError: add_message() argument must be an HttpRequest object, not
'Request'.
Any suggestions are welcome!
Solution given here: http://tech.agilitynerd.com/django-rest-registration-with-django-rest-auth.html
To disable messaging just for allauth, override the adapter (for instance in main.adapters):
from allauth.account.adapter import DefaultAccountAdapter
class MessageFreeAdapter(DefaultAccountAdapter):
def add_message(self, request, level, message_template,
message_context=None, extra_tags=''):
pass
then add this in settings.py:
ACCOUNT_ADAPTER = 'main.adapters.MessageFreeAdapter'
Related
In my frontend i'm logging into another app's api in the browser, I'm then redirected back to my app, that hits a View in my backend which gets a code from the other app's api, sends code back in a post request then receives an access token and stores it in a model associated with the current user.
My problem is that after the user gives permission to other app in the browser it redirects back to my backend view without the users token in the header so if i have permissions_classes set it wont allow user to access that view... but if i take the permissions_classes off, the view won't know who the current user is.
View #1 that prepares the other app's API url:
class getAPIAuthURL(APIView):
authentication_class = [authentication.TokenAuthentication]
permission_class = [permissions.IsAuthenticated]
def get(self, request):
scopes = 'scopes'
url = Request('GET', 'https://accounts.api.com/authorize',
params={
'scope': scopes,
'response_type': 'code',
'redirect_uri': REDIRECT_URL,
'client_id': CLIENT_ID
}
).prepare().url
return Response(url, status=status.HTTP_200_OK)
View #2 that gets data and stores it in model (this is the REDIRECT_URL from previous view):
class APICallback(APIView):
authentication_class = [authentication.TokenAuthentication]
permission_class = [permissions.IsAuthenticated]
def api_callback(request, format=None):
code = request.GET.get('code')
if not code:
return Response({'Error': 'Code not found in request'}, status=status.HTTP_400_BAD_REQUEST)
response = post('https://accounts.api.com/api/token', data={
'code': code,
}).json()
print(response)
user = request.user
access_token = response.get('access_token')
token = APITokenModel(user=user, access_token=access_token)
token.save()
return redirect('frontend')
I have other Views that make requests and it has been able to get the token to know who the user is, but when this View is called I get a 401 Unauthorized error.
How do I let Django know the token I'm receiving from the other app's api belongs to the current user?
also... when I take off permissions and authentication class from the View it returns the user as Anonymous User
First, what authentication class are you using? You should know that your TokenAuthentication class uses the Authorization header in your request to authenticate you. If that's not been passed then you should fix that.
It would be worth knowing that you don't send auth tokens as GET and should not be sent as those. Unless of course you want to write an Authentication class of your own.
EDIT
In lieu of our discuss in the comments, try this redirect...
# import the class
from django.http import HttpResponseRedirect
# now redirect
return HttpResponseRedirect(redirect_to="url", headers=dict)
I am using JWT auth to login users. Username and password are sent in Body, however, in the customized response, an anonymUser is always returned. I think the problem is that in settings.py stands 'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION', and when I generate a token before and then send it in Headers the user is identified. Bit the thing is, that I cannot use 2 views in order to generate token and decode it, everything has to be in one view and I don't know how to login the user in the view and then get token and decode it.
#api_view(('POST',))
def check_token(request):
token_refresh = RefreshToken.for_user(request.user)
print(request.user) # AnonymUser
print(request.user.id) # None
print(str(token_refresh.access_token))
data = {'token': str(token_refresh.access_token), 'refresh_token': str(token_refresh)}
aT = str.encode(str(token_refresh.access_token))
try:
valid_data = TokenBackend(algorithm='HS256').decode(aT, verify=False)
print(valid_data)
data['uuid'] = valid_data['user_id']
data['validUntil'] = valid_data['exp']
data['clientId'] = 'default'
return JsonResponse(data)
except ValidationError as v:
print("Validation error", v)
The answer can be found here
The The request.user is set by the django.contrib.auth.middleware.AuthenticationMiddleware.
So the request.user does not know about JWT as it is using Djangos authentication system. You can read about using JWT with Django here
I'm creating a Restful API using Django Rest Framework, i'm not serving sensitive data but still i wanted to add some sort of authorization system for viewing my API endpoints.
Basically each user has an API key assigned, and in order to view any endpoint, the user needs to provide the key when performing any request. All the endpoints use only GET to retrieve the data, so what i did is the following:
The API key is provided in the GET params, so something like myURL/api/endpoint/?key=1234&filter=test
A middleware checks if that API key exists in my database, and if it does the user is able to get the data.
Here is my middleware:
TOKEN_QUERY = "key"
class TokenMiddleware(AuthenticationMiddleware):
def process_request(self, request):
if request.user.is_authenticated:
return None
else:
try:
token = request.GET[TOKEN_QUERY]
except Exception as e:
# A token isn't included in the query params
return JsonResponse({'error': 'Missing parameter: make sure to include your key.'})
try:
query = API_keys.objects.get(api_token=token)
except:
token = None
if token != None:
return None
else:
return JsonResponse({'error': 'Authentication failed. Make sure to provid a valid API key.'})
This system works without any problem, but i'm concerned about safety. How safe is this? Should i not use a GET request (of course i'll make sure to use HTTPS and SSL) ? Or is there a de facto way to create this kind of system? Any kind of advice is appreciated.
You can try this
from rest_framework import permissions
TOKEN_QUERY = "key"
# guest token validation class
class GuestTokenPermission(permissions.BasePermission):
def __init__(self, allowed_methods):
self.allowed_methods = allowed_methods
def has_permission(self, request, view):
token = request.META.get('HTTP_GUEST_TOKEN', None)
if token == TOKEN_QUERY:
return request.method in self.allowed_methods
else:
if request.user.is_superuser:
return request.method in self.allowed_methods
# put where you want to set permission
permission_classes = (partial(GuestTokenPermission, ['GET', 'POST', 'HEAD']),)
Refer https://www.django-rest-framework.org/api-guide/permissions/
I'm working on a single page application using django, djangoRestFramework and Angular1.x. I've implemented a login view using the built in login method, but anytime I make a request to the api, it returns with csrf verification failed.
I wrapped my login view with the csrf_protect decorator like:
class LoginView(views.APIView):
#method_decorator(csrf_protect)
def post(self, request):
user = authenticate(
username=request.data.get('username'),
password=request.data.get('password')
)
if user is None or not user.is_active:
return Response({
'status': 'Unauthorized',
'message': 'Username or password incorrect'
}, status=status.HTTP_401_UNAUTHORIZED)
login(request,user)
return Response(UserSerializer(user).data)
And I ran angular.config like:
angular.run(['$http', function($http) {
$http.defaults.xsrfHeaderName = 'X-CSRFToken';
$http.defaults.xsrfCookieName = 'csrftoken';
}])
When I take out the csrf_protect decorator, it still gives me an error saying
'Request object has no attribute session'.
Also when i'm working on localhost and I visit the django admin page before I try logging in, it doesn't give me an error. I can't figure out the issue here, any help would be appreciated
Actually this works for me with angular 1.4,
var myApp = angular.module('myApp', []).config(function($httpProvider)
{
$httpProvider.defaults.xsrfCookieName = 'csrftoken'
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'
});
I found this snippet of code that helps me authenticate a user and then create a rest_framework token for them. The client I am using is a native android app and I will get the access token from the client side and post it to django in the ObtainAuth class.
Here is the code for the server side.
#psa('social:complete')
def register_by_access_token(request, backend):
backend = request.strategy.backend
# Split by spaces and get the array
auth = get_authorization_header(request).split()
if not auth or auth[0].lower() != b'token':
msg = 'No token header provided.'
return msg
if len(auth) == 1:
msg = 'Invalid token header. No credentials provided.'
return msg
access_token = auth[1]
user = backend.do_auth(access_token)
return user
class ObtainAuthToken(APIView):
model = Token
serializer_class = AuthTokenSerializer
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
def post(self,request):
serializer = self.serializer_class(data= request.DATA)
if backend == 'auth':
if serializer.is_valid:
token, created = Token.objects.get_or_create(user=serializer.object['user'])
if token:
return Response({'token': token.key})
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
else:
user = register_by_access_token(request, backend)
if user and user.is_active:
token, created = Token.objects.get_or_create(user=user)
return Response({'id': user.id, 'email': user.email, 'firstname': user.first_name, 'userRole': 'user', 'token': token.key})
The register_by_access_token method will get the facebook access token and then create a user with the rest_framework.It takes a request and the backend to be used e.g 'facebook'.
If a user logs in with my backend then the backend is 'auth' and it uses the normal process of retrieving the email and password and then giving me a token to use.As detailed here
My question is how do I post the authentication backend be it 'facebook' or 'auth' so that I can receive the token?
What I've tried.
I have tried sending the backend type ('facebook' or 'auth') with the access token but I get an error that the method takes 3 arguments and I've only provided 2.
I've tried making the url take a backend like this:
url(r'^login/(?P<backend>[^/]+)/$',views.ObtainAuthToken.as_view())
then sending the access token to a url like this mysite.com:8000/login/facebook.
None of these work and I don't have much expereience with psa or django to know how to pass this parameter.
How do I send which backend to use so that it can be accepted by the method? If anyone has ever had this use case please help me out.
according to my understanding social login requires a access token , so when you are login with facebook when you call 'mysite.com:8000/login/facebook' it is expecting a access token,
for my project i defined my urls as 'url(r'^login/(?P[^/]+)/$',register_by_access_token,name='register_by_access_token')',with the token i am sending it completes the login, for facebook i send backend as 'facebook' for google i send backend as 'google-oauth2' but both the case i am sending a token given my respective backend, when you are not using a third party backend you wont get the token and the login expects that.
so my suggestion is if you are going with auth use normal login post, not the same URL.