From dd8a3ca96c42dfc7e6d8b9768f7108241cc89129 Mon Sep 17 00:00:00 2001 From: Elies Lou Date: Mon, 3 Apr 2023 16:52:42 +0200 Subject: [PATCH 1/6] feat(oauth): added signIn and link for oauth --- .../auth/ReactNativeFirebaseAuthModule.java | 113 ++++++++++++++++- packages/auth/e2e/auth.e2e.js | 22 ++-- packages/auth/e2e/provider.e2e.js | 4 +- packages/auth/ios/RNFBAuth/RNFBAuthModule.m | 116 +++++++++++++++++- packages/auth/lib/User.js | 14 +-- packages/auth/lib/index.d.ts | 56 ++++++++- packages/auth/lib/index.js | 14 +-- packages/auth/lib/providers/OAuthProvider.js | 10 +- 8 files changed, 314 insertions(+), 35 deletions(-) diff --git a/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java b/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java index b6406f19a4..67ad03da26 100644 --- a/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java +++ b/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java @@ -30,6 +30,9 @@ import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.google.android.gms.tasks.OnCompleteListener; +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.OnSuccessListener; +import com.google.android.gms.tasks.Task; import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseException; import com.google.firebase.FirebaseNetworkException; @@ -56,6 +59,7 @@ import com.google.firebase.auth.MultiFactorResolver; import com.google.firebase.auth.MultiFactorSession; import com.google.firebase.auth.OAuthProvider; +import com.google.firebase.auth.OAuthCredential; import com.google.firebase.auth.PhoneAuthCredential; import com.google.firebase.auth.PhoneAuthOptions; import com.google.firebase.auth.PhoneAuthProvider; @@ -203,7 +207,6 @@ public void addIdTokenListener(final String appName) { FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); - if (!mIdTokenListeners.containsKey(appName)) { FirebaseAuth.IdTokenListener newIdTokenListener = firebaseAuth1 -> { @@ -862,6 +865,87 @@ private void signInWithCredential( } } + @ReactMethod + public void signInWithProvider(String appName, String providerId, @Nullable String email, Promise promise){ + OAuthProvider.Builder provider = OAuthProvider.newBuilder(providerId); + if(email != null){ + provider.addCustomParameter("login_hint", email); + } + Activity activity = getCurrentActivity(); + FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); + FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); + + OnSuccessListener onSuccess = new OnSuccessListener(){ + @Override + public void onSuccess(AuthResult authResult) { + Log.d(TAG, "signInWithProvider:onComplete:success"); + promiseWithAuthResult(authResult, promise); + } + }; + + OnFailureListener onFailure = new OnFailureListener(){ + @Override + public void onFailure(@NonNull Exception e) { + Log.w(TAG, "signInWithProvider:onComplete:failure", e); + promiseRejectAuthException(promise, e); + } + }; + + + Task pendingResultTask = firebaseAuth.getPendingAuthResult(); + if(pendingResultTask != null){ + pendingResultTask + .addOnSuccessListener(onSuccess) + .addOnFailureListener(onFailure); + } else { + firebaseAuth + .startActivityForSignInWithProvider(activity, provider.build()) + .addOnSuccessListener(onSuccess) + .addOnFailureListener(onFailure); + } + } + + @ReactMethod + public void linkWithProvider(String appName, String providerId, @Nullable String email, Promise promise){ + OAuthProvider.Builder provider = OAuthProvider.newBuilder(providerId); + if(email != null){ + provider.addCustomParameter("login_hint", email); + } + Activity activity = getCurrentActivity(); + FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); + FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); + FirebaseUser firebaseUser = firebaseAuth.getCurrentUser(); + + OnSuccessListener onSuccess = new OnSuccessListener(){ + @Override + public void onSuccess(AuthResult authResult) { + Log.d(TAG, "linkWithProvider:onComplete:success"); + promiseWithAuthResult(authResult, promise); + } + }; + + OnFailureListener onFailure = new OnFailureListener(){ + @Override + public void onFailure(@NonNull Exception e) { + Log.w(TAG, "linkInWithProvider:onComplete:failure", e); + promiseRejectAuthException(promise, e); + } + }; + + + Task pendingResultTask = firebaseAuth.getPendingAuthResult(); + if(pendingResultTask != null){ + pendingResultTask + .addOnSuccessListener(onSuccess) + .addOnFailureListener(onFailure); + } else { + firebaseUser + .startActivityForLinkWithProvider(activity, provider.build()) + .addOnSuccessListener(onSuccess) + .addOnFailureListener(onFailure); + } + } + /** * signInWithPhoneNumber * @@ -1889,6 +1973,30 @@ private void promiseWithAuthResult(AuthResult authResult, Promise promise) { WritableMap authResultMap = Arguments.createMap(); WritableMap userMap = firebaseUserToMap(authResult.getUser()); + if(authResult.getCredential() != null){ + if(authResult.getCredential() instanceof OAuthCredential){ + OAuthCredential creds = (OAuthCredential) authResult.getCredential(); + WritableMap credentialMap = Arguments.createMap(); + + credentialMap.putString("providerId", creds.getProvider()); + credentialMap.putString("signInMethod", creds.getSignInMethod()); + + if(creds.getIdToken() != null){ + credentialMap.putString("idToken", creds.getIdToken()); + } + + if(creds.getAccessToken() != null){ + credentialMap.putString("accessToken", creds.getAccessToken()); + } + + if(creds.getSecret() != null){ + credentialMap.putString("secret", creds.getSecret()); + } + + authResultMap.putMap("credential", credentialMap); + } + } + if (authResult.getAdditionalUserInfo() != null) { WritableMap additionalUserInfoMap = Arguments.createMap(); @@ -1929,6 +2037,7 @@ private void promiseWithAuthResult(AuthResult authResult, Promise promise) { */ private void promiseRejectAuthException(Promise promise, Exception exception) { WritableMap error = getJSError(exception); + final String sessionId = error.getString("sessionId"); final MultiFactorResolver multiFactorResolver = mCachedResolvers.get(sessionId); WritableMap resolverAsMap = Arguments.createMap(); @@ -1950,6 +2059,8 @@ private WritableMap getJSError(Exception exception) { String message = exception.getMessage(); String invalidEmail = "The email address is badly formatted."; + System.out.print(exception); + try { FirebaseAuthException authException = (FirebaseAuthException) exception; code = authException.getErrorCode(); diff --git a/packages/auth/e2e/auth.e2e.js b/packages/auth/e2e/auth.e2e.js index 5c89134e8a..602b62d32c 100644 --- a/packages/auth/e2e/auth.e2e.js +++ b/packages/auth/e2e/auth.e2e.js @@ -912,12 +912,11 @@ describe('auth()', function () { }); describe('signInWithPopup', function () { - it('should throw an unsupported error', function () { - (() => { - firebase.auth().signInWithPopup(); - }).should.throw( - 'firebase.auth().signInWithPopup() is unsupported by the native Firebase SDKs.', - ); + it('should trigger the oauth flow', async function () { + await (async () => { + const provider = new firebase.auth.OAuthProvider('oidc.react.com'); + await firebase.auth().signInWithPopup(provider); + }).should.not.throw(); }); }); @@ -1025,12 +1024,11 @@ describe('auth()', function () { }); describe('signInWithRedirect()', function () { - it('should throw an unsupported error', function () { - (() => { - firebase.auth().signInWithRedirect(); - }).should.throw( - 'firebase.auth().signInWithRedirect() is unsupported by the native Firebase SDKs.', - ); + it('should trigger the oauth flow', async function () { + await (async () => { + const provider = new firebase.auth.OAuthProvider('oidc.react.com'); + await firebase.auth().signInWithRedirect(provider); + }).should.not.throw(); }); }); diff --git a/packages/auth/e2e/provider.e2e.js b/packages/auth/e2e/provider.e2e.js index 1475eb5675..36fea1ea29 100644 --- a/packages/auth/e2e/provider.e2e.js +++ b/packages/auth/e2e/provider.e2e.js @@ -149,9 +149,7 @@ describe('auth() -> Providers', function () { describe('OAuthProvider', function () { describe('constructor', function () { it('should throw an unsupported error', function () { - (() => new firebase.auth.OAuthProvider()).should.throw( - '`new OAuthProvider()` is not supported on the native Firebase SDKs.', - ); + (() => new firebase.auth.OAuthProvider('oidc.react.com')).should.not.throw(); }); }); diff --git a/packages/auth/ios/RNFBAuth/RNFBAuthModule.m b/packages/auth/ios/RNFBAuth/RNFBAuthModule.m index 8f410ec6d9..9e2ed8c4b2 100644 --- a/packages/auth/ios/RNFBAuth/RNFBAuthModule.m +++ b/packages/auth/ios/RNFBAuth/RNFBAuthModule.m @@ -45,6 +45,10 @@ static NSString *const keyHandleCodeInApp = @"handleCodeInApp"; static NSString *const keyDynamicLinkDomain = @"dynamicLinkDomain"; static NSString *const keyAdditionalUserInfo = @"additionalUserInfo"; +static NSString *const keyCredential = @"credential"; +static NSString *const keyIdToken = @"idToken"; +static NSString *const keyAccessToken = @"accessToken"; +static NSString *const keySecret = @"secret"; static NSString *const AUTH_STATE_CHANGED_EVENT = @"auth_state_changed"; static NSString *const AUTH_ID_TOKEN_CHANGED_EVENT = @"auth_id_token_changed"; static NSString *const PHONE_AUTH_STATE_CHANGED_EVENT = @"phone_auth_state_changed"; @@ -56,6 +60,7 @@ static __strong NSMutableDictionary *credentials; static __strong NSMutableDictionary *cachedResolver; static __strong NSMutableDictionary *cachedSessions; +static __strong NSMutableDictionary *oAuthProviders; @implementation RNFBAuthModule #pragma mark - @@ -77,6 +82,7 @@ - (id)init { credentials = [[NSMutableDictionary alloc] init]; cachedResolver = [[NSMutableDictionary alloc] init]; cachedSessions = [[NSMutableDictionary alloc] init]; + oAuthProviders = [[NSMutableDictionary alloc] init]; }); return self; } @@ -104,6 +110,7 @@ - (void)invalidate { [credentials removeAllObjects]; [cachedResolver removeAllObjects]; [cachedSessions removeAllObjects]; + [oAuthProviders removeAllObjects]; } #pragma mark - @@ -571,6 +578,73 @@ - (void)invalidate { }]; } + +RCT_EXPORT_METHOD(signInWithProvider + : (FIRApp *)firebaseApp + : (NSString *)providerID + : (NSString *_Nullable)email + : (RCTPromiseResolveBlock)resolve + : (RCTPromiseRejectBlock)reject) { + FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:providerID]; + [oAuthProviders setValue:provider forKey:providerID]; + + if (email) { + [provider setCustomParameters:@{@"login_hint": email}]; + } + + [provider getCredentialWithUIDelegate:nil + completion:^(FIRAuthCredential *_Nullable credential, NSError *_Nullable error) { + if (error) { + [self promiseRejectAuthException:reject error:error]; + } + + if (credential) { + [[FIRAuth auth] signInWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) { + if (error) { + [self promiseRejectAuthException:reject error:error]; + } + else { + [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult credential:credential]; + } + }]; + } + }]; +} + +RCT_EXPORT_METHOD(linkWithProvider + : (FIRApp *)firebaseApp + : (NSString *)providerID + : (NSString *_Nullable)email + : (RCTPromiseResolveBlock)resolve + : (RCTPromiseRejectBlock)reject) { + FIROAuthProvider *provider = [FIROAuthProvider providerWithProviderID:providerID]; + [oAuthProviders setValue:provider forKey:providerID]; + + if (email) { + [provider setCustomParameters:@{@"login_hint": email}]; + } + + [provider getCredentialWithUIDelegate:nil + completion:^(FIRAuthCredential *_Nullable credential, NSError *_Nullable error) { + if (error) { + [self promiseRejectAuthException:reject error:error]; + } + + if (credential) { + [[FIRAuth auth].currentUser linkWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) { + if (error) { + [self promiseRejectAuthException:reject error:error]; + } + else { + [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult credential:credential]; + } + }]; + } + }]; +} + RCT_EXPORT_METHOD(confirmPasswordReset : (FIRApp *)firebaseApp : (NSString *)code @@ -1315,7 +1389,14 @@ - (void)promiseWithUser:(RCTPromiseResolveBlock)resolve - (void)promiseWithAuthResult:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject - authResult:(FIRAuthDataResult *)authResult { + authResult:(FIRAuthDataResult *)authResult { + [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult credential:nil]; +} + +- (void)promiseWithAuthResult:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject + authResult:(FIRAuthDataResult *)authResult + credential:(FIRAuthCredential *_Nullable)credential { if (authResult && authResult.user) { NSMutableDictionary *authResultDict = [NSMutableDictionary dictionary]; @@ -1347,6 +1428,39 @@ - (void)promiseWithAuthResult:(RCTPromiseResolveBlock)resolve [authResultDict setValue:[NSNull null] forKey:keyAdditionalUserInfo]; } + if (credential && ([credential isKindOfClass:[FIROAuthCredential class]])) { + NSMutableDictionary *credentialDict = [NSMutableDictionary dictionary]; + FIROAuthCredential* oAuthCredential = (FIROAuthCredential *)credential; + + [credentialDict setValue:oAuthCredential.provider forKey:keyProviderId]; + + if (oAuthCredential.IDToken) { + [credentialDict setValue:oAuthCredential.IDToken forKey:keyIdToken]; + } + else { + [credentialDict setValue:[NSNull null] forKey:keyIdToken]; + } + + if (oAuthCredential.accessToken) { + [credentialDict setValue:oAuthCredential.accessToken forKey:keyAccessToken]; + } + else { + [credentialDict setValue:[NSNull null] forKey:keyAccessToken]; + } + + if (oAuthCredential.accessToken) { + [credentialDict setValue:oAuthCredential.secret forKey:keySecret]; + } + else { + [credentialDict setValue:[NSNull null] forKey:keySecret]; + } + + [authResultDict setValue:credentialDict forKey:keyCredential]; + } + else { + [authResultDict setValue:[NSNull null] forKey:keyCredential]; + } + [authResultDict setValue:[self firebaseUserToDict:authResult.user] forKey:keyUser]; resolve(authResultDict); } else { diff --git a/packages/auth/lib/User.js b/packages/auth/lib/User.js index 8ac80ae49b..98a92a0118 100644 --- a/packages/auth/lib/User.js +++ b/packages/auth/lib/User.js @@ -310,16 +310,14 @@ export default class User { ); } - linkWithPopup() { - throw new Error( - 'firebase.auth.User.linkWithPopup() is unsupported by the native Firebase SDKs.', - ); + linkWithPopup(provider) { + return this._auth.native + .linkWithProvider(provider.providerId, provider.customParameters?.login_hint) + .then(userCredential => this._auth._setUserCredential(userCredential)); } - linkWithRedirect() { - throw new Error( - 'firebase.auth.User.linkWithRedirect() is unsupported by the native Firebase SDKs.', - ); + async linkWithRedirect(provider) { + return this.linkWithPopup(provider); } reauthenticateWithPhoneNumber() { diff --git a/packages/auth/lib/index.d.ts b/packages/auth/lib/index.d.ts index 6d5ff139a1..51eae8f7a7 100644 --- a/packages/auth/lib/index.d.ts +++ b/packages/auth/lib/index.d.ts @@ -109,6 +109,11 @@ export namespace FirebaseAuthTypes { credential: (token: string | null, secret?: string) => AuthCredential; } + export type OAuthProvider = AuthProvider & { + new (providerId: string): OAuthProvider; + setCustomParameters: (customOAuthParameters: object) => void; + }; + /** * Interface that represents an Open ID Connect auth provider. Implemented by other providers. */ @@ -315,7 +320,7 @@ export namespace FirebaseAuthTypes { * firebase.auth.OAuthProvider; * ``` */ - OAuthProvider: AuthProvider; + OAuthProvider: OAuthProvider; /** * Custom Open ID connect auth provider implementation. * @@ -387,6 +392,12 @@ export namespace FirebaseAuthTypes { * Any additional user information assigned to the user. */ additionalUserInfo?: AdditionalUserInfo; + + /** + * The AuthCredential returned from the identity provider. + */ + credential: AuthCredential | null; + /** * Returns the {@link auth.User} interface of this credential. */ @@ -1212,6 +1223,46 @@ export namespace FirebaseAuthTypes { */ linkWithCredential(credential: AuthCredential): Promise; + /** + * Link the user with a 3rd party provider. + * + * #### Example + * + * ```js + * const oauthProvider = new firebase.auth.OAuthProvider('oidc.react.com') + * const authCredentials = await firebase.auth().currentUser.linkWithPopup(oauthProvider); + * ``` + * + * @error auth/provider-already-linked Thrown if the provider has already been linked to the user. This error is thrown even if this is not the same provider's account that is currently linked to the user. + * @error auth/invalid-credential Thrown if the provider's credential is not valid. This can happen if it has already expired when calling link, or if it used invalid token(s). See the Firebase documentation for your provider, and make sure you pass in the correct parameters to the credential method. + * @error auth/credential-already-in-use Thrown if the account corresponding to the credential already exists among your users, or is already linked to a Firebase User. + * @error auth/email-already-in-use Thrown if the email corresponding to the credential already exists among your users. + * @error auth/operation-not-allowed Thrown if you have not enabled the provider in the Firebase Console. Go to the Firebase Console for your project, in the Auth section and the Sign in Method tab and configure the provider. + * @throws on iOS {@link auth.NativeFirebaseAuthError}, on Android {@link auth.NativeFirebaseError} + * @param provider A created {@link auth.AuthProvider}. + */ + linkWithPopup(provider: AuthProvider): Promise; + + /** + * Link the user with a 3rd party provider. + * + * #### Example + * + * ```js + * const oauthProvider = new firebase.auth.OAuthProvider('oidc.react.com') + * const authCredentials = await firebase.auth().currentUser.linkWithPopup(oauthProvider); + * ``` + * + * @error auth/provider-already-linked Thrown if the provider has already been linked to the user. This error is thrown even if this is not the same provider's account that is currently linked to the user. + * @error auth/invalid-credential Thrown if the provider's credential is not valid. This can happen if it has already expired when calling link, or if it used invalid token(s). See the Firebase documentation for your provider, and make sure you pass in the correct parameters to the credential method. + * @error auth/credential-already-in-use Thrown if the account corresponding to the credential already exists among your users, or is already linked to a Firebase User. + * @error auth/email-already-in-use Thrown if the email corresponding to the credential already exists among your users. + * @error auth/operation-not-allowed Thrown if you have not enabled the provider in the Firebase Console. Go to the Firebase Console for your project, in the Auth section and the Sign in Method tab and configure the provider. + * @throws on iOS {@link auth.NativeFirebaseAuthError}, on Android {@link auth.NativeFirebaseError} + * @param provider A created {@link auth.AuthProvider}. + */ + linkWithRedirect(provider: Provider): Promise; + /** * Re-authenticate a user with a third-party authentication provider. * @@ -1722,6 +1773,9 @@ export namespace FirebaseAuthTypes { */ signInWithCredential(credential: AuthCredential): Promise; + signInWithPopup(provider: AuthProvider): Promise; + signInWithRedirect(provider: AuthProvider): Promise; + /** * Sends a password reset email to the given email address. * Unlike the web SDK, the email will contain a password reset link rather than a code. diff --git a/packages/auth/lib/index.js b/packages/auth/lib/index.js index fe6af99339..fd083553c2 100644 --- a/packages/auth/lib/index.js +++ b/packages/auth/lib/index.js @@ -360,16 +360,16 @@ class FirebaseAuthModule extends FirebaseModule { throw new Error('firebase.auth().setPersistence() is unsupported by the native Firebase SDKs.'); } - signInWithPopup() { - throw new Error( - 'firebase.auth().signInWithPopup() is unsupported by the native Firebase SDKs.', - ); + signInWithPopup(provider) { + return this.native + .signInWithProvider(provider.providerId, provider.customParameters?.login_hint) + .then(userCredential => this._setUserCredential(userCredential)); } signInWithRedirect() { - throw new Error( - 'firebase.auth().signInWithRedirect() is unsupported by the native Firebase SDKs.', - ); + return this.native + .signInWithProvider(provider.providerId, provider.customParameters?.login_hint) + .then(userCredential => this._setUserCredential(userCredential)); } // firebase issue - https://github.com/invertase/react-native-firebase/pull/655#issuecomment-349904680 diff --git a/packages/auth/lib/providers/OAuthProvider.js b/packages/auth/lib/providers/OAuthProvider.js index 7ef56c624c..837fbf338a 100644 --- a/packages/auth/lib/providers/OAuthProvider.js +++ b/packages/auth/lib/providers/OAuthProvider.js @@ -18,8 +18,14 @@ const providerId = 'oauth'; export default class OAuthProvider { - constructor() { - throw new Error('`new OAuthProvider()` is not supported on the native Firebase SDKs.'); + constructor(providerId) { + this.providerId = providerId; + } + + customParameters = {}; + + setCustomParameters(customParameters) { + this.customParameters = customParameters; } static get PROVIDER_ID() { From c7b3a3723f821fbf8f4d9ff1b6da1178619deb4d Mon Sep 17 00:00:00 2001 From: Cedric Date: Tue, 18 Apr 2023 17:17:46 +0200 Subject: [PATCH 2/6] ios: on AuthError, send email in userInfo --- packages/auth/ios/RNFBAuth/RNFBAuthModule.m | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/auth/ios/RNFBAuth/RNFBAuthModule.m b/packages/auth/ios/RNFBAuth/RNFBAuthModule.m index 9e2ed8c4b2..d7b9b2dcdd 100644 --- a/packages/auth/ios/RNFBAuth/RNFBAuthModule.m +++ b/packages/auth/ios/RNFBAuth/RNFBAuthModule.m @@ -1268,7 +1268,8 @@ - (void)promiseRejectAuthException:(RCTPromiseRejectBlock)reject error:(NSError @"message" : [jsError valueForKey:@"message"], @"nativeErrorMessage" : [jsError valueForKey:@"nativeErrorMessage"], @"authCredential" : [jsError valueForKey:@"authCredential"], - @"resolver" : [jsError valueForKey:@"resolver"] + @"resolver" : [jsError valueForKey:@"resolver"], + @"email" : [jsError valueForKey:@"email"] }]; } @@ -1366,13 +1367,16 @@ - (NSDictionary *)getJSError:(NSError *)error { NSString *sessionKey = [NSString stringWithFormat:@"%@", @([resolver.session hash])]; cachedResolver[sessionKey] = resolver; } + + NSString *email = [error userInfo][FIRAuthErrorUserInfoEmailKey]; return @{ @"code" : code, @"message" : message, @"nativeErrorMessage" : nativeErrorMessage, @"authCredential" : authCredentialDict != nil ? (id)authCredentialDict : [NSNull null], - @"resolver" : resolverDict != nil ? (id)resolverDict : [NSNull null] + @"resolver" : resolverDict != nil ? (id)resolverDict : [NSNull null], + @"email": email != nil ? email : [NSNull null] }; } From 4d35fb3944e96d8507965f7264a7a222338671ed Mon Sep 17 00:00:00 2001 From: Cedric Date: Wed, 26 Apr 2023 22:51:08 +0200 Subject: [PATCH 3/6] use prompt select_account --- packages/auth/ios/RNFBAuth/RNFBAuthModule.m | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/auth/ios/RNFBAuth/RNFBAuthModule.m b/packages/auth/ios/RNFBAuth/RNFBAuthModule.m index d7b9b2dcdd..2234801005 100644 --- a/packages/auth/ios/RNFBAuth/RNFBAuthModule.m +++ b/packages/auth/ios/RNFBAuth/RNFBAuthModule.m @@ -589,7 +589,10 @@ - (void)invalidate { [oAuthProviders setValue:provider forKey:providerID]; if (email) { - [provider setCustomParameters:@{@"login_hint": email}]; + [provider setCustomParameters:@{@"login_hint": email, @"prompt": @"select_account"}]; + } + else { + [provider setCustomParameters:@{@"prompt": @"select_account"}]; } [provider getCredentialWithUIDelegate:nil @@ -622,7 +625,10 @@ - (void)invalidate { [oAuthProviders setValue:provider forKey:providerID]; if (email) { - [provider setCustomParameters:@{@"login_hint": email}]; + [provider setCustomParameters:@{@"login_hint": email, @"prompt": @"select_account"}]; + } + else { + [provider setCustomParameters:@{@"prompt": @"select_account"}]; } [provider getCredentialWithUIDelegate:nil @@ -1367,7 +1373,7 @@ - (NSDictionary *)getJSError:(NSError *)error { NSString *sessionKey = [NSString stringWithFormat:@"%@", @([resolver.session hash])]; cachedResolver[sessionKey] = resolver; } - + NSString *email = [error userInfo][FIRAuthErrorUserInfoEmailKey]; return @{ From 21d7c184b03d87098355d189bb495240d2b19e4e Mon Sep 17 00:00:00 2001 From: Cedric Date: Wed, 26 Apr 2023 22:52:02 +0200 Subject: [PATCH 4/6] handle email error on android --- .../common/ReactNativeFirebaseModule.java | 9 ++++++ packages/app/lib/index.d.ts | 5 ++++ .../app/lib/internal/NativeFirebaseError.js | 9 ++++++ .../auth/ReactNativeFirebaseAuthModule.java | 30 +++++++++++++++---- 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/packages/app/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseModule.java b/packages/app/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseModule.java index 353a4bf4ad..91e4e41e49 100644 --- a/packages/app/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseModule.java +++ b/packages/app/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseModule.java @@ -54,6 +54,15 @@ public static void rejectPromiseWithCodeAndMessage( promise.reject(code, message, userInfoMap); } + public static void rejectPromiseWithMap( + Promise promise, String code, String message, ReadableMap map) { + WritableMap userInfoMap = Arguments.createMap(); + userInfoMap.putString("code", code); + userInfoMap.putString("message", message); + userInfoMap.merge(map); + promise.reject(code, message, userInfoMap); + } + public static void rejectPromiseWithCodeAndMessage(Promise promise, String code, String message) { WritableMap userInfoMap = Arguments.createMap(); userInfoMap.putString("code", code); diff --git a/packages/app/lib/index.d.ts b/packages/app/lib/index.d.ts index 26913309c2..b9553ad9ab 100644 --- a/packages/app/lib/index.d.ts +++ b/packages/app/lib/index.d.ts @@ -42,6 +42,11 @@ export namespace ReactNativeFirebase { */ readonly message: string; + /** + * The email address of the user's account used in the operation that triggered the error, if applicable + */ + readonly email?: string; + /** * The firebase module namespace that this error originated from, e.g. 'analytics' */ diff --git a/packages/app/lib/internal/NativeFirebaseError.js b/packages/app/lib/internal/NativeFirebaseError.js index b458e51739..da465f56a2 100644 --- a/packages/app/lib/internal/NativeFirebaseError.js +++ b/packages/app/lib/internal/NativeFirebaseError.js @@ -39,6 +39,15 @@ export default class NativeFirebaseError extends Error { value: `[${this.code}] ${userInfo.message || nativeError.message}`, }); + if (typeof userInfo === 'object' && userInfo !== null) { + if ('email' in userInfo) { + Object.defineProperty(this, 'email', { + enumerable: true, + value: userInfo.email, + }); + } + } + Object.defineProperty(this, 'jsStack', { enumerable: false, value: jsStack, diff --git a/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java b/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java index 67ad03da26..1c0ea2c2dd 100644 --- a/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java +++ b/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java @@ -46,6 +46,7 @@ import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseAuthException; import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException; +import com.google.firebase.auth.FirebaseAuthUserCollisionException; import com.google.firebase.auth.FirebaseAuthMultiFactorException; import com.google.firebase.auth.FirebaseAuthProvider; import com.google.firebase.auth.FirebaseAuthSettings; @@ -868,9 +869,12 @@ private void signInWithCredential( @ReactMethod public void signInWithProvider(String appName, String providerId, @Nullable String email, Promise promise){ OAuthProvider.Builder provider = OAuthProvider.newBuilder(providerId); + if(email != null){ provider.addCustomParameter("login_hint", email); } + provider.addCustomParameter("prompt", "select_account"); + Activity activity = getCurrentActivity(); FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); @@ -908,9 +912,12 @@ public void onFailure(@NonNull Exception e) { @ReactMethod public void linkWithProvider(String appName, String providerId, @Nullable String email, Promise promise){ OAuthProvider.Builder provider = OAuthProvider.newBuilder(providerId); + if(email != null){ provider.addCustomParameter("login_hint", email); } + provider.addCustomParameter("prompt", "select_account"); + Activity activity = getCurrentActivity(); FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); @@ -1977,7 +1984,7 @@ private void promiseWithAuthResult(AuthResult authResult, Promise promise) { if(authResult.getCredential() instanceof OAuthCredential){ OAuthCredential creds = (OAuthCredential) authResult.getCredential(); WritableMap credentialMap = Arguments.createMap(); - + credentialMap.putString("providerId", creds.getProvider()); credentialMap.putString("signInMethod", creds.getSignInMethod()); @@ -2037,15 +2044,23 @@ private void promiseWithAuthResult(AuthResult authResult, Promise promise) { */ private void promiseRejectAuthException(Promise promise, Exception exception) { WritableMap error = getJSError(exception); - + final String sessionId = error.getString("sessionId"); final MultiFactorResolver multiFactorResolver = mCachedResolvers.get(sessionId); WritableMap resolverAsMap = Arguments.createMap(); + + WritableMap map = Arguments.createMap(); if (multiFactorResolver != null) { resolverAsMap = resolverToMap(sessionId, multiFactorResolver); + map.putMap("resolver", resolverAsMap); } - rejectPromiseWithCodeAndMessage( - promise, error.getString("code"), error.getString("message"), resolverAsMap); + + if(error.getString("email") != null){ + map.putString("email", error.getString("email")); + } + + rejectPromiseWithMap( + promise, error.getString("code"), error.getString("message"), map); } /** @@ -2060,7 +2075,6 @@ private WritableMap getJSError(Exception exception) { String invalidEmail = "The email address is badly formatted."; System.out.print(exception); - try { FirebaseAuthException authException = (FirebaseAuthException) exception; code = authException.getErrorCode(); @@ -2096,7 +2110,7 @@ private WritableMap getJSError(Exception exception) { + " before retrying this request."; break; case "ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL": - message = + message = "An account already exists with the same email address but different sign-in" + " credentials. Sign in using a provider associated with this email address."; break; @@ -2134,6 +2148,10 @@ private WritableMap getJSError(Exception exception) { } } + if(exception instanceof FirebaseAuthUserCollisionException) { + error.putString("email", ((FirebaseAuthUserCollisionException) exception).getEmail()); + } + if (exception instanceof FirebaseAuthMultiFactorException) { final FirebaseAuthMultiFactorException multiFactorException = (FirebaseAuthMultiFactorException) exception; From 1710fde57b4c3796bbc4e11ee43408feb9cd9e99 Mon Sep 17 00:00:00 2001 From: Elies Lou Date: Wed, 3 May 2023 10:26:25 +0200 Subject: [PATCH 5/6] fix(auth): signInWithRedirect uses provider --- packages/auth/lib/index.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/auth/lib/index.js b/packages/auth/lib/index.js index fd083553c2..bf4b825aae 100644 --- a/packages/auth/lib/index.js +++ b/packages/auth/lib/index.js @@ -366,10 +366,8 @@ class FirebaseAuthModule extends FirebaseModule { .then(userCredential => this._setUserCredential(userCredential)); } - signInWithRedirect() { - return this.native - .signInWithProvider(provider.providerId, provider.customParameters?.login_hint) - .then(userCredential => this._setUserCredential(userCredential)); + signInWithRedirect(provider) { + return this.signInWithPopup(provider); } // firebase issue - https://github.com/invertase/react-native-firebase/pull/655#issuecomment-349904680 From 20fbbd58ee0a5acbfbaa8e12c89a0ddd9e09ec76 Mon Sep 17 00:00:00 2001 From: Mike Hardy Date: Thu, 29 Jun 2023 16:28:44 -0500 Subject: [PATCH 6/6] style(lint): `yarn lint:ios:fix && yarn lint:android` --- .../auth/ReactNativeFirebaseAuthModule.java | 119 +++++++++--------- packages/auth/ios/RNFBAuth/RNFBAuthModule.m | 109 ++++++++-------- 2 files changed, 116 insertions(+), 112 deletions(-) diff --git a/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java b/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java index 1c0ea2c2dd..257e72b379 100644 --- a/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java +++ b/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java @@ -46,10 +46,10 @@ import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseAuthException; import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException; -import com.google.firebase.auth.FirebaseAuthUserCollisionException; import com.google.firebase.auth.FirebaseAuthMultiFactorException; import com.google.firebase.auth.FirebaseAuthProvider; import com.google.firebase.auth.FirebaseAuthSettings; +import com.google.firebase.auth.FirebaseAuthUserCollisionException; import com.google.firebase.auth.FirebaseUser; import com.google.firebase.auth.FirebaseUserMetadata; import com.google.firebase.auth.GetTokenResult; @@ -59,8 +59,8 @@ import com.google.firebase.auth.MultiFactorInfo; import com.google.firebase.auth.MultiFactorResolver; import com.google.firebase.auth.MultiFactorSession; -import com.google.firebase.auth.OAuthProvider; import com.google.firebase.auth.OAuthCredential; +import com.google.firebase.auth.OAuthProvider; import com.google.firebase.auth.PhoneAuthCredential; import com.google.firebase.auth.PhoneAuthOptions; import com.google.firebase.auth.PhoneAuthProvider; @@ -867,10 +867,11 @@ private void signInWithCredential( } @ReactMethod - public void signInWithProvider(String appName, String providerId, @Nullable String email, Promise promise){ + public void signInWithProvider( + String appName, String providerId, @Nullable String email, Promise promise) { OAuthProvider.Builder provider = OAuthProvider.newBuilder(providerId); - if(email != null){ + if (email != null) { provider.addCustomParameter("login_hint", email); } provider.addCustomParameter("prompt", "select_account"); @@ -879,41 +880,41 @@ public void signInWithProvider(String appName, String providerId, @Nullable Stri FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); - OnSuccessListener onSuccess = new OnSuccessListener(){ - @Override - public void onSuccess(AuthResult authResult) { - Log.d(TAG, "signInWithProvider:onComplete:success"); - promiseWithAuthResult(authResult, promise); - } - }; - - OnFailureListener onFailure = new OnFailureListener(){ - @Override - public void onFailure(@NonNull Exception e) { - Log.w(TAG, "signInWithProvider:onComplete:failure", e); - promiseRejectAuthException(promise, e); - } - }; + OnSuccessListener onSuccess = + new OnSuccessListener() { + @Override + public void onSuccess(AuthResult authResult) { + Log.d(TAG, "signInWithProvider:onComplete:success"); + promiseWithAuthResult(authResult, promise); + } + }; + OnFailureListener onFailure = + new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception e) { + Log.w(TAG, "signInWithProvider:onComplete:failure", e); + promiseRejectAuthException(promise, e); + } + }; Task pendingResultTask = firebaseAuth.getPendingAuthResult(); - if(pendingResultTask != null){ - pendingResultTask - .addOnSuccessListener(onSuccess) - .addOnFailureListener(onFailure); + if (pendingResultTask != null) { + pendingResultTask.addOnSuccessListener(onSuccess).addOnFailureListener(onFailure); } else { firebaseAuth - .startActivityForSignInWithProvider(activity, provider.build()) - .addOnSuccessListener(onSuccess) - .addOnFailureListener(onFailure); + .startActivityForSignInWithProvider(activity, provider.build()) + .addOnSuccessListener(onSuccess) + .addOnFailureListener(onFailure); } } @ReactMethod - public void linkWithProvider(String appName, String providerId, @Nullable String email, Promise promise){ + public void linkWithProvider( + String appName, String providerId, @Nullable String email, Promise promise) { OAuthProvider.Builder provider = OAuthProvider.newBuilder(providerId); - if(email != null){ + if (email != null) { provider.addCustomParameter("login_hint", email); } provider.addCustomParameter("prompt", "select_account"); @@ -923,33 +924,32 @@ public void linkWithProvider(String appName, String providerId, @Nullable String FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); FirebaseUser firebaseUser = firebaseAuth.getCurrentUser(); - OnSuccessListener onSuccess = new OnSuccessListener(){ - @Override - public void onSuccess(AuthResult authResult) { - Log.d(TAG, "linkWithProvider:onComplete:success"); - promiseWithAuthResult(authResult, promise); - } - }; - - OnFailureListener onFailure = new OnFailureListener(){ - @Override - public void onFailure(@NonNull Exception e) { - Log.w(TAG, "linkInWithProvider:onComplete:failure", e); - promiseRejectAuthException(promise, e); - } - }; + OnSuccessListener onSuccess = + new OnSuccessListener() { + @Override + public void onSuccess(AuthResult authResult) { + Log.d(TAG, "linkWithProvider:onComplete:success"); + promiseWithAuthResult(authResult, promise); + } + }; + OnFailureListener onFailure = + new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception e) { + Log.w(TAG, "linkInWithProvider:onComplete:failure", e); + promiseRejectAuthException(promise, e); + } + }; Task pendingResultTask = firebaseAuth.getPendingAuthResult(); - if(pendingResultTask != null){ - pendingResultTask - .addOnSuccessListener(onSuccess) - .addOnFailureListener(onFailure); + if (pendingResultTask != null) { + pendingResultTask.addOnSuccessListener(onSuccess).addOnFailureListener(onFailure); } else { firebaseUser - .startActivityForLinkWithProvider(activity, provider.build()) - .addOnSuccessListener(onSuccess) - .addOnFailureListener(onFailure); + .startActivityForLinkWithProvider(activity, provider.build()) + .addOnSuccessListener(onSuccess) + .addOnFailureListener(onFailure); } } @@ -1980,23 +1980,23 @@ private void promiseWithAuthResult(AuthResult authResult, Promise promise) { WritableMap authResultMap = Arguments.createMap(); WritableMap userMap = firebaseUserToMap(authResult.getUser()); - if(authResult.getCredential() != null){ - if(authResult.getCredential() instanceof OAuthCredential){ + if (authResult.getCredential() != null) { + if (authResult.getCredential() instanceof OAuthCredential) { OAuthCredential creds = (OAuthCredential) authResult.getCredential(); WritableMap credentialMap = Arguments.createMap(); credentialMap.putString("providerId", creds.getProvider()); credentialMap.putString("signInMethod", creds.getSignInMethod()); - if(creds.getIdToken() != null){ + if (creds.getIdToken() != null) { credentialMap.putString("idToken", creds.getIdToken()); } - if(creds.getAccessToken() != null){ + if (creds.getAccessToken() != null) { credentialMap.putString("accessToken", creds.getAccessToken()); } - if(creds.getSecret() != null){ + if (creds.getSecret() != null) { credentialMap.putString("secret", creds.getSecret()); } @@ -2055,12 +2055,11 @@ private void promiseRejectAuthException(Promise promise, Exception exception) { map.putMap("resolver", resolverAsMap); } - if(error.getString("email") != null){ + if (error.getString("email") != null) { map.putString("email", error.getString("email")); } - rejectPromiseWithMap( - promise, error.getString("code"), error.getString("message"), map); + rejectPromiseWithMap(promise, error.getString("code"), error.getString("message"), map); } /** @@ -2110,7 +2109,7 @@ private WritableMap getJSError(Exception exception) { + " before retrying this request."; break; case "ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL": - message = + message = "An account already exists with the same email address but different sign-in" + " credentials. Sign in using a provider associated with this email address."; break; @@ -2148,7 +2147,7 @@ private WritableMap getJSError(Exception exception) { } } - if(exception instanceof FirebaseAuthUserCollisionException) { + if (exception instanceof FirebaseAuthUserCollisionException) { error.putString("email", ((FirebaseAuthUserCollisionException) exception).getEmail()); } diff --git a/packages/auth/ios/RNFBAuth/RNFBAuthModule.m b/packages/auth/ios/RNFBAuth/RNFBAuthModule.m index 2234801005..c8d812b116 100644 --- a/packages/auth/ios/RNFBAuth/RNFBAuthModule.m +++ b/packages/auth/ios/RNFBAuth/RNFBAuthModule.m @@ -578,7 +578,6 @@ - (void)invalidate { }]; } - RCT_EXPORT_METHOD(signInWithProvider : (FIRApp *)firebaseApp : (NSString *)providerID @@ -589,30 +588,35 @@ - (void)invalidate { [oAuthProviders setValue:provider forKey:providerID]; if (email) { - [provider setCustomParameters:@{@"login_hint": email, @"prompt": @"select_account"}]; - } - else { - [provider setCustomParameters:@{@"prompt": @"select_account"}]; + [provider setCustomParameters:@{@"login_hint" : email, @"prompt" : @"select_account"}]; + } else { + [provider setCustomParameters:@{@"prompt" : @"select_account"}]; } [provider getCredentialWithUIDelegate:nil - completion:^(FIRAuthCredential *_Nullable credential, NSError *_Nullable error) { - if (error) { - [self promiseRejectAuthException:reject error:error]; - } + completion:^(FIRAuthCredential *_Nullable credential, + NSError *_Nullable error) { + if (error) { + [self promiseRejectAuthException:reject error:error]; + } - if (credential) { - [[FIRAuth auth] signInWithCredential:credential - completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) { - if (error) { - [self promiseRejectAuthException:reject error:error]; - } - else { - [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult credential:credential]; - } - }]; - } - }]; + if (credential) { + [[FIRAuth auth] + signInWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + if (error) { + [self promiseRejectAuthException:reject + error:error]; + } else { + [self promiseWithAuthResult:resolve + rejecter:reject + authResult:authResult + credential:credential]; + } + }]; + } + }]; } RCT_EXPORT_METHOD(linkWithProvider @@ -625,30 +629,35 @@ - (void)invalidate { [oAuthProviders setValue:provider forKey:providerID]; if (email) { - [provider setCustomParameters:@{@"login_hint": email, @"prompt": @"select_account"}]; - } - else { - [provider setCustomParameters:@{@"prompt": @"select_account"}]; + [provider setCustomParameters:@{@"login_hint" : email, @"prompt" : @"select_account"}]; + } else { + [provider setCustomParameters:@{@"prompt" : @"select_account"}]; } [provider getCredentialWithUIDelegate:nil - completion:^(FIRAuthCredential *_Nullable credential, NSError *_Nullable error) { - if (error) { - [self promiseRejectAuthException:reject error:error]; - } + completion:^(FIRAuthCredential *_Nullable credential, + NSError *_Nullable error) { + if (error) { + [self promiseRejectAuthException:reject error:error]; + } - if (credential) { - [[FIRAuth auth].currentUser linkWithCredential:credential - completion:^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) { - if (error) { - [self promiseRejectAuthException:reject error:error]; - } - else { - [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult credential:credential]; - } - }]; - } - }]; + if (credential) { + [[FIRAuth auth].currentUser + linkWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + if (error) { + [self promiseRejectAuthException:reject + error:error]; + } else { + [self promiseWithAuthResult:resolve + rejecter:reject + authResult:authResult + credential:credential]; + } + }]; + } + }]; } RCT_EXPORT_METHOD(confirmPasswordReset @@ -1382,7 +1391,7 @@ - (NSDictionary *)getJSError:(NSError *)error { @"nativeErrorMessage" : nativeErrorMessage, @"authCredential" : authCredentialDict != nil ? (id)authCredentialDict : [NSNull null], @"resolver" : resolverDict != nil ? (id)resolverDict : [NSNull null], - @"email": email != nil ? email : [NSNull null] + @"email" : email != nil ? email : [NSNull null] }; } @@ -1399,7 +1408,7 @@ - (void)promiseWithUser:(RCTPromiseResolveBlock)resolve - (void)promiseWithAuthResult:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject - authResult:(FIRAuthDataResult *)authResult { + authResult:(FIRAuthDataResult *)authResult { [self promiseWithAuthResult:resolve rejecter:reject authResult:authResult credential:nil]; } @@ -1440,34 +1449,30 @@ - (void)promiseWithAuthResult:(RCTPromiseResolveBlock)resolve if (credential && ([credential isKindOfClass:[FIROAuthCredential class]])) { NSMutableDictionary *credentialDict = [NSMutableDictionary dictionary]; - FIROAuthCredential* oAuthCredential = (FIROAuthCredential *)credential; + FIROAuthCredential *oAuthCredential = (FIROAuthCredential *)credential; [credentialDict setValue:oAuthCredential.provider forKey:keyProviderId]; if (oAuthCredential.IDToken) { [credentialDict setValue:oAuthCredential.IDToken forKey:keyIdToken]; - } - else { + } else { [credentialDict setValue:[NSNull null] forKey:keyIdToken]; } if (oAuthCredential.accessToken) { [credentialDict setValue:oAuthCredential.accessToken forKey:keyAccessToken]; - } - else { + } else { [credentialDict setValue:[NSNull null] forKey:keyAccessToken]; } if (oAuthCredential.accessToken) { [credentialDict setValue:oAuthCredential.secret forKey:keySecret]; - } - else { + } else { [credentialDict setValue:[NSNull null] forKey:keySecret]; } [authResultDict setValue:credentialDict forKey:keyCredential]; - } - else { + } else { [authResultDict setValue:[NSNull null] forKey:keyCredential]; }