def test_code_exchange_fails_when_redirect_uri_does_not_match(self): """ Tests code exchange fails when redirect uri does not match the one used for code request """ self.client.login(username="******", password="******") # retrieve a valid authorization code authcode_data = { 'client_id': self.application.client_id, 'state': 'random_state_string', 'scope': 'read write', 'redirect_uri': 'http://example.it?foo=bar', 'response_type': 'code', 'allow': True, } response = self.client.post(reverse('oauth2_provider:authorize'), data=authcode_data) query_dict = parse_qs(urlparse(response['Location']).query) authorization_code = query_dict['code'].pop() # exchange authorization code for a valid access token token_request_data = { 'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': 'http://example.it?foo=baraa' } auth_headers = get_basic_auth_header(self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) self.assertEqual(response.status_code, 401)
def test_refresh_invalidates_old_tokens(self): """ Ensure existing refresh tokens are cleaned up when issuing new ones """ self.client.login(username="******", password="******") authorization_code = self.get_auth() token_request_data = { 'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': 'http://example.it' } auth_headers = get_basic_auth_header(self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) content = json.loads(response.content.decode("utf-8")) rt = content['refresh_token'] at = content['access_token'] token_request_data = { 'grant_type': 'refresh_token', 'refresh_token': rt, 'scope': content['scope'], } response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) self.assertEqual(response.status_code, 200) self.assertFalse(RefreshToken.objects.filter(token=rt).exists()) self.assertFalse(AccessToken.objects.filter(token=at).exists())
def test_refresh_bad_scopes(self): """ Request an access token using a refresh token and wrong scopes """ self.client.login(username="******", password="******") authorization_code = self.get_auth() token_request_data = { 'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': 'http://example.it' } auth_headers = get_basic_auth_header(self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) content = json.loads(response.content.decode("utf-8")) self.assertTrue('refresh_token' in content) token_request_data = { 'grant_type': 'refresh_token', 'refresh_token': content['refresh_token'], 'scope': 'read write nuke', } response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) self.assertEqual(response.status_code, 401)
def test_refresh_fail_repeating_requests(self): """ Try refreshing an access token with the same refresh token more than once """ self.client.login(username="******", password="******") authorization_code = self.get_auth() token_request_data = { 'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': 'http://example.it' } auth_headers = get_basic_auth_header(self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) content = json.loads(response.content.decode("utf-8")) self.assertTrue('refresh_token' in content) token_request_data = { 'grant_type': 'refresh_token', 'refresh_token': content['refresh_token'], 'scope': content['scope'], } response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) self.assertEqual(response.status_code, 200) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) self.assertEqual(response.status_code, 401)
def get_access_token(self, scopes): self.client.login(username="******", password="******") # retrieve a valid authorization code authcode_data = { 'client_id': self.application.client_id, 'state': 'random_state_string', 'scope': scopes, 'redirect_uri': 'http://example.it', 'response_type': 'code', 'allow': True, } response = self.client.post(reverse('oauth2_provider:authorize'), data=authcode_data) query_dict = parse_qs(urlparse(response['Location']).query) authorization_code = query_dict['code'].pop() # exchange authorization code for a valid access token token_request_data = { 'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': 'http://example.it' } auth_headers = self.get_basic_auth_header( self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) content = json.loads(response.content.decode("utf-8")) return content['access_token']
def test_code_exchange_succeed_when_redirect_uri_match(self): """ Tests code exchange succeed when redirect uri matches the one used for code request """ self.client.login(username="******", password="******") # retrieve a valid authorization code authcode_data = { 'client_id': self.application.client_id, 'state': 'random_state_string', 'scope': 'read write', 'redirect_uri': 'http://example.it?foo=bar', 'response_type': 'code', 'allow': True, } response = self.client.post(reverse('oauth2_provider:authorize'), data=authcode_data) query_dict = parse_qs(urlparse(response['Location']).query) authorization_code = query_dict['code'].pop() # exchange authorization code for a valid access token token_request_data = { 'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': 'http://example.it?foo=bar' } auth_headers = self.get_basic_auth_header(self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) self.assertEqual(response.status_code, 200) content = json.loads(response.content.decode("utf-8")) self.assertEqual(content['token_type'], "Bearer") self.assertEqual(content['scope'], "read write") self.assertEqual(content['expires_in'], oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS)
def test_scopes_save_in_access_token(self): """ Test scopes are properly saved in access token """ self.client.login(username="******", password="******") # retrieve a valid authorization code authcode_data = { 'client_id': self.application.client_id, 'state': 'random_state_string', 'scope': 'scope1 scope2', 'redirect_uri': 'http://example.it', 'response_type': 'code', 'allow': True, } response = self.client.post(reverse('oauth2_provider:authorize'), data=authcode_data) query_dict = parse_qs(urlparse(response['Location']).query) authorization_code = query_dict['code'].pop() # exchange authorization code for a valid access token token_request_data = { 'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': 'http://example.it' } auth_headers = get_basic_auth_header(self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) content = json.loads(response.content.decode("utf-8")) access_token = content['access_token'] at = AccessToken.objects.get(token=access_token) self.assertEqual(at.scope, "scope1 scope2")
def test_refresh(self): """ Request an access token using a refresh token """ self.client.login(username="******", password="******") authorization_code = self.get_auth() token_request_data = { 'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': 'http://example.it' } auth_headers = get_basic_auth_header(self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) content = json.loads(response.content.decode("utf-8")) self.assertTrue('refresh_token' in content) # make a second token request to be sure the previous refresh token remains valid, see #65 authorization_code = self.get_auth() token_request_data = { 'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': 'http://example.it' } response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) token_request_data = { 'grant_type': 'refresh_token', 'refresh_token': content['refresh_token'], 'scope': content['scope'], } response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) self.assertEqual(response.status_code, 200) content = json.loads(response.content.decode("utf-8")) self.assertTrue('access_token' in content) # check refresh token cannot be used twice response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) self.assertEqual(response.status_code, 401) content = json.loads(response.content.decode("utf-8")) self.assertTrue('invalid_grant' in content.values())
def test_delete_view_post_actually_deletes(self): """ Test that a POST on this view works if the token belongs to the logged-in user. """ self.token = AccessToken.objects.create(user=self.foo_user, token='1234567890', application=self.application, expires=timezone.now() + datetime.timedelta(days=1), scope='read write') self.client.login(username="******", password="******") url = reverse("oauth2_provider:authorized-token-delete", kwargs={"pk": self.token.pk}) response = self.client.post(url) self.assertFalse(AccessToken.objects.exists()) self.assertRedirects(response, reverse('oauth2_provider:authorized-token-list'))
def test_list_view_authorization_required(self): """ Test that the view redirects to login page if user is not logged-in. """ response = self.client.get(reverse('oauth2_provider:authorized-token-list')) self.assertEqual(response.status_code, 302) self.assertTrue('/accounts/login/?next=' in response['Location'])
def test_password_resource_access_allowed(self): token_request_data = { 'grant_type': 'password', 'username': '******', 'password': '******', } auth_headers = self.get_basic_auth_header( self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) content = json.loads(response.content.decode("utf-8")) access_token = content['access_token'] # use token to access the resource auth_headers = { 'HTTP_AUTHORIZATION': 'Bearer ' + access_token, } request = self.factory.get("/fake-resource", **auth_headers) request.user = self.test_user view = ResourceView.as_view() response = view(request) self.assertEqual(response, "This is a protected resource")
def test_resource_access_allowed(self): self.client.login(username="******", password="******") # retrieve a valid authorization code authcode_data = { 'client_id': self.application.client_id, 'state': 'random_state_string', 'scope': 'read write', 'redirect_uri': 'http://example.it', 'response_type': 'token', 'allow': True, } response = self.client.post(reverse('oauth2_provider:authorize'), data=authcode_data) # within implicit grant, access token is in the url fragment frag_dict = parse_qs(urlparse(response['Location']).fragment) access_token = frag_dict['access_token'].pop() # use token to access the resource auth_headers = { 'HTTP_AUTHORIZATION': 'Bearer ' + access_token, } request = self.factory.get("/fake-resource", **auth_headers) request.user = self.test_user view = ResourceView.as_view() response = view(request) self.assertEqual(response, "This is a protected resource")
def test_basic_auth_grant_expired(self): """ Request an access token using an expired grant token """ self.client.login(username="******", password="******") g = Grant(application=self.application, user=self.test_user, code='BLAH', expires=timezone.now(), redirect_uri='', scope='') g.save() token_request_data = { 'grant_type': 'authorization_code', 'code': 'BLAH', 'redirect_uri': 'http://example.it' } auth_headers = get_basic_auth_header(self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) self.assertEqual(response.status_code, 401)
def test_basic_auth(self): """ Request an access token using basic authentication for client authentication """ self.client.login(username="******", password="******") authorization_code = self.get_auth() token_request_data = { 'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': 'http://example.it' } auth_headers = get_basic_auth_header(self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) self.assertEqual(response.status_code, 200) content = json.loads(response.content.decode("utf-8")) self.assertEqual(content['token_type'], "Bearer") self.assertEqual(content['scope'], "read write") self.assertEqual(content['expires_in'], oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS)
def test_client_credential_access_allowed(self): """ Request an access token using Client Credential Flow """ token_request_data = { 'grant_type': 'client_credentials', } auth_headers = self.get_basic_auth_header( self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) self.assertEqual(response.status_code, 200) content = json.loads(response.content.decode("utf-8")) access_token = content['access_token'] # use token to access the resource auth_headers = { 'HTTP_AUTHORIZATION': 'Bearer ' + access_token, } request = self.factory.get("/fake-resource", **auth_headers) request.user = self.test_user view = ResourceView.as_view() response = view(request) self.assertEqual(response, "This is a protected resource")
def test_pre_auth_approval_prompt(self): """ TODO """ tok = AccessToken.objects.create(user=self.test_user, token='1234567890', application=self.application, expires=timezone.now() + datetime.timedelta(days=1), scope='read write') self.client.login(username="******", password="******") query_string = urlencode({ 'client_id': self.application.client_id, 'response_type': 'code', 'state': 'random_state_string', 'scope': 'read write', 'redirect_uri': 'http://example.it', 'approval_prompt': 'auto', }) url = "{url}?{qs}".format(url=reverse('oauth2_provider:authorize'), qs=query_string) response = self.client.get(url) self.assertEqual(response.status_code, 302) # user already authorized the application, but with different scopes: prompt them. tok.scope = 'read' tok.save() response = self.client.get(url) self.assertEqual(response.status_code, 200)
def test_pre_auth_valid_client(self): """ Test response for a valid client_id with response_type: token """ self.client.login(username="******", password="******") query_string = urlencode({ 'client_id': self.application.client_id, 'response_type': 'token', 'state': 'random_state_string', 'scope': 'read write', 'redirect_uri': 'http://example.it', }) url = "{url}?{qs}".format(url=reverse('oauth2_provider:authorize'), qs=query_string) response = self.client.get(url) self.assertEqual(response.status_code, 200) # check form is in context and form params are valid self.assertIn("form", response.context) form = response.context["form"] self.assertEqual(form['redirect_uri'].value(), "http://example.it") self.assertEqual(form['state'].value(), "random_state_string") self.assertEqual(form['scope'].value(), "read write") self.assertEqual(form['client_id'].value(), self.application.client_id)
def test_public(self): """ Request an access token using client_type: public """ self.client.login(username="******", password="******") self.application.client_type = Application.CLIENT_PUBLIC self.application.save() authorization_code = self.get_auth() token_request_data = { 'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': 'http://example.it', 'client_id': self.application.client_id } response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data) self.assertEqual(response.status_code, 200) content = json.loads(response.content.decode("utf-8")) self.assertEqual(content['token_type'], "Bearer") self.assertEqual(content['scope'], "read write") self.assertEqual(content['expires_in'], oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS)
def test_multi_scope_valid(self): """ Test access to a multi-scope protected resource with correct scopes provided """ self.client.login(username="******", password="******") # retrieve a valid authorization code authcode_data = { 'client_id': self.application.client_id, 'state': 'random_state_string', 'scope': 'scope1 scope2', 'redirect_uri': 'http://example.it', 'response_type': 'code', 'allow': True, } response = self.client.post(reverse('oauth2_provider:authorize'), data=authcode_data) query_dict = parse_qs(urlparse(response['Location']).query) authorization_code = query_dict['code'].pop() # exchange authorization code for a valid access token token_request_data = { 'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': 'http://example.it' } auth_headers = self.get_basic_auth_header( self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) content = json.loads(response.content.decode("utf-8")) access_token = content['access_token'] # use token to access the resource auth_headers = { 'HTTP_AUTHORIZATION': 'Bearer ' + access_token, } request = self.factory.get("/fake-resource", **auth_headers) request.user = self.test_user view = MultiScopeResourceView.as_view() response = view(request) self.assertEqual(response, "This is a protected resource")
def test_empty_list_view(self): """ Test that when you have no tokens, an appropriate message is shown """ self.client.login(username="******", password="******") response = self.client.get(reverse('oauth2_provider:authorized-token-list')) self.assertEqual(response.status_code, 200) self.assertIn(b'There are no authorized tokens yet.', response.content)
def test_delete_view_authorization_required(self): """ Test that the view redirects to login page if user is not logged-in. """ self.token = AccessToken.objects.create(user=self.foo_user, token='1234567890', application=self.application, expires=timezone.now() + datetime.timedelta(days=1), scope='read write') url = reverse("oauth2_provider:authorized-token-delete", kwargs={"pk": self.token.pk}) response = self.client.get(url) self.assertEqual(response.status_code, 302) self.assertTrue('/accounts/login/?next=' in response['Location'])
def test_delete_view_works(self): """ Test that a GET on this view returns 200 if the token belongs to the logged-in user. """ self.token = AccessToken.objects.create(user=self.foo_user, token='1234567890', application=self.application, expires=timezone.now() + datetime.timedelta(days=1), scope='read write') self.client.login(username="******", password="******") url = reverse("oauth2_provider:authorized-token-delete", kwargs={"pk": self.token.pk}) response = self.client.get(url) self.assertEqual(response.status_code, 200)
def test_list_view_shows_correct_user_token(self): """ Test that only currently logged-in user's tokens are shown """ self.client.login(username="******", password="******") AccessToken.objects.create(user=self.foo_user, token='1234567890', application=self.application, expires=timezone.now() + datetime.timedelta(days=1), scope='read write') response = self.client.get(reverse('oauth2_provider:authorized-token-list')) self.assertEqual(response.status_code, 200) self.assertIn(b'There are no authorized tokens yet.', response.content)
def test_delete_view_token_belongs_to_user(self): """ Test that a 404 is returned when trying to GET this view with someone else's tokens. """ self.token = AccessToken.objects.create(user=self.foo_user, token='1234567890', application=self.application, expires=timezone.now() + datetime.timedelta(days=1), scope='read write') self.client.login(username="******", password="******") url = reverse("oauth2_provider:authorized-token-delete", kwargs={"pk": self.token.pk}) response = self.client.get(url) self.assertEqual(response.status_code, 404)
def test_client_credential_user_is_none_on_access_token(self): token_request_data = {'grant_type': 'client_credentials'} auth_headers = self.get_basic_auth_header( self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) self.assertEqual(response.status_code, 200) content = json.loads(response.content.decode("utf-8")) access_token = AccessToken.objects.get(token=content["access_token"]) self.assertIsNone(access_token.user)
def test_request_is_not_overwritten(self): self.client.login(username="******", password="******") query_string = urlencode({ 'client_id': self.application.client_id, 'response_type': 'code', 'state': 'random_state_string', 'scope': 'read write', 'redirect_uri': 'http://example.it', }) url = "{url}?{qs}".format(url=reverse('oauth2_provider:authorize'), qs=query_string) response = self.client.get(url) self.assertEqual(response.status_code, 200) assert 'request' not in response.context_data
def test_client_credential_does_not_issue_refresh_token(self): token_request_data = { 'grant_type': 'client_credentials', } auth_headers = self.get_basic_auth_header( self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) self.assertEqual(response.status_code, 200) content = json.loads(response.content.decode("utf-8")) self.assertNotIn("refresh_token", content)
def test_pre_auth_invalid_client(self): """ Test error for an invalid client_id with response_type: code """ self.client.login(username="******", password="******") query_string = urlencode({ 'client_id': 'fakeclientid', 'response_type': 'code', }) url = "{url}?{qs}".format(url=reverse('oauth2_provider:authorize'), qs=query_string) response = self.client.get(url) self.assertEqual(response.status_code, 400)
def test_pre_auth_wrong_response_type(self): """ Test error when passing a wrong response_type in query string """ self.client.login(username="******", password="******") query_string = urlencode({ 'client_id': self.application.client_id, 'response_type': 'WRONG', }) url = "{url}?{qs}".format(url=reverse('oauth2_provider:authorize'), qs=query_string) response = self.client.get(url) self.assertEqual(response.status_code, 302) self.assertIn("error=unsupported_response_type", response['Location'])
def test_basic_auth_bad_granttype(self): """ Request an access token using a bad grant_type string """ self.client.login(username="******", password="******") token_request_data = { 'grant_type': 'UNKNOWN', 'code': 'BLAH', 'redirect_uri': 'http://example.it' } auth_headers = self.get_basic_auth_header(self.application.client_id, self.application.client_secret) response = self.client.post(reverse('oauth2_provider:token'), data=token_request_data, **auth_headers) self.assertEqual(response.status_code, 400)