@@ -25,8 +25,8 @@ public class FirebaseAuthProvider : IDisposable, IFirebaseAuthProvider
25
25
private const string GoogleSetAccountUrl = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/setAccountInfo?key={0}" ;
26
26
private const string GoogleCreateAuthUrl = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/createAuthUri?key={0}" ;
27
27
private const string GoogleUpdateUserPassword = "https://identitytoolkit.googleapis.com/v1/accounts:update?key={0}" ;
28
-
29
-
28
+
29
+
30
30
private const string ProfileDeleteDisplayName = "DISPLAY_NAME" ;
31
31
private const string ProfileDeletePhotoUrl = "PHOTO_URL" ;
32
32
@@ -55,7 +55,7 @@ public async Task<FirebaseAuthLink> SignInWithCustomTokenAsync(string customToke
55
55
firebaseAuthLink . User = await this . GetUserAsync ( firebaseAuthLink . FirebaseToken ) . ConfigureAwait ( false ) ;
56
56
return firebaseAuthLink ;
57
57
}
58
-
58
+
59
59
/// <summary>
60
60
/// Using the idToken of an authenticated user, get the details of the user's account
61
61
/// </summary>
@@ -66,14 +66,14 @@ public async Task<User> GetUserAsync(string firebaseToken)
66
66
var content = $ "{{\" idToken\" :\" { firebaseToken } \" }}";
67
67
var responseData = "N/A" ;
68
68
try
69
- {
69
+ {
70
70
var response = await this . client . PostAsync ( new Uri ( string . Format ( GoogleGetUser , this . authConfig . ApiKey ) ) , new StringContent ( content , Encoding . UTF8 , "application/json" ) ) . ConfigureAwait ( false ) ;
71
71
responseData = await response . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
72
72
response . EnsureSuccessStatusCode ( ) ;
73
73
74
74
var resultJson = JObject . Parse ( responseData ) ;
75
75
var user = JsonConvert . DeserializeObject < User > ( resultJson [ "users" ] . First ( ) . ToString ( ) ) ;
76
- return user ;
76
+ return user ;
77
77
}
78
78
catch ( Exception ex )
79
79
{
@@ -92,15 +92,26 @@ public async Task<User> GetUserAsync(FirebaseAuth auth)
92
92
}
93
93
94
94
/// <summary>
95
- /// Using the provided access token from third party auth provider (google, facebook...), get the firebase auth with token and basic user credentials.
95
+ /// Using the provided access token from third party auth provider (google, facebook...), or ID token (apple), get the firebase auth with token and basic user credentials.
96
96
/// </summary>
97
97
/// <param name="authType"> The auth type. </param>
98
- /// <param name="oauthAccessToken "> The access token retrieved from login provider of your choice. </param>
98
+ /// <param name="oauthToken "> The access token or ID token retrieved from login provider of your choice. </param>
99
99
/// <returns> The <see cref="FirebaseAuth"/>. </returns>
100
- public async Task < FirebaseAuthLink > SignInWithOAuthAsync ( FirebaseAuthType authType , string oauthAccessToken )
100
+ public async Task < FirebaseAuthLink > SignInWithOAuthAsync ( FirebaseAuthType authType , string oauthToken )
101
101
{
102
102
var providerId = this . GetProviderId ( authType ) ;
103
- var content = $ "{{\" postBody\" :\" access_token={ oauthAccessToken } &providerId={ providerId } \" ,\" requestUri\" :\" http://localhost\" ,\" returnSecureToken\" :true}}";
103
+
104
+ string content ;
105
+
106
+ switch ( authType )
107
+ {
108
+ case FirebaseAuthType . Apple :
109
+ content = $ "{{\" postBody\" :\" id_token={ oauthToken } &providerId={ providerId } \" ,\" requestUri\" :\" http://localhost\" ,\" returnSecureToken\" :true}}";
110
+ break ;
111
+ default :
112
+ content = $ "{{\" postBody\" :\" access_token={ oauthToken } &providerId={ providerId } \" ,\" requestUri\" :\" http://localhost\" ,\" returnSecureToken\" :true}}";
113
+ break ;
114
+ }
104
115
105
116
FirebaseAuthLink firebaseAuthLink = await this . ExecuteWithPostContentAsync ( GoogleIdentityUrl , content ) . ConfigureAwait ( false ) ;
106
117
firebaseAuthLink . User = await this . GetUserAsync ( firebaseAuthLink . FirebaseToken ) . ConfigureAwait ( false ) ;
@@ -174,7 +185,7 @@ public async Task<FirebaseAuthLink> SignInWithEmailAndPasswordAsync(string email
174
185
firebaseAuthLink . User = await this . GetUserAsync ( firebaseAuthLink . FirebaseToken ) . ConfigureAwait ( false ) ;
175
186
return firebaseAuthLink ;
176
187
}
177
-
188
+
178
189
/// <summary>
179
190
/// Change a password from an user with his token.
180
191
/// </summary>
@@ -187,8 +198,8 @@ public async Task<FirebaseAuthLink> ChangeUserPassword(string idToken, string pa
187
198
188
199
return await this . ExecuteWithPostContentAsync ( GoogleUpdateUserPassword , content ) . ConfigureAwait ( false ) ;
189
200
}
190
-
191
-
201
+
202
+
192
203
/// <summary>
193
204
/// Creates new user with given credentials.
194
205
/// </summary>
@@ -273,15 +284,15 @@ public async Task DeleteUserAsync(string firebaseToken)
273
284
{
274
285
var content = $ "{{ \" idToken\" : \" { firebaseToken } \" }}";
275
286
var responseData = "N/A" ;
276
-
277
- try
287
+
288
+ try
278
289
{
279
290
var response = await this . client . PostAsync ( new Uri ( string . Format ( GoogleDeleteUserUrl , this . authConfig . ApiKey ) ) , new StringContent ( content , Encoding . UTF8 , "application/json" ) ) . ConfigureAwait ( false ) ;
280
291
responseData = await response . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
281
-
292
+
282
293
response . EnsureSuccessStatusCode ( ) ;
283
294
}
284
- catch ( Exception ex )
295
+ catch ( Exception ex )
285
296
{
286
297
AuthErrorReason errorReason = GetFailureReason ( responseData ) ;
287
298
throw new FirebaseAuthException ( GoogleDeleteUserUrl , content , responseData , ex , errorReason ) ;
@@ -296,12 +307,12 @@ public async Task SendPasswordResetEmailAsync(string email)
296
307
{
297
308
var content = $ "{{\" requestType\" :\" PASSWORD_RESET\" ,\" email\" :\" { email } \" }}";
298
309
var responseData = "N/A" ;
299
-
310
+
300
311
try
301
312
{
302
313
var response = await this . client . PostAsync ( new Uri ( string . Format ( GoogleGetConfirmationCodeUrl , this . authConfig . ApiKey ) ) , new StringContent ( content , Encoding . UTF8 , "application/json" ) ) . ConfigureAwait ( false ) ;
303
314
responseData = await response . Content . ReadAsStringAsync ( ) . ConfigureAwait ( false ) ;
304
-
315
+
305
316
response . EnsureSuccessStatusCode ( ) ;
306
317
}
307
318
catch ( Exception ex )
@@ -550,6 +561,9 @@ private static AuthErrorReason GetFailureReason(string responseData)
550
561
case "A system error has occurred - missing or invalid postBody" :
551
562
failureReason = AuthErrorReason . SystemError ;
552
563
break ;
564
+ case "MISSING_OR_INVALID_NONCE : Duplicate credential received. Please try again with a new credential." :
565
+ failureReason = AuthErrorReason . DuplicateCredentialUse ;
566
+ break ;
553
567
554
568
//possible errors from Email/Password Account Signup (via signupNewUser or setAccountInfo) or Signin
555
569
case "INVALID_EMAIL" :
@@ -563,7 +577,7 @@ private static AuthErrorReason GetFailureReason(string responseData)
563
577
case "EMAIL_EXISTS" :
564
578
failureReason = AuthErrorReason . EmailExists ;
565
579
break ;
566
-
580
+
567
581
//possible errors from Account Delete
568
582
case "USER_NOT_FOUND" :
569
583
failureReason = AuthErrorReason . UserNotFound ;
@@ -610,12 +624,14 @@ private static AuthErrorReason GetFailureReason(string responseData)
610
624
break ;
611
625
}
612
626
613
- if ( failureReason == AuthErrorReason . Undefined )
614
- {
627
+ if ( failureReason == AuthErrorReason . Undefined )
628
+ {
615
629
//possible errors from Email/Password Account Signup (via signupNewUser or setAccountInfo)
616
- if ( errorData ? . error ? . message ? . StartsWith ( "WEAK_PASSWORD :" ) ?? false ) failureReason = AuthErrorReason . WeakPassword ;
630
+ if ( errorData ? . error ? . message ? . StartsWith ( "WEAK_PASSWORD :" ) ?? false ) failureReason = AuthErrorReason . WeakPassword ;
617
631
//possible errors from Email/Password Signin
618
632
else if ( errorData ? . error ? . message ? . StartsWith ( "TOO_MANY_ATTEMPTS_TRY_LATER :" ) ?? false ) failureReason = AuthErrorReason . TooManyAttemptsTryLater ;
633
+ //ID Token issued is stale to sign-in (e.g. with Apple)
634
+ else if ( errorData ? . error ? . message ? . StartsWith ( "ERROR_INVALID_CREDENTIAL" ) ?? false ) failureReason = AuthErrorReason . StaleIDToken ;
619
635
}
620
636
}
621
637
}
@@ -638,6 +654,7 @@ private string GetProviderId(FirebaseAuthType authType)
638
654
{
639
655
case FirebaseAuthType . Facebook :
640
656
case FirebaseAuthType . Google :
657
+ case FirebaseAuthType . Apple :
641
658
case FirebaseAuthType . Github :
642
659
case FirebaseAuthType . Twitter :
643
660
return authType . ToEnumString ( ) ;
0 commit comments