Repository navigation
feat(auth): Add IdTokenResult and User::GetIdTokenResult support - #1938
AustinBenoit wants to merge 2 commits into
Conversation
Implements IdTokenResult struct and User::GetIdTokenResult(bool force_refresh) with token parsing, custom claims extraction, and timestamp decoding. Supports Desktop, Android, and iOS platforms. Adds comprehensive unit tests, doc samples, and release notes.
| IdTokenResult() | ||
| : auth_timestamp(0), issued_at_timestamp(0), expiration_timestamp(0) {} | ||
|
|
||
| bool operator==(const IdTokenResult& other) const { |
There was a problem hiding this comment.
📝 Documentation issue: warning: Member operator==(const IdTokenResult &other) const (function) of struct firebase::auth::IdTokenResult is not documented.
| sign_in_second_factor == other.sign_in_second_factor; | ||
| } | ||
|
|
||
| bool operator!=(const IdTokenResult& other) const { |
There was a problem hiding this comment.
📝 Documentation issue: warning: Member operator!=(const IdTokenResult &other) const (function) of struct firebase::auth::IdTokenResult is not documented.
There was a problem hiding this comment.
Code Review
This pull request adds the IdTokenResult struct and the User::GetIdTokenResult() method to retrieve decoded ID token claims and metadata across Desktop, Android, and iOS platforms. It also introduces JWT parsing utilities and comprehensive tests. The review feedback suggests explicitly deleting the local JNI reference j_token in the Android implementation to prevent reference table overflow, and logging warnings on Desktop if token parsing fails to assist with debugging.
| jstring j_token = static_cast<jstring>(env->CallObjectMethod( | ||
| result, tokenresult::GetMethodId(tokenresult::kGetToken))); | ||
| assert(env->ExceptionCheck() == false); | ||
| std::string token = JniStringToString(env, j_token); | ||
| ParseIdToken(token, data); |
There was a problem hiding this comment.
In ReadIdTokenResult, the local JNI reference j_token returned by CallObjectMethod is not deleted. While local references are eventually cleaned up when returning to Java, in callbacks triggered from native threads, failing to delete local references can lead to local reference table overflow and crashes. We should explicitly delete j_token using env->DeleteLocalRef(j_token) after converting it to a C++ string.
jstring j_token = static_cast<jstring>(env->CallObjectMethod(
result, tokenresult::GetMethodId(tokenresult::kGetToken)));
assert(env->ExceptionCheck() == false);
std::string token = JniStringToString(env, j_token);
env->DeleteLocalRef(j_token);
ParseIdToken(token, data);| if (current_token.IsValid()) { | ||
| IdTokenResult id_token_result; | ||
| ParseIdToken(current_token.token(), &id_token_result); | ||
| promise.CompleteWithResult(id_token_result); | ||
| return promise.future(); | ||
| } |
There was a problem hiding this comment.
If ParseIdToken fails (returns false), the promise is still completed successfully with an empty or partially parsed IdTokenResult. This can lead to silent failures where the application receives a successful future but with invalid claims and timestamps. We should log a warning if ParseIdToken fails to help with debugging.
| if (current_token.IsValid()) { | |
| IdTokenResult id_token_result; | |
| ParseIdToken(current_token.token(), &id_token_result); | |
| promise.CompleteWithResult(id_token_result); | |
| return promise.future(); | |
| } | |
| if (current_token.IsValid()) { | |
| IdTokenResult id_token_result; | |
| if (!ParseIdToken(current_token.token(), &id_token_result)) { | |
| LogWarning("GetIdTokenResult: Failed to parse cached ID token."); | |
| } | |
| promise.CompleteWithResult(id_token_result); | |
| return promise.future(); | |
| } |
| IdTokenResult id_token_result; | ||
| ParseIdToken(get_token_result.token(), &id_token_result); | ||
| handle->promise.CompleteWithResult(id_token_result); | ||
| }; |
There was a problem hiding this comment.
Similarly, when completing the promise with a refreshed token, we should log a warning if ParseIdToken fails to parse the token.
IdTokenResult id_token_result;
if (!ParseIdToken(get_token_result.token(), &id_token_result)) {
LogWarning("GetIdTokenResult: Failed to parse refreshed ID token.");
}
handle->promise.CompleteWithResult(id_token_result);- Document IdTokenResult equality and inequality operators for Doxygen - Include <map> in id_token_util.cc for IWYU compliance - Delete local JNI reference j_token in ReadIdTokenResult - Log warnings on Desktop if ParseIdToken fails for cached or refreshed tokens - Use PRIu64 with cinttypes in doc_samples.cc instead of casting to unsigned long long - Reformat single-line block in user_desktop_test.cc
Description
Adds
IdTokenResultstruct andUser::GetIdTokenResult(bool force_refresh)to retrieve Firebase Auth ID token details, including standard claims, custom claims (std::map<Variant, Variant>), authentication/issued-at/expiration timestamps, and sign-in provider / second-factor metadata.Key changes:
IdTokenResultstruct andUser::GetIdTokenResult(),User::GetIdTokenResultLastResult(), andUser::GetIdTokenResultThreadSafe()tofirebase/auth/user.h.User::GetIdTokenResultusingEnsureFreshTokenand a JWT payload decoder with URL-safe base64 decoding and JSON parsing intoVariantmaps.User::GetIdTokenResultvia JNI callingFirebaseUser.getIdToken(forceRefresh).User::GetIdTokenResultusing[FIRUser getIDTokenResultWithCompletion:].IdTokenResultTestinauth/tests/id_token_result_test.ccandauth/tests/user_test.cc).auth/samples/src/doc_samples.cc.release_build_files/readme.md.Testing
firebase_authdesktop library locally.firebase_auth_id_token_result_test(12 unit test cases passing, covering valid tokens with standard & custom claims, base64url padding variants, malformed tokens, and equality operators).scripts/format_code.py -git_diff).Type of Change
Place an
xthe applicable box:Notes
Release Notessection ofrelease_build_files/readme.md.