Token used too early error thrown by firebase_admin auth's verify_id_token method
Question:
Whenever I run
from firebase_admin import auth
auth.verify_id_token(firebase_auth_token)
It throws the following error:
Token used too early, 1650302066 < 1650302067. Check that your computer's clock is set correctly.
I’m aware that the underlying google auth APIs do check the time of the token, however as outlined here there should be a 10 second clock skew. Apparently, my server time is off by 1 second, however running this still fails even though this is well below the allowed 10 second skew. Is there a way to fix this?
Answers:
This is how the firebase_admin.verify_id_token verifies the token:
verified_claims = google.oauth2.id_token.verify_token(
token,
request=request,
audience=self.project_id,
certs_url=self.cert_url)
and this is the definition of google.oauth2.id_token.verify_token(…)
def verify_token(
id_token,
request,
audience=None,
certs_url=_GOOGLE_OAUTH2_CERTS_URL,
clock_skew_in_seconds=0,
):
As you can see, the function verify_token allows to specify a "clock_skew_in_seconds" but the firebase_admin function is not passing it along, thus the the default of 0 is used and since your server clock is off by 1 second, the check in verify_token fails.
I would consider this a bug in firebase_admin.verify_id_token and maybe you can open an issue against the firebase admin SDK, but other than that you can only make sure, your clock is either exact or shows a time EARLIER than the actual time
Edit:
I actually opened an issue on GitHub for firebase/firebase-admin-Python and created an according pull request since I looked at all the source files already anyway…
If and when the pull request is merged, the server’s clock is allowed to be off by up to a minute.
I see this still isn’t pulled. To fix it for me I did the following so it would retry to validate the token at the correct time.
@staticmethod
def decode_token(id_token: str) -> FirebaseToken:
"""Decode a Firebase ID token.
Args:
id_token (str): A valid Firebase `id_token`, this will be checked to determine if:
* The token is present and a valid JWT.
* The token has NOT expired.
* The token has NOT been revoked.
* The token has been issued under the correct GCP credentials (API key).
Returns:
FirebaseToken: A serialized Firebase ID token.
Raises:
HTTPException: If the token fails it's validation.
"""
try:
payload = JWTBearer.verify_token(id_token)
except ValueError as err:
raise HTTPException(
status_code=401, detail="Unable to verify token"
) from err
except (
ExpiredIdTokenError,
InvalidIdTokenError,
RevokedIdTokenError,
) as err:
# this happens on localhost all the time.
str_err = str(err)
if (str_err.find("Token used too early") > -1):
times = str_err.split(",")[1].split("<")
time = int(times[1]) - int(times[0])
sleep(time)
return JWTBearer.decode_token(id_token)
raise HTTPException(
status_code=403, detail=err.default_message
) from err
except CertificateFetchError as err:
raise HTTPException(
status_code=500,
detail="Failed to fetch public key certificates",
) from err
return FirebaseToken(**payload)
Whenever I run
from firebase_admin import auth
auth.verify_id_token(firebase_auth_token)
It throws the following error:
Token used too early, 1650302066 < 1650302067. Check that your computer's clock is set correctly.
I’m aware that the underlying google auth APIs do check the time of the token, however as outlined here there should be a 10 second clock skew. Apparently, my server time is off by 1 second, however running this still fails even though this is well below the allowed 10 second skew. Is there a way to fix this?
This is how the firebase_admin.verify_id_token verifies the token:
verified_claims = google.oauth2.id_token.verify_token(
token,
request=request,
audience=self.project_id,
certs_url=self.cert_url)
and this is the definition of google.oauth2.id_token.verify_token(…)
def verify_token(
id_token,
request,
audience=None,
certs_url=_GOOGLE_OAUTH2_CERTS_URL,
clock_skew_in_seconds=0,
):
As you can see, the function verify_token allows to specify a "clock_skew_in_seconds" but the firebase_admin function is not passing it along, thus the the default of 0 is used and since your server clock is off by 1 second, the check in verify_token fails.
I would consider this a bug in firebase_admin.verify_id_token and maybe you can open an issue against the firebase admin SDK, but other than that you can only make sure, your clock is either exact or shows a time EARLIER than the actual time
Edit:
I actually opened an issue on GitHub for firebase/firebase-admin-Python and created an according pull request since I looked at all the source files already anyway…
If and when the pull request is merged, the server’s clock is allowed to be off by up to a minute.
I see this still isn’t pulled. To fix it for me I did the following so it would retry to validate the token at the correct time.
@staticmethod
def decode_token(id_token: str) -> FirebaseToken:
"""Decode a Firebase ID token.
Args:
id_token (str): A valid Firebase `id_token`, this will be checked to determine if:
* The token is present and a valid JWT.
* The token has NOT expired.
* The token has NOT been revoked.
* The token has been issued under the correct GCP credentials (API key).
Returns:
FirebaseToken: A serialized Firebase ID token.
Raises:
HTTPException: If the token fails it's validation.
"""
try:
payload = JWTBearer.verify_token(id_token)
except ValueError as err:
raise HTTPException(
status_code=401, detail="Unable to verify token"
) from err
except (
ExpiredIdTokenError,
InvalidIdTokenError,
RevokedIdTokenError,
) as err:
# this happens on localhost all the time.
str_err = str(err)
if (str_err.find("Token used too early") > -1):
times = str_err.split(",")[1].split("<")
time = int(times[1]) - int(times[0])
sleep(time)
return JWTBearer.decode_token(id_token)
raise HTTPException(
status_code=403, detail=err.default_message
) from err
except CertificateFetchError as err:
raise HTTPException(
status_code=500,
detail="Failed to fetch public key certificates",
) from err
return FirebaseToken(**payload)