17
17
18
18
import * as exp from '@firebase/auth-exp/internal' ;
19
19
import * as compat from '@firebase/auth-types' ;
20
+ import { FirebaseError } from '@firebase/util' ;
20
21
import { User } from './user' ;
21
22
23
+ const enum CredentialFrom {
24
+ ERROR = 'credentialFromError' ,
25
+ RESULT = 'credentialFromResult' ,
26
+ }
27
+
22
28
function credentialFromResponse (
23
29
userCredential : exp . UserCredentialInternal
24
30
) : exp . AuthCredential | null {
25
- const { providerId, _tokenResponse } = userCredential ;
31
+ return credentialFromObject ( userCredential ) ;
32
+ }
33
+
34
+ function modifyError (
35
+ auth : exp . Auth , e : FirebaseError ,
36
+ ) : void {
37
+ // The response contains all fields from the server which may or may not
38
+ // actually match the underlying type
39
+ const response = ( e . customData as exp . TaggedWithTokenResponse | undefined ) ?. _tokenResponse as unknown as Record < string , string > ;
40
+ if ( e . code === 'auth/multi-factor-auth-required' ) {
41
+ const mfaErr = e as compat . MultiFactorError ;
42
+ mfaErr . resolver = exp . getMultiFactorResolver ( auth , e as any ) ;
43
+ } else if ( response ) {
44
+ const credential = credentialFromObject ( e ) ;
45
+ const credErr = e as compat . AuthError ;
46
+ if ( credential ) {
47
+ credErr . credential = credential ;
48
+ credErr . tenantId = response . tenantId || undefined ;
49
+ credErr . email = response . email || undefined ;
50
+ credErr . phoneNumber = response . phoneNumber || undefined ;
51
+ }
52
+ }
53
+ }
54
+
55
+ function credentialFromObject ( object : FirebaseError | exp . UserCredential ) : exp . AuthCredential | null {
56
+ const { _tokenResponse} = ( object instanceof FirebaseError ? object . customData : object ) as exp . TaggedWithTokenResponse ;
26
57
if ( ! _tokenResponse ) {
27
58
return null ;
28
59
}
60
+
61
+ const fn = object instanceof FirebaseError ? CredentialFrom . ERROR : CredentialFrom . RESULT ;
62
+
29
63
// Handle phone Auth credential responses, as they have a different format
30
- // from other backend responses (i.e. no providerId).
31
- if ( 'temporaryProof' in _tokenResponse && 'phoneNumber' in _tokenResponse ) {
32
- return exp . PhoneAuthProvider . credentialFromResult ( userCredential ) ;
64
+ // from other backend responses (i.e. no providerId). This is also only the
65
+ // case for user credentials (does not work for errors).
66
+ if ( ! ( object instanceof FirebaseError ) ) {
67
+ if ( 'temporaryProof' in _tokenResponse && 'phoneNumber' in _tokenResponse ) {
68
+ return exp . PhoneAuthProvider . credentialFromResult ( object ) ;
69
+ }
33
70
}
71
+
72
+ const providerId = _tokenResponse . providerId ;
73
+
34
74
// Email and password is not supported as there is no situation where the
35
75
// server would return the password to the client.
36
76
if ( ! providerId || providerId === exp . ProviderId . PASSWORD ) {
37
77
return null ;
38
78
}
39
79
80
+ // We know for a fact that the function will match the value type
81
+ // (based on the declaration of fn). We will therefore cast object to a
82
+ // meaningless type to bypass the type system
83
+ const castObject = object as exp . UserCredential & FirebaseError ;
84
+
40
85
switch ( providerId ) {
41
86
case exp . ProviderId . GOOGLE :
42
- return exp . GoogleAuthProvider . credentialFromResult ( userCredential ) ;
87
+ return exp . GoogleAuthProvider [ fn ] ( castObject ) ;
43
88
case exp . ProviderId . FACEBOOK :
44
- return exp . FacebookAuthProvider . credentialFromResult ( userCredential ! ) ;
89
+ return exp . FacebookAuthProvider [ fn ] ( castObject ) ;
45
90
case exp . ProviderId . GITHUB :
46
- return exp . GithubAuthProvider . credentialFromResult ( userCredential ! ) ;
91
+ return exp . GithubAuthProvider [ fn ] ( castObject ) ;
47
92
case exp . ProviderId . TWITTER :
48
- return exp . TwitterAuthProvider . credentialFromResult ( userCredential ) ;
93
+ return exp . TwitterAuthProvider [ fn ] ( castObject ) ;
49
94
default :
50
95
const {
51
96
oauthIdToken,
@@ -63,21 +108,21 @@ function credentialFromResponse(
63
108
return null ;
64
109
}
65
110
// TODO(avolkovi): uncomment this and get it working with SAML & OIDC
66
- // if (pendingToken) {
67
- // if (providerId.indexOf(compat.constants.SAML_PREFIX ) == 0) {
68
- // return new impl.SAMLAuthCredential (providerId, pendingToken);
69
- // } else {
70
- // // OIDC and non-default providers excluding Twitter.
71
- // return new impl .OAuthCredential(
72
- // providerId,
73
- // {
74
- // pendingToken ,
75
- // idToken: oauthIdToken ,
76
- // accessToken: oauthAccessToken
77
- // },
78
- // providerId );
79
- // }
80
- // }
111
+ if ( pendingToken ) {
112
+ if ( providerId . indexOf ( 'saml.' ) == 0 ) {
113
+ return exp . SAMLAuthProvider . credential ( providerId , pendingToken ) ;
114
+ } else {
115
+ // OIDC and non-default providers excluding Twitter.
116
+ return exp . OAuthCredential . _fromParams (
117
+ {
118
+ providerId ,
119
+ signInMethod : providerId ,
120
+ pendingToken ,
121
+ idToken : oauthIdToken ,
122
+ accessToken : oauthAccessToken
123
+ } ) ;
124
+ }
125
+ }
81
126
return new exp . OAuthProvider ( providerId ) . credential ( {
82
127
idToken : oauthIdToken ,
83
128
accessToken : oauthAccessToken ,
@@ -94,12 +139,12 @@ export async function convertCredential(
94
139
try {
95
140
credential = await credentialPromise ;
96
141
} catch ( e ) {
97
- if ( e . code === 'auth/multi-factor-auth-required' ) {
98
- e . resolver = exp . getMultiFactorResolver ( auth , e ) ;
142
+ if ( e instanceof FirebaseError ) {
143
+ modifyError ( auth , e ) ;
99
144
}
100
145
throw e ;
101
146
}
102
- const { operationType, user } = await credential ;
147
+ const { operationType, user } = credential ;
103
148
104
149
return {
105
150
operationType,
0 commit comments