Skip to content

Commit 5e51836

Browse files
authored
fix(auth,web): convert Auth errors that omit customData without throwing (#18686)
JS Auth errors sometimes omit customData, which caused a TypeError in getFirebaseAuthException after the sign-in failure was already handled. Fixes #18680
1 parent 82ac5ff commit 5e51836

4 files changed

Lines changed: 75 additions & 9 deletions

File tree

‎packages/firebase_auth/firebase_auth_web/lib/src/interop/auth_interop.dart‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ extension AuthErrorExtension on AuthError {
630630
external set tenantId(JSString s);
631631
external JSString get phoneNumber;
632632
external set phoneNumber(JSString s);
633-
external JSObject get customData;
633+
external JSObject? get customData;
634634
}
635635

636636
extension type AuthErrorCustomData._(JSObject _) implements JSObject {

‎packages/firebase_auth/firebase_auth_web/lib/src/utils/web_utils.dart‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ FirebaseAuthException getFirebaseAuthException(
9292
.replaceFirst('Firebase: ', '');
9393

9494
// "customData" - see Firebase AuthError docs: https://firebase.google.com/docs/reference/js/auth.autherror
95-
final customData = exception.customData as auth_interop.AuthErrorCustomData;
95+
// Some Auth error shapes omit customData; casting a missing value throws TypeError.
96+
final customData = exception.customData as auth_interop.AuthErrorCustomData?;
9697

9798
if (code == 'multi-factor-auth-required') {
9899
final _auth = auth;
@@ -111,9 +112,9 @@ FirebaseAuthException getFirebaseAuthException(
111112
return FirebaseAuthMultiFactorExceptionPlatform(
112113
code: code,
113114
message: message,
114-
email: customData.email?.toDart,
115-
phoneNumber: customData.phoneNumber?.toDart,
116-
tenantId: customData.tenantId?.toDart,
115+
email: customData?.email?.toDart,
116+
phoneNumber: customData?.phoneNumber?.toDart,
117+
tenantId: customData?.tenantId?.toDart,
117118
resolver: MultiFactorResolverWeb(
118119
resolverWeb.hints.map(fromInteropMultiFactorInfo).toList(),
119120
MultiFactorSessionWeb('web', resolverWeb.session),
@@ -127,9 +128,9 @@ FirebaseAuthException getFirebaseAuthException(
127128
return FirebaseAuthException(
128129
code: code,
129130
message: message,
130-
email: customData.email?.toDart,
131-
phoneNumber: customData.phoneNumber?.toDart,
132-
tenantId: customData.tenantId?.toDart,
131+
email: customData?.email?.toDart,
132+
phoneNumber: customData?.phoneNumber?.toDart,
133+
tenantId: customData?.tenantId?.toDart,
133134
credential: credential,
134135
);
135136
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2020, the Chromium project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
@TestOn('browser')
6+
library;
7+
8+
import 'dart:js_interop';
9+
import 'dart:js_interop_unsafe';
10+
11+
import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart';
12+
import 'package:firebase_auth_web/src/utils/web_utils.dart';
13+
import 'package:flutter_test/flutter_test.dart';
14+
15+
void main() {
16+
setUp(() {
17+
// getFirebaseAuthException always calls OAuthProvider.credentialFromError.
18+
// Stub it so this test does not need the full Auth JS SDK.
19+
final oauthProvider = JSObject();
20+
oauthProvider['credentialFromError'] = ((JSAny _) => null).toJS;
21+
final firebaseAuth = JSObject();
22+
firebaseAuth['OAuthProvider'] = oauthProvider;
23+
globalContext['firebase_auth'] = firebaseAuth;
24+
});
25+
26+
JSObject authError({required bool includeCustomData}) {
27+
final error = JSObject();
28+
error['name'] = 'FirebaseError'.toJS;
29+
error['code'] = 'auth/invalid-credential'.toJS;
30+
error['message'] =
31+
'Firebase: INVALID_LOGIN_CREDENTIALS (auth/invalid-credential).'.toJS;
32+
if (includeCustomData) {
33+
final customData = JSObject();
34+
customData['appName'] = '[DEFAULT]'.toJS;
35+
customData['email'] = 'user@example.com'.toJS;
36+
error['customData'] = customData;
37+
}
38+
return error;
39+
}
40+
41+
test(
42+
'converts an Auth error that omits customData without throwing',
43+
() {
44+
final exception = getFirebaseAuthException(
45+
authError(includeCustomData: false),
46+
);
47+
48+
expect(exception, isA<FirebaseAuthException>());
49+
expect(exception.code, 'invalid-credential');
50+
expect(exception.email, isNull);
51+
expect(exception.phoneNumber, isNull);
52+
expect(exception.tenantId, isNull);
53+
},
54+
);
55+
56+
test('still reads email from customData when it is present', () {
57+
final exception = getFirebaseAuthException(
58+
authError(includeCustomData: true),
59+
);
60+
61+
expect(exception.code, 'invalid-credential');
62+
expect(exception.email, 'user@example.com');
63+
});
64+
}

‎packages/firebase_core/firebase_core_web/lib/src/interop/utils/es6_interop.dart‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ extension JSErrorExtension on JSError {
2222
external JSString? get stack;
2323

2424
// "customData" - see Firebase AuthError docs: https://firebase.google.com/docs/reference/js/auth.autherror
25-
external JSAny get customData;
25+
// Auth errors from the JS SDK usually include this, but some error shapes omit it.
26+
external JSAny? get customData;
2627
}

0 commit comments

Comments
 (0)