"""Functional test for authenticating a user.""" import base64 import json import faker from flask import jsonify import rsa from auth import handlers # noqa from auth.app import app from auth.logic import user from auth.models.client import clients def test_success_roundtrip(monkeypatch): """Test a full roundtrip. A roundtrip is (almost) an e2e test. This makes the system go through the /authorize endpoint, then send inforamtion to the /login and finish by getting the access token. """ navigator = app.test_client() # information about the client client = list(clients.values())[0] # information about the user. fake = faker.Faker() login = fake.user_name() password = fake.password() email = fake.email() user.create_user(login, password, email) # set the navigator. client = list(clients.values())[0] current_state = 'state' # authorize request: this request is meant to display the form that asks # the user a login and password. it also creates the rsa keys and set the # request id. def render_template( name, keys=None, client_id=None, state=None, redirect=None): """Mock for rendering template. Args: name (string): template name. client_id (str): the client id. state (str): the state of the token. redirect (str): url to redirect the user to. """ assert state == current_state assert str(client.id) == client_id return jsonify(keys) monkeypatch.setattr(handlers, 'render_template', render_template) authorize_request = navigator.get( '/authorize?client_id={}&state={}'.format(client.id, current_state)) authorize_response = json.loads(authorize_request.data.decode('utf8')) assert authorize_request.status_code == 200 public_key = authorize_response['public_key'] request_id = authorize_response['token'] # with the data retrieved, encrypt the user password with the public pem # key. since javascript doesn't support binary, we will use base64 to turn # the encrypted key into a string. public_key = rsa.PublicKey.load_pkcs1_openssl_pem( authorize_response.get('public_key').encode('utf8')) encrypted_password = base64.b64encode( rsa.encrypt(password.encode('utf8'), public_key)).decode('utf8') # when a user validate the form and right after the password has been # encrypted, a request to /login is instantiated - which checks the # different information provided. on success, it returns a code that will # be used to fetch the access token. login_request = navigator.post('/login', data=dict( client_id=client.id, request_id=request_id, login=login, password=encrypted_password)) assert login_request.status_code == 200 login_response = json.loads(login_request.data.decode('utf8')) code = login_response['code'].encode('utf8') assert login_response['redirect'] # final step: request the access token. this happens behind the scene by # the service itself, using the code that was returned by the /login. access_token_request = navigator.post('/token', data=dict( client_id=client.id, client_secret=client.secret, code=code)) assert access_token_request.status_code == 200 access_token_response = json.loads( access_token_request.data.decode('utf8')) assert access_token_response.get('access_token') assert access_token_response.get('expires_in') assert access_token_response.get('token_type') == 'bearer' # try to retrieve the user info with the provided client id, # client secret and token me_request = navigator.get( '/me?client_id={}&client_secret={}&token={}'.format( client.id, client.secret, access_token_response.get('access_token'))) assert json.loads(me_request.data.decode('utf8')) assert me_request.status_code == 200 # try to revoke the user token revoke_request = navigator.get( '/revoke?token={}&client_id={}&client_secret={}'.format( access_token_response.get('access_token'), client.id, client.secret)) revoke_response = json.loads(revoke_request.data.decode('utf8')) assert revoke_request.status_code == 200 assert revoke_response.get('revoked') assert isinstance(revoke_response.get('revoked'), bool) # try to revoke token without passing expected param revoke_request2 = navigator.get( '/revoke?token={}&client_id={}&client_secret={}'.format( access_token_response.get('access_token'), client.id, client.secret)) assert revoke_request2.status_code == 403