Skip to content

Commit 41e7e46

Browse files
committed
fix(auth, android): handle null id-tokens from google credential manager
1 parent a4b3407 commit 41e7e46

3 files changed

Lines changed: 90 additions & 1 deletion

File tree

packages/auth/__tests__/auth.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,26 @@ describe('Auth', function () {
413413
expect(credential?.idToken).toBe('id-token');
414414
});
415415

416+
it('GoogleAuthProvider.credential maps id-token-only credentials for native bridge', function () {
417+
const credential = GoogleAuthProvider.credential('google-id-token', null);
418+
expect(credential.providerId).toBe('google.com');
419+
expect(credential.signInMethod).toBe('google.com');
420+
expect(credential.idToken).toBe('google-id-token');
421+
expect(credential.accessToken).toBeUndefined();
422+
expect(credential.token).toBe('google-id-token');
423+
expect(credential.secret).toBe('');
424+
});
425+
426+
it('GoogleAuthProvider.credential maps access-token-only credentials for native bridge', function () {
427+
const credential = GoogleAuthProvider.credential(null, 'google-access-token');
428+
expect(credential.providerId).toBe('google.com');
429+
expect(credential.signInMethod).toBe('google.com');
430+
expect(credential.accessToken).toBe('google-access-token');
431+
expect(credential.idToken).toBeUndefined();
432+
expect(credential.token).toBe('');
433+
expect(credential.secret).toBe('google-access-token');
434+
});
435+
416436
it('PhoneAuthCredential.fromJSON deserializes phone credentials', function () {
417437
const credential = PhoneAuthCredential.fromJSON({
418438
verificationId: 'vid',

packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2046,7 +2046,12 @@ private AuthCredential getCredentialForProvider(
20462046
case "facebook.com":
20472047
return FacebookAuthProvider.getCredential(authToken);
20482048
case "google.com":
2049-
return GoogleAuthProvider.getCredential(authToken, authSecret);
2049+
// Firebase Android rejects empty tokens; id-token-only and access-token-only flows require
2050+
// null.
2051+
// https://firebase.google.com/docs/reference/android/com/google/firebase/auth/GoogleAuthProvider#getCredential(java.lang.String,java.lang.String)
2052+
String googleIdToken = (authToken == null || authToken.isEmpty()) ? null : authToken;
2053+
String googleAccessToken = (authSecret == null || authSecret.isEmpty()) ? null : authSecret;
2054+
return GoogleAuthProvider.getCredential(googleIdToken, googleAccessToken);
20502055
case "twitter.com":
20512056
return TwitterAuthProvider.getCredential(authToken, authSecret);
20522057
case "github.com":

packages/auth/e2e/provider.e2e.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,70 @@ describe('auth() -> Providers', function () {
236236
'At least one of ID token and access token must be non-null',
237237
);
238238
});
239+
240+
it('should accept id-token-only credentials (Credential Manager)', function () {
241+
const { GoogleAuthProvider } = authModular;
242+
243+
const token = 'google-id-token-only';
244+
const credential = GoogleAuthProvider.credential(token, null);
245+
credential.providerId.should.equal('google.com');
246+
credential.signInMethod.should.equal('google.com');
247+
credential.token.should.equal(token);
248+
credential.secret.should.equal('');
249+
credential.idToken.should.equal(token);
250+
should.equal(credential.accessToken, undefined);
251+
});
252+
253+
it('should accept access-token-only credentials', function () {
254+
const { GoogleAuthProvider } = authModular;
255+
256+
const accessToken = 'google-access-token-only';
257+
const credential = GoogleAuthProvider.credential(null, accessToken);
258+
credential.providerId.should.equal('google.com');
259+
credential.signInMethod.should.equal('google.com');
260+
credential.token.should.equal('');
261+
credential.secret.should.equal(accessToken);
262+
credential.accessToken.should.equal(accessToken);
263+
should.equal(credential.idToken, undefined);
264+
});
265+
266+
it('should reach Android native signInWithCredential for id-token-only Google credentials', async function () {
267+
if (!Platform.android) {
268+
this.skip();
269+
}
270+
const { getApp } = modular;
271+
const { GoogleAuthProvider, signInWithCredential, getAuth } = authModular;
272+
const defaultAuth = getAuth(getApp());
273+
const credential = GoogleAuthProvider.credential('google-id-token-only-e2e', null);
274+
275+
try {
276+
await signInWithCredential(defaultAuth, credential);
277+
throw new Error('Did not error.');
278+
} catch (error) {
279+
error.code.should.be.a.String();
280+
error.code.should.not.equal('auth/unknown');
281+
String(error.message).should.not.match(/accessToken cannot be empty/i);
282+
}
283+
});
284+
285+
it('should reach Android native signInWithCredential for access-token-only Google credentials', async function () {
286+
if (!Platform.android) {
287+
this.skip();
288+
}
289+
const { getApp } = modular;
290+
const { GoogleAuthProvider, signInWithCredential, getAuth } = authModular;
291+
const defaultAuth = getAuth(getApp());
292+
const credential = GoogleAuthProvider.credential(null, 'google-access-token-only-e2e');
293+
294+
try {
295+
await signInWithCredential(defaultAuth, credential);
296+
throw new Error('Did not error.');
297+
} catch (error) {
298+
error.code.should.be.a.String();
299+
error.code.should.not.equal('auth/unknown');
300+
String(error.message).should.not.match(/idToken cannot be empty/i);
301+
}
302+
});
239303
});
240304

241305
describe('GOOGLE_SIGN_IN_METHOD', function () {

0 commit comments

Comments
 (0)