5
votes

I am using the firebase-admin-python SDK to handle authentication between an iOS app and a flask backend (python). This is my backend authentication endpoint, following the firebase guide:

from flask import request
from firebase_admin import auth


def get():
    """accessed via '/api/authtoken' """
    try:
        fir_token = request.headers["Authorization"]
        decoded_token = auth.verify_id_token(fir_token)
        fir_auth_id = decoded_token["uid"]
    except:
        ...

How do I mock the fir_token for a unit test? How do I also mock auth.verify_id_token such that I don't need to actually connect to the firebase server?

2

2 Answers

3
votes

Put the logic behind an interface.

class TokenVerifier(object):
    def verify(self, token):
        raise NotImplementedError()


class FirebaseTokenVerifier(TokenVerifier):
    def verify(self, token):
        return auth.verify_id_token(token)


class MockTokenVerifier(TokenVerifier):
    def verify(self, token):
         # Return mock object that will help pass your tests.
         # Or raise an error to simulate token validation failures.

Then make sure during unit tests your code uses the MockTokenVerifier.

It is also possible to create mock ID tokens, and stub out parts of the Admin SDK so that the auth.verify_id_token() runs normally during tests (see unit tests of the SDK). But I prefer the above solution since it's easier, cleaner and doesn't require messing with the internals of the SDK.

0
votes

I achieved this using the patch decorator from unittest.mock library.

auth.py

...
from flask_restful import Resource, reqparse
from firebase_admin import auth

class Token(Resource):
    def __init__(self):
        self.parser = reqparse.RequestParser()
        self.parser.add_argument('token', type=str, required=True)

    # route for /api/auth/token
    def post(self):
        args = self.parser.parse_args()

        try:
            firebase_user = auth.verify_id_token(args['token'])
        except Exception:
            abort(401)

        # use firebase_user

test_auth.py

from unittest import mock


mock_firebase_user = {
    'user_id': 'firebasegenerateduserid',
    'email': '[email protected]',
    # ... add more firebase return values
}

# client is from conftest.py
def test_auth(client):
    with mock.patch('auth.auth.verify_id_token') as magic_mock:
        magic_mock.return_value = mock_firebase_user
        post_data = {
            'token': 'firebaserusertokenid'
        }

        response = client.post('/api/auth/token', data=post_data)
        assert response.status_code == 200