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' ;
21
+ import { Auth } from './auth' ;
20
22
import { User } from './user' ;
23
+ import { unwrap , wrapped } from './wrap' ;
21
24
22
25
function credentialFromResponse (
23
26
userCredential : exp . UserCredentialInternal
24
27
) : exp . AuthCredential | null {
25
- const { providerId, _tokenResponse } = userCredential ;
28
+ return credentialFromObject ( userCredential ) ;
29
+ }
30
+
31
+ function attachExtraErrorFields ( auth : exp . Auth , e : FirebaseError ) : void {
32
+ // The response contains all fields from the server which may or may not
33
+ // actually match the underlying type
34
+ const response = ( ( e . customData as exp . TaggedWithTokenResponse | undefined )
35
+ ?. _tokenResponse as unknown ) as Record < string , string > ;
36
+ if ( e . code === 'auth/multi-factor-auth-required' ) {
37
+ const mfaErr = e as compat . MultiFactorError ;
38
+ mfaErr . resolver = new MultiFactorResolver (
39
+ auth ,
40
+ exp . getMultiFactorResolver ( auth , e as exp . MultiFactorError )
41
+ ) ;
42
+ } else if ( response ) {
43
+ const credential = credentialFromObject ( e ) ;
44
+ const credErr = e as compat . AuthError ;
45
+ if ( credential ) {
46
+ credErr . credential = credential ;
47
+ credErr . tenantId = response . tenantId || undefined ;
48
+ credErr . email = response . email || undefined ;
49
+ credErr . phoneNumber = response . phoneNumber || undefined ;
50
+ }
51
+ }
52
+ }
53
+
54
+ function credentialFromObject (
55
+ object : FirebaseError | exp . UserCredential
56
+ ) : exp . AuthCredential | null {
57
+ const { _tokenResponse } = ( object instanceof FirebaseError
58
+ ? object . customData
59
+ : object ) as exp . TaggedWithTokenResponse ;
26
60
if ( ! _tokenResponse ) {
27
61
return null ;
28
62
}
63
+
29
64
// 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 ) ;
65
+ // from other backend responses (i.e. no providerId). This is also only the
66
+ // case for user credentials (does not work for errors).
67
+ if ( ! ( object instanceof FirebaseError ) ) {
68
+ if ( 'temporaryProof' in _tokenResponse && 'phoneNumber' in _tokenResponse ) {
69
+ return exp . PhoneAuthProvider . credentialFromResult ( object ) ;
70
+ }
33
71
}
72
+
73
+ const providerId = _tokenResponse . providerId ;
74
+
34
75
// Email and password is not supported as there is no situation where the
35
76
// server would return the password to the client.
36
77
if ( ! providerId || providerId === exp . ProviderId . PASSWORD ) {
37
78
return null ;
38
79
}
39
80
81
+ let provider : Pick <
82
+ typeof exp . OAuthProvider ,
83
+ 'credentialFromResult' | 'credentialFromError'
84
+ > ;
40
85
switch ( providerId ) {
41
86
case exp . ProviderId . GOOGLE :
42
- return exp . GoogleAuthProvider . credentialFromResult ( userCredential ) ;
87
+ provider = exp . GoogleAuthProvider ;
88
+ break ;
43
89
case exp . ProviderId . FACEBOOK :
44
- return exp . FacebookAuthProvider . credentialFromResult ( userCredential ! ) ;
90
+ provider = exp . FacebookAuthProvider ;
91
+ break ;
45
92
case exp . ProviderId . GITHUB :
46
- return exp . GithubAuthProvider . credentialFromResult ( userCredential ! ) ;
93
+ provider = exp . GithubAuthProvider ;
94
+ break ;
47
95
case exp . ProviderId . TWITTER :
48
- return exp . TwitterAuthProvider . credentialFromResult ( userCredential ) ;
96
+ provider = exp . TwitterAuthProvider ;
97
+ break ;
49
98
default :
50
99
const {
51
100
oauthIdToken,
@@ -63,27 +112,30 @@ function credentialFromResponse(
63
112
return null ;
64
113
}
65
114
// 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
- // }
115
+ if ( pendingToken ) {
116
+ if ( providerId . startsWith ( 'saml.' ) ) {
117
+ return exp . SAMLAuthCredential . _create ( providerId , pendingToken ) ;
118
+ } else {
119
+ // OIDC and non-default providers excluding Twitter.
120
+ return exp . OAuthCredential . _fromParams ( {
121
+ providerId,
122
+ signInMethod : providerId ,
123
+ pendingToken,
124
+ idToken : oauthIdToken ,
125
+ accessToken : oauthAccessToken
126
+ } ) ;
127
+ }
128
+ }
81
129
return new exp . OAuthProvider ( providerId ) . credential ( {
82
130
idToken : oauthIdToken ,
83
131
accessToken : oauthAccessToken ,
84
132
rawNonce : nonce
85
133
} ) ;
86
134
}
135
+
136
+ return object instanceof FirebaseError
137
+ ? provider . credentialFromError ( object )
138
+ : provider . credentialFromResult ( object ) ;
87
139
}
88
140
89
141
export async function convertCredential (
@@ -94,12 +146,12 @@ export async function convertCredential(
94
146
try {
95
147
credential = await credentialPromise ;
96
148
} catch ( e ) {
97
- if ( e . code === 'auth/multi-factor-auth-required' ) {
98
- e . resolver = exp . getMultiFactorResolver ( auth , e ) ;
149
+ if ( e instanceof FirebaseError ) {
150
+ attachExtraErrorFields ( auth , e ) ;
99
151
}
100
152
throw e ;
101
153
}
102
- const { operationType, user } = await credential ;
154
+ const { operationType, user } = credential ;
103
155
104
156
return {
105
157
operationType,
@@ -124,3 +176,30 @@ export async function convertConfirmationResult(
124
176
convertCredential ( auth , confirmationResultExp . confirm ( verificationCode ) )
125
177
} ;
126
178
}
179
+
180
+ class MultiFactorResolver implements compat . MultiFactorResolver {
181
+ readonly auth : Auth ;
182
+ constructor (
183
+ auth : exp . Auth ,
184
+ private readonly resolver : exp . MultiFactorResolver
185
+ ) {
186
+ this . auth = wrapped ( auth ) ;
187
+ }
188
+
189
+ get session ( ) : compat . MultiFactorSession {
190
+ return this . resolver . session ;
191
+ }
192
+
193
+ get hints ( ) : compat . MultiFactorInfo [ ] {
194
+ return this . resolver . hints ;
195
+ }
196
+
197
+ resolveSignIn (
198
+ assertion : compat . MultiFactorAssertion
199
+ ) : Promise < compat . UserCredential > {
200
+ return convertCredential (
201
+ unwrap ( this . auth ) ,
202
+ this . resolver . resolveSignIn ( assertion as exp . MultiFactorAssertion )
203
+ ) ;
204
+ }
205
+ }
0 commit comments