def post(self): args = login_parser.parse_args() user = User.query.filter(User.email==args.username).first() if user and args.password == user.password: auth_token = auth.encode_auth_token(user.id) return {'auth_token': auth_token.decode('utf-8')} else: return {'message': "Unauthorized"}, 401
def test_decode_auth_token(): '''Tests that a token can be decoded''' token = encode_auth_token('access', 123, datetime.timedelta(days=1), 'shazaam') res = decode_auth_token(token, 'shazaam') keys = ['sub', 'exp', 'iat', 'type'] assert set(keys) == set(res) assert res['sub'] == 123
def login(): '''Log a user in''' json_data = request.get_json() if not json_data: return jsonify({'error': 'Request body must be json.'}), 400 if not all(k in json_data for k in ('email', 'password')): return jsonify({'error': 'Login request must have email and password'}), 400 db_conn = get_db(current_app, g) result = get_user_by_email(db_conn, json_data['email']) # check that user exists if result is None: return jsonify({'error': 'Invalid email address or password'}), 400 # verify password salt = bytes(result['salt']) db_pass = bytes(result['password']) req_pass = bcrypt.hashpw(json_data['password'].encode('utf-8'), salt) if req_pass != db_pass: return jsonify({'error': 'Invalid email address or password'}), 400 # generate jwt access_token = encode_auth_token( 'access', result['id'], datetime.timedelta(minutes=20), current_app.config['SECRET_KEY'] ) refresh_token = encode_auth_token( 'refresh', result['id'], datetime.timedelta(days=5), current_app.config['SECRET_KEY'] ) return jsonify({ 'message': 'Success', 'email': result['email'], 'userId': result['id'], 'accessToken': access_token.decode(), 'refreshToken': refresh_token.decode() }), 200
def authenticate(): ''' Verifies a refresh token and generates an access token ''' token_data = get_data_from_token(request.headers, current_app.config['SECRET_KEY']) if 'error' in token_data: return jsonify({'error': token_data['error']}), token_data['status_code'] if token_data['type'] != 'refresh': return jsonify({'error': 'Invalid token type'}), 401 db_conn = get_db(current_app, g) user = get_user_by_id(db_conn, token_data['sub']) # create new access token access_token = encode_auth_token( 'access', token_data['sub'], datetime.timedelta(minutes=20), current_app.config['SECRET_KEY'] ) response_data = { 'message': 'Success', 'userId': token_data['sub'], 'email': user['email'], 'accessToken': access_token.decode() } print(token_data['exp']) print(datetime.datetime.utcnow()) # create a new refresh token if the current one is about to expire expiry_time = datetime.datetime.fromtimestamp(token_data['exp']) time_til_expiry = expiry_time - datetime.datetime.utcnow() if time_til_expiry <= datetime.timedelta(minutes=30): refresh_token = encode_auth_token( 'refresh', token_data['sub'], datetime.timedelta(days=5), current_app.config['SECRET_KEY'] ) response_data['refreshToken'] = refresh_token.decode() return jsonify(response_data)
def test_save_existing_project_fail(temp_app, temp_db): '''Tests various failure cases when saving an existing project''' # Tests trying to save a nonexistent project login_res = login_mackland(temp_app) auth_token = login_res['accessToken'] project = generate_temp_project() project['id'] = 0 res = put_save(project, auth_token, temp_app) res_data = json.loads(res.data) assert res.status_code == 404 assert res_data['error'] == 'Project does not exist' # Tests trying to save a project created by a different user project['id'] = 1 res = put_save(project, auth_token, temp_app) res_data = json.loads(res.data) assert res.status_code == 403 assert res_data['error'] == 'Forbidden: project belongs to another user' # Tests trying to save a project with no auth header res = temp_app.put('/save', data=json.dumps(project), content_type='application/json') res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'No authentication provided' # Tests trying to save a project with no auth token res = put_save(project, '', temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'No authentication provided' # Tests trying to save a project with an expired auth token token = generate_expired_token('access', temp_app.application.config['SECRET_KEY']) res = put_save(project, token, temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'Invalid token' # Tests trying to save a project with an invalid auth token token = generate_invalid_token('access') res = put_save(project, token, temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'Invalid token' # Tests trying to use a refresh token to access projects token = encode_auth_token('refresh', 1, datetime.timedelta(days=3), temp_app.application.config['SECRET_KEY']) res = put_save(project, token.decode(), temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'Invalid token type'
def test_get_new_refresh_token(temp_app, temp_db): '''Tests sending a refresh token that's about to expire, and getting a new one''' auth_token = encode_auth_token('refresh', 1, datetime.timedelta(minutes=30), temp_app.application.config['SECRET_KEY']) res = get_authenticate(auth_token.decode(), temp_app) res_data = json.loads(res.data) assert res.status_code == 200 keys = ['message', 'userId', 'email', 'accessToken', 'refreshToken'] assert set(keys) == set(res_data) assert res_data['email'] == '*****@*****.**'
def test_save_new_project_fail(temp_app, temp_db): '''Tests various failure cases when trying to save a new project''' # Tests trying to save a project with a duplicate name under the same user login_res = login_hello(temp_app) auth_token = login_res['accessToken'] data = generate_temp_project() data['name'] = 'New Project 1' res = post_save(data, auth_token, temp_app) res_data = json.loads(res.data) assert res.status_code == 400 assert res_data['error'] == 'A project with that name already exists' # Tests trying to save project with no auth header data = generate_temp_project() res = temp_app.post('/save', data=json.dumps(data), content_type='application/json') res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'No authentication provided' # Tests trying to save project with no auth token res = post_save(data, '', temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'No authentication provided' # Tests trying to save project with an expired token token = generate_expired_token('access', temp_app.application.config['SECRET_KEY']) res = post_save(data, token, temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'Invalid token' # Tests trying to save project with a token signed with the wrong key token = generate_invalid_token('access') res = post_save(data, token, temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'Invalid token' # Tests trying to use a refresh token to access projects token = encode_auth_token('refresh', 1, datetime.timedelta(days=3), temp_app.application.config['SECRET_KEY']) res = post_save(data, token.decode(), temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'Invalid token type'
def test_delete_project_fail(temp_app, temp_db): '''Tests various failure cases when trying to delete a project''' # Tests trying to delete a nonexistent project''' login_res = login_mackland(temp_app) auth_token = login_res['accessToken'] res = delete_project(0, auth_token, temp_app) res_data = json.loads(res.data) assert res.status_code == 400 assert res_data['error'] == 'Project does not exist' # Tests trying to delete a project created by a different user res = delete_project(1, auth_token, temp_app) res_data = json.loads(res.data) assert res.status_code == 403 assert res_data['error'] == 'Forbidden: project belongs to another user' # Tests trying to delete project with no auth header res = temp_app.delete('/project/1') res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'No authentication provided' # Tests trying to delete project with no auth token res = temp_app.delete('/project/1', headers=dict(Authorization='Bearer ')) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'No authentication provided' # Tests trying to delete project with an expired token token = generate_expired_token('access', temp_app.application.config['SECRET_KEY']) res = delete_project(1, token, temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'Invalid token' # Tests trying to delete project with a token signed with the wrong key token = generate_invalid_token('access') res = delete_project(1, token, temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'Invalid token' # Tests trying to use a refresh token to access projects token = encode_auth_token('refresh', 1, datetime.timedelta(days=3), temp_app.application.config['SECRET_KEY']) res = delete_project(1, token.decode(), temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'Invalid token type'
def test_get_projects_fail(temp_app, temp_db): '''Tests getting projects with various failure cases''' # Tests trying to get projects with no auth header res = temp_app.get('/projects') res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'No authentication provided' # Tests trying to get projects with no auth header res = temp_app.get('/projects', headers=dict(Authorization='Bearer ')) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'No authentication provided' # Tests trying to get projects with an expired token token = generate_expired_token('access', temp_app.application.config['SECRET_KEY']) res = get_projects(token, temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'Invalid token' # Tests trying to get projects with a token signed with the wrong key token = generate_invalid_token('access') res = get_projects(token, temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'Invalid token' # Tests trying to use a refresh token to access projects token = encode_auth_token('refresh', 1, datetime.timedelta(days=3), temp_app.application.config['SECRET_KEY']) res = get_projects(token.decode(), temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'Invalid token type'
def test_authenticate_fail(temp_app, temp_db): '''Tests various failure cases when verifying a jwt''' # Tests verifying with no auth header res = temp_app.get('auth/authenticate') res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'No authentication provided' # Tests verifying with no auth token res = get_authenticate('', temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'No authentication provided' # Tests trying to verify with an expired token auth_token = generate_expired_token( 'refresh', temp_app.application.config['SECRET_KEY']) res = get_authenticate(auth_token, temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'Invalid token' # Tests trying to verify with a token signed with the wrong key auth_token = generate_invalid_token('refresh') res = get_authenticate(auth_token, temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'Invalid token' # Tests trying to use an access token to refresh token = encode_auth_token('access', 1, datetime.timedelta(days=3), temp_app.application.config['SECRET_KEY']) res = get_authenticate(token.decode(), temp_app) res_data = json.loads(res.data) assert res.status_code == 401 assert res_data['error'] == 'Invalid token type'
def test_encode_auth_token(): '''Tests that a token can be encoded''' token = encode_auth_token('access', 134, datetime.timedelta(days=1), 'shazaam') assert isinstance(token, bytes)
def test_decode_token_wrong_key(): '''Tests that a token can't be decoded with the wrong key''' token = encode_auth_token('access', 123, datetime.timedelta(days=1), 'shazaam') res = decode_auth_token(token, 'kazaam') assert res is None