firebase_admin auth.verify_id_token is very slow
Asked Answered
F

3

11

I am resolving user data using firebase for auth like so:

from firebase_admin import auth decoded_token = auth.verify_id_token(client_id_token) I am initializing my firebase creds with firebase_admin.initialize_app(cred)

Here cliend_id_token is a token that the client sends. However, this takes around 1 second to perform, which seems way too long. One possibility is to use a caching layer above this (lru cache, memcache) but it still seems that it should not fundamentally take so long. Looking at the the signature of verify_id_token there does not seem to be anything that stands out as something that I can pass in:

def verify_id_token(id_token, app=None):

Any thoughts on how to diagnose (or if I am missing something)?

Flavorous answered 30/10, 2018 at 0:15 Comment(1)
I have this problem too with nodejsCrosley
G
4

I was having this same issue. Validation was taking anywhere from 200-400ms. That was something like 60-95% of the total request time for the service in general. This was using the Python SDK, but I imagine it's the same for every SDK.

The way I found to fix this was to manually validate the payload using Google's Certificates. Instructions can be found here: https://firebase.google.com/docs/auth/admin/verify-id-tokens#verify_id_tokens_using_a_third-party_jwt_library

A sample implementation in Python here:

from jose import jwt

...

def verify_token(token: str) -> dict[str, Any]:
    """
    Verifies the JWT Token without having to make a network request to Firebase.

    Args:
        token: The JWT Token to validate
    """
    alg = "RS256"
    headers = jwt.get_unverified_header(token)
    key_id = headers["kid"]
    if key_id not in settings.FIREBASE_CERTIFICATES.keys():
        raise Exception(f"Unknown key id: {key_id}")

    key = settings.FIREBASE_CERTIFICATES[key_id]
    decoded = jwt.decode(token, key, algorithms=[alg], audience=settings["FIREBASE_AUDIENCE"])
    current_ts = int(dt.datetime.now().timestamp())

    if decoded.get("exp", 0) < current_ts:
        raise Exception("Token expired")

    if decoded.get("iat", 0) > current_ts:
        raise Exception("Token issued in the future")

    if decoded.get("aud", "") != settings["FIREBASE_AUDIENCE"]:
        raise Exception("Invalid audience")

    if decoded.get("iss", "") != settings["FIREBASE_ISSUER"]:
        raise Exception("Invalid issuer")

    if not decoded.get("sub", ""):
        raise Exception("Invalid subject")

    if decoded.get("auth_time", 0) > current_ts:
        raise Exception("Invalid auth time")

    return {
        "uid": decoded.get("user_id", ""),
        "email": decoded.get("email", ""),
        "email_verified": decoded.get("email_verified", False),
        "phone_number": decoded.get("phone_number", ""),
    }

This uses a settings.FIREBASE_CERTIFICATES which as an object (Python's dict in this case) that holds the KeyID:Certificate pair. It also needs settings.FIREBASE_AUDIENCE which is the Project ID. The FIREBASE_ISSUER is just https://securetoken.google.com/<projectId>.

With that I had response times going from 500ms to 15ms. Insane. The downside is that if you're using the object that the validation method returns, this won't get you all the relevant information. For my use case specifically the JWT returns everything I needed, so no extra requests were needed.

Glanville answered 25/8, 2023 at 17:21 Comment(1)
For those who want a node.js implementation, see here.Atrioventricular
C
0

The problem is because that function does an http request in order to have the key to decode the jwt. In addition, because it returns info such as the email of the user, while the jwt contains only the uid as sub field of the decoded jwt, I think that it does another http request under the hood to get the user from the decoded uid.

You should implement your custom decode function, following the docs: https://firebase.google.com/docs/auth/admin/verify-id-tokens

Crosley answered 20/1, 2021 at 23:44 Comment(0)
N
0

I'm having the same issue. It's about 200ms for me (I'm using fastapi). @EuberDeveloper - glad to hear it's the same on node js - you saved me from testing it out.

I wanted to mention how I got my setup working faster in case anyone would benefit.

I've got Google API gateway with Firebase security defined in the swagger spec in front of a Cloud Run instance. API gateway validates the jwt (as per the swagger spec) and passes on the authorization header to the backend as a renamed header (from memory it's X-FORWARDED-AUTHORIZATION but best to double check). This is pretty fast.

Then in the backend you don't need to validate the id token since it'll already be validated by the time the request gets there. And if you send the UID along in the request to your backend as well as the idtoken in the authorization header, you can fetch users with the UID field you send. This removed that 200ms it was costing me to decode the id token.

Note - if you want to do things like check how old a refresh token is and revoke it for some reason then you'll still need to decode the id token.

Neoplasty answered 22/1, 2023 at 13:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.