I have an oauth flow in which a user grants access to a certain scope and then my application can do stuff. For this to work, I need an access token with the defined scope.
I implemented this (with the django allauth package) and it works great. But...
I would like to test it.
This is what I have so far (request package is like an urllib on steroids):
login = self.client.login(username='test_user', password='test')
self.assertTrue(login)
resp = self.client.post(reverse('oauth_login'))
self.assertEqual(resp.status_code, 302)
payload = {'session_key': 'email', 'session_password': 'pw', }
resp2 = requests.post(resp['location'], data=payload)
resp3 = self.client.get(reverse('do_stuff_with_access_token'))
self.assertEqual(resp.status_code, 302)
The issue here is that I do not get the access token in my request variables. My guess is that I am going out of the scope of the application and Django does not get the variable in its request scope.
How can you test this in an elegant manner? Mocking an access_token seems a bit wrong to me. I am now trying to go Selenium for filling in the form, but even that is not really a success so far...
Thanks!
Kudos to Mark!
To help anyone on their way.
Mocking worked out. In my case (django 1.4) you need to add your tokens to the session. A lot of different advices can be found on the net, but I like simple things and this works in Django at least with the test suite:
session = self.client.session
session['request_token'] = {...}
session['access_token'] = {...}
session.save()
I faced the same problem and found the following solved my dilemma:
user = User.objects.get(username='lauren')
client = APIClient()
client.force_authenticate(user=user)
This was taken directly from the Django documentation:
http://www.django-rest-framework.org/api-guide/testing/#force_authenticateusernone-tokennone
Unfortunately it took a number of searches before getting to this point. I hope this saves someone else some time.
Related
I have a problem setting cookies in Django.
Basically I have 3 different cookie I wanna set:
Session ID
Access token
Refresh token
For some reason Access and Refresh tokens are set, but the Session ID (SSID) doesn't set. If I change key of "SSID" to for example "TEST_COOKIE" it passes and I can see it in dev tools. However I need SSID and for some magical reason it doesn't work.
Here's example of my code:
class AuthResponse(SuccessResponse):
def __init__(self, data={}, ssid='', access_token: str = '', refresh_token: str = '', **kwargs):
super().__init__(data, **kwargs)
self.set_cookie(key=settings.SESSION_COOKIE_NAME,
value=ssid,)
if access_token:
self.set_cookie(key=settings.ACCESS_KEY_COOKIE_NAME,
value=access_token,)
if refresh_token:
self.set_cookie(key=settings.REFRESH_KEY_COOKIE_NAME,
value=refresh_token,)
AuthResponse inherits from SuccessResponse which is based on DjangoJsonResponse, and DjangoJsonResponse eventually inherits from HttpResponse.
So the question is - what could cause of getting rid of "SSID" cookie?
I tried to look around and find if all the data appears in init function and apprently eveyrthing is fine. All data, ssid, access_token and refresh_token come through, but only "SSID" doesn't get set.
As well I tried to use "httponly" and "secure" while setting cookies, but it didn't help.
There was an idea that might be middleware affects somehow on this, however I don't know who to check this out...
Is there anyone familiar with this who can potentially make an advice of why is this happening?
I found the answer while working on localhost the SESSION_COOKIE_DOMAIN should not be used, so I made it in this way in settings.py:
if website_settings.current_instance != 'dev':
SESSION_COOKIE_DOMAIN = (
website_settings.session_cookie_domain
if website_settings.session_cookie_domain
else f".{SITE_DOMAIN}"
)
This way it saves all needed cookies and browser sees them.
I'm trying to test drf-social-oauth2's integration with Google via python manage.py test with the following test class:
class DRFSocialOAuthIntegrationTest(TestCase):
def setUp(self):
self.test_user = UserModel.objects.create_user("test_user", "test#user.com", TEST_USER_PASSWORD)
self.application = Application(
name="Test Application",
redirect_uris="",
user=self.test_user,
client_type=Application.CLIENT_CONFIDENTIAL,
authorization_grant_type=Application.GRANT_PASSWORD, # https://github.com/jazzband/django-oauth-toolkit/blob/master/oauth2_provider/models.py
)
self.application.save()
# Every test needs a client.
self.client = Client()
def tearDown(self):
self.application.delete()
self.test_user.delete()
def test_drf_social_oauth2_integration(self):
'''Following testing steps found at curl -X POST -d "grant_type=convert_token&client_id=<django-oauth-generated-client_id>&client_secret=<django-oauth-generated-client_secret>&backend=google-oauth2&token=<google_token>" http://localhost:8000/auth/convert-token'''
def convert_google_token(self):
'''
Convert Google token to our access token
curl -X POST -d "grant_type=convert_token&client_id=<client_id>&client_secret=<client_secret>&backend=google-oauth2&token=<google_token>" http://localhost:8000/auth/convert-token
'''
return self.client.post(
'/auth/convert-token',
{
'grant_type': 'convert_token',
'client_id': self.application.client_id,
'client_secret': self.application.client_secret,
'backend': 'google-oauth2',
'token': <google_token>
}
)
That seems to work fine, but I have to keep feeding it the google_token by manually navigating to https://developers.google.com/oauthplayground/?code=<my_code>&scope=email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+openid&authuser=0&prompt=consent#:
Once there, I click on the 'Exchange authorization code for tokens' button, get the access token, and paste that access token into the tokenparameter of the request inside convert_google_token(). This doesn't feel very automated. I'm not sure if I should just click the checkbox so that the access token is refreshed automatically and therefore never have to edit it in convert_google_token(). Or maybe I'm supposed to get that access token programmatically. But I believe that would entail getting the authorization code first, which would have to also be programmatically since it is a one-time code, right?
So in order to get that authorization code, I was trying to parse the html response from developers.google.com/oauthplayground/ like so:
def get_google_authorization_code_html():
import requests
return requests.post(
'https://developers.google.com/oauthplayground/',
json={
'code': <my_code>,
'scope': "email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+openid",
'authuser': "0",
'prompt': "consent"
}
)
response = get_google_authorization_code_html() #;print(f'Google authorization code html returned: {response.content=}')
self.assertEqual(response.status_code, 200)
from bs4 import BeautifulSoup
soup = BeautifulSoup(response.content) ;print('Google authorization code html return: '); print(soup.prettify())
However, the input element that in the browser shows the authorization code is actually blank in the html response, so I can't retrieve it from there. Would this even be the proper way to go about this? I may be missing key knowledge as I'm pretty new to OAuth2, Django, and testing. I've been trying to read up on it and particularly diagrams such as the one found in this Medium article make me think I'm more or less on the right track, but I'm not sure.
You are making a trivial mistake there. Whenever you need the response of a third-party API for testing purposes, you have to mock the response of the API and also/perhaps mock some of your code as well. In this case, you must mock the drf convert-token, otherwise, you will have to keep reading google's token (and this is not ideal).
I did a lot of research on this topic, but it's still not working for me.
I set my csrftoken cookie in Django,and it does in the response object.
But in any browser, it says no cookies in this site
Backend:
#ensure_csrf_cookie
def home(request):
csrf_token = get_token(request)
response = HttpResponse()
response = render(request, 'index.html')
response.set_cookie(key='csrftoken', value=csrf_token)
return response
Angular:
myapp.config(function($httpProvider){
//I use this when in angular1.0.x
//$http.defaults.headers.post['X-CSRFToken'] = $cookies['csrftoken'];
//now in angular1.2.x I use code below. but none of them works
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
});
When I do a POST I get message
Failed to load resource: the server responded with a status of 403 (FORBIDDEN)
Also if I print out header info in the $http error function:
console.log(header('Set-Cookie'));
console.log(header('Access-Control-Allow-Headers'));
console.log(header('Access-Control-Allow-Methods'));
all of these three are null.
I can't figure it why! Especially, it works fine in localhost, either Firefox or Chrome, but in an Apache server, always no cookie in this site.
Is there any setting should I do? Can anyone help my with this issue?
I'm not sure this will help, but your view is terribly written. You're trying to force the csrf in about five different ways, and you also have some redundant lines that don't do anything (you do response = HttpResponse() and then override it completely, making that line completely void). so there's a good chance one of them is screwing things over.
The point is - when you use render you don't need to do anything else to enforce the csrf (you know, except for making sure it's enabled). That's the point of using it over render_to_response. Try this much simpler version and see how much it helps:
def home(request):
return render(request, 'index.html')
Please check the domain of the cookie set by Django.
Be aware of cross-domain requests.
$http docs : Angular provides a mechanism to counter XSRF, When performing XHR requests but will not be set for cross-domain requests.
Here is a small lib that might help you https://github.com/pasupulaphani/angular-csrf-cross-domain/blob/master/dist/angular-csrf-cross-domain.js
Try including the ngCookies module in your application.
myApp.run(function ($http, $cookies) {
$http.defaults.headers.common['X-CSRFToken'] = $cookies.csrftoken;
});
So I have a Django app, which as part of its functionality makes a request (using the requests module) to another server. What I want to do is have a server available for unittesting which gives me canned responses to test requests from the Django app (allowing to test how Django handles the different potential responses).
An example of the code would be:
payload = {'access_key': key,
'username': name}
response = requests.get(downstream_url, params=payload)
# Handle response here ...
I've read that you can use SimpleHTTPServer to accomplish this, but I'm not sure of how I use it to this end, any thoughts would be much appreciated!
Use the mock module.
from mock import patch, MagicMock
#patch('your.module.requests')
def test_something(self, requests_mock):
response = MagicMock()
response.json.return_value = {'key': 'value'}
requests_mock.get.return_value = response
…
requests_mock.get.assert_called_once_with(…)
response.json.assert_called_once()
Much more examples in the docs.
You don't need to (and should not) test the code that makes the request. You want to mock out that part and focus on testing the logic that handles the response.
This is my first post, and I have a problem I could not make it work django OMAB socialauth of three things I just need to google, facebook, and twitter, google works well with open id, but not much twitter and I put in my
settings. py:
TWITTER_CONSUMER_KEY = '00' this is no real
TWITTER_CONSUMER_SECRET = '00' this is no real
FACEBOOK_APP_ID = '' ihave no key
FACEBOOK_API_SECRET = ''
LINKEDIN_CONSUMER_KEY = ''
LINKEDIN_CONSUMER_SECRET = ''
ORKUT_CONSUMER_KEY = ''
ORKUT_CONSUMER_SECRET = ''ihave no key
GOOGLE_OAUTH2_CLIENT_ID = ''
GOOGLE_OAUTH2_CLIENT_SECRET = ''
SOCIAL_AUTH_CREATE_USERS = True
SOCIAL_AUTH_FORCE_RANDOM_USERNAME = False
SOCIAL_AUTH_DEFAULT_USERNAME = 'socialauth_user'
SOCIAL_AUTH_COMPLETE_URL_NAME = 'socialauth_complete'
LOGIN_ERROR_URL = '/login/error/'
#SOCIAL_AUTH_USER_MODEL = 'app.CustomUser'
SOCIAL_AUTH_ERROR_KEY = 'socialauth_error'
GITHUB_APP_ID = ''
GITHUB_API_SECRET = ''
FOURSQUARE_CONSUMER_KEY = ''
FOURSQUARE_CONSUMER_SECRET = ''
LOGIN_URL = '/login-form/'
LOGIN_REDIRECT_URL = '/'
LOGIN_ERROR_URL = '/login-error/'
I am using the example that comes in the zip of OMAB socialauth django , but not working.
When I created my twitter app, I wrote my domain www.sisvei.com , I am testing locally socialauth django ie 127.0.0.1:8000, then sign in with twitter sends me to this url:
http://127.0.0.1:8000/login/error/ and a message saying is the Incorrect authentication service
this happens with facebook and google oauth and oauth2
I'm new to django and I this much work comprising this part of django socialath hopefully help me, thank you very much.
You need to be more specific on "why it doesn't work". Where are you getting the errors?
When debugging a third-party oauth/openid app in Django, generally it boils down to:
configuration & keys - did you make sure to obtain all of the necessary API keys for the services you will be using, and to add them to your configuration?
urls - did you remember to add the necessary urlpatterns to your base urls.py file?
authentication setup on the server - often, you'll need to have a file available or respond with a specific header when the authentication service hits your server. Have you checked to make sure that is set up?
databases - have you run syncdb after installing the app? Are all the tables set up?
templates - if the third party app requires you to set up templates, do you have them set up?
custom views - are you using custom views? If so, try using the built-in views that came with the third party app first, to see if they work
After those are confirmed, you're going to want to be able to see what requests are taking place. Use the debugger included in Chrome/Safari, or get the web developer add-on for Firefox, and look at the network requests as they happen. Do you see HTTP responses other than 200 (say, 404, 500, 403, etc?) those mean that the services aren't responding correctly.
From your error, it looks like you have not correctly set up your callback URL on Twitter. It should be sending you to www.sisvei.com, not 127.0.0.1. Alternatively, check the URL when you get to the Twitter login page -- is the callback URL in the URL, and is it pointing to 127.0.0.1? Then Django is sending it the wrong callback URL.
Finally this:
I wrote my domain www.sisvei.com python does not support this
Is unclear. As far as I know, Python doesn't care what the domain is.
WAIT A MINUTE ...
Are you using runserver? Are you getting the following error?
Error: "www.sisvei.com" is not a valid port number or address:port pair.
If so, there is an easy fix! Just run it like so:
python manage.py runserver www.sisvei.com:80
That should resolve your error if that's what's happening. You're probably running it as
python manage.py runserver 127.0.0.1
127.0.0.1 is a reserved IP address that points back to localhost, your own computer. As a result, it is not possible to use it for authentication or any other purpose outside of programs running on your own machine. See this article for more info.
I'm not sure, but I might be having similar problems, oscar. For me, SocialAuth was generating an AuthenticationURL for facebook, foursquare and hotmail, but not for google, twitter or any of the other address it supports. I think it may be something wrong with the API, so I posted an issue on the social-auth google group...you may want to check there to see if anyone updates!!
https://code.google.com/p/socialauth/issues/detail?id=282&colspec=ID%20Type%20Status%20Priority%20Milestone%20Owner%20Summary%20Modified