Skip to content
6 changes: 6 additions & 0 deletions FirebaseAuthUI/FUIAuth.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,12 @@ __attribute__((deprecated("Instead use authUI:didSignInWithAuthDataResult:error:
*/
@property(nonatomic, copy, nullable) NSURL *TOSURL;

/** @property shouldAutoUpgradeAnonymousUsers
@brief Whether to enable auto upgrading of anonymous accounts, defaults to NO.
*/
@property(nonatomic, assign, getter=shouldAutoUpgradeAnonymousUsers) BOOL
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the getter is needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a public API, the developer may want to condition on whether shouldAutoUpgrade is turned on or not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@protocol86 I mean the property name and the getter name are identical (shouldAutoUpgradeAnonymousUsers). why getter=?

shouldAutoUpgradeAnonymousUsers;

/** @property privacyPolicyURL
@brief The URL of your app's Privacy Policy. If not nil, a privacy policy notice is
displayed on the initial sign-in screen and potentially the phone number auth and
Expand Down
311 changes: 271 additions & 40 deletions FirebaseAuthUI/FUIAuth.m

Large diffs are not rendered by default.

45 changes: 43 additions & 2 deletions FirebaseAuthUI/FUIAuthBaseViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -242,20 +242,61 @@ + (void)showAlertWithTitle:(nullable NSString *)title
[presentingViewController presentViewController:alertController animated:YES completion:nil];
}

+ (void)showAlertWithTitle:(nullable NSString *)title
message:(NSString *)message
actionTitle:(NSString *)actionTitle
presentingViewController:(UIViewController *)presentingViewController
actionHandler:(FUIAuthAlertActionHandler)actionHandler
cancelHandler:(FUIAuthAlertActionHandler)cancelHandler {
UIAlertController *alertController =
[UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction =
[UIAlertAction actionWithTitle:actionTitle
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
actionHandler();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand those handler are with nonnull type. but is it a good practice to check null before calling them? same to other handler calls.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ooh, missed this. Will be addressed in a follow up PR.

}];
[alertController addAction:okAction];
UIAlertAction *cancelAction =
[UIAlertAction actionWithTitle:FUILocalizedString(kStr_Cancel)
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * _Nonnull action) {
cancelHandler();
}];
[alertController addAction:cancelAction];
[presentingViewController presentViewController:alertController animated:YES completion:nil];
}

+ (void)showSignInAlertWithEmail:(NSString *)email
provider:(id<FUIAuthProvider>)provider
presentingViewController:(UIViewController *)presentingViewController
signinHandler:(FUIAuthAlertActionHandler)signinHandler
cancelHandler:(FUIAuthAlertActionHandler)cancelHandler {
[self showSignInAlertWithEmail:email
providerShortName:provider.shortName
providerSignInLabel:provider.signInLabel
presentingViewController:presentingViewController
signinHandler:signinHandler
cancelHandler:cancelHandler];
}

+ (void)showSignInAlertWithEmail:(NSString *)email
providerShortName:(NSString *)providerShortName
providerSignInLabel:(NSString *)providerSignInLabel
presentingViewController:(UIViewController *)presentingViewController
signinHandler:(FUIAuthAlertActionHandler)signinHandler
cancelHandler:(FUIAuthAlertActionHandler)cancelHandler {
NSString *message =
[NSString stringWithFormat:FUILocalizedString(kStr_ProviderUsedPreviouslyMessage),
email, provider.shortName];
email, providerShortName];
UIAlertController *alertController =
[UIAlertController alertControllerWithTitle:FUILocalizedString(kStr_ExistingAccountTitle)
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *signInAction =
[UIAlertAction actionWithTitle:provider.signInLabel
[UIAlertAction actionWithTitle:providerSignInLabel
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
signinHandler();
Expand Down
45 changes: 45 additions & 0 deletions FirebaseAuthUI/FUIAuthBaseViewController_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,51 @@ NS_ASSUME_NONNULL_BEGIN
actionTitle:(NSString *)actionTitle
presentingViewController:(UIViewController *)presentingViewController;

/** @fn showAlertWithTitle:message:actionTitle:presentingViewController:
@brief Displays an alert view with given title, message and action title on top of the
specified view controller.
@param title The title of the alert.
@param message The message of the alert.
@param actionTitle The title of the action button.
@param actionHandler The block to execute if the action button is tapped.
@param cancelHandler The block to execute if the cancel button is tapped.
@param presentingViewController The controller which shows alert.
*/
+ (void)showAlertWithTitle:(nullable NSString *)title
message:(NSString *)message
actionTitle:(NSString *)actionTitle
presentingViewController:(UIViewController *)presentingViewController
actionHandler:(FUIAuthAlertActionHandler)actionHandler
cancelHandler:(FUIAuthAlertActionHandler)cancelHandler;

/** @fn showSignInAlertWithEmail:provider:handler:
@brief Displays an alert to conform with user whether she wants to proceed with the provider.
@param email The email address to sign in with.
@param provider The identity provider to sign in with.
@param signinHandler Handler for the sign in action of the alert.
@param cancelHandler Handler for the cancel action of the alert.
*/
+ (void)showSignInAlertWithEmail:(NSString *)email
provider:(id<FUIAuthProvider>)provider
presentingViewController:(UIViewController *)presentingViewController
signinHandler:(FUIAuthAlertActionHandler)signinHandler
cancelHandler:(FUIAuthAlertActionHandler)cancelHandler;

/** @fn showSignInAlertWithEmail:providerShortName:providerSignInLabel:handler:
@brief Displays an alert to conform with user whether she wants to proceed with the provider.
@param email The email address to sign in with.
@param providerShortName The name of the provider as displayed in the sign-in alert message.
@param providerSignInLabel The name of the provider as displayed in the sign-in alert button.
@param signinHandler Handler for the sign in action of the alert.
@param cancelHandler Handler for the cancel action of the alert.
*/
+ (void)showSignInAlertWithEmail:(NSString *)email
providerShortName:(NSString *)providerShortName
providerSignInLabel:(NSString *)providerSignInLabel
presentingViewController:(UIViewController *)presentingViewController
signinHandler:(FUIAuthAlertActionHandler)signinHandler
cancelHandler:(FUIAuthAlertActionHandler)cancelHandler;

/** @fn pushViewController:
@brief Push the view controller to the navigation controller of the current view controller
with animation. The pushed view controller will have a fixed "Back" title for back button.
Expand Down
7 changes: 7 additions & 0 deletions FirebaseAuthUI/FUIAuthErrorUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ NS_ASSUME_NONNULL_BEGIN
*/
+ (NSError *)userCancelledSignInError;

/** @fn userCancelledSignIn
@brief Constructs an @c NSError with the @c FUIAuthErrorCodeMergeConflict code.
@param userInfo The userInfo dictionary to add to the NSError object.
@return The merge conflict error.
*/
+ (NSError *)mergeConflictErrorWithUserInfo:(NSDictionary *)userInfo;

/** @fn providerErrorWithUnderlyingError:providerID:
@brief Constructs an @c NSError with the @c FUIAuthErrorCodeProviderError code and a populated
@c NSUnderlyingErrorKey and @c FUIAuthErrorUserInfoProviderIDKey in the
Expand Down
4 changes: 4 additions & 0 deletions FirebaseAuthUI/FUIAuthErrorUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ + (NSError *)userCancelledSignInError {
return [self errorWithCode:FUIAuthErrorCodeUserCancelledSignIn userInfo:nil];
}

+ (NSError *)mergeConflictErrorWithUserInfo:(NSDictionary *)userInfo {
return [self errorWithCode:FUIAuthErrorCodeMergeConflict userInfo:userInfo];
}

+ (NSError *)providerErrorWithUnderlyingError:(NSError *)underlyingError
providerID:(NSString *)providerID {
return [self errorWithCode:FUIAuthErrorCodeProviderError
Expand Down
13 changes: 13 additions & 0 deletions FirebaseAuthUI/FUIAuthErrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ extern NSString *const FUIAuthErrorDomain;
*/
extern NSString *const FUIAuthErrorUserInfoProviderIDKey;

/** @bar FUIAuthCredentialKey
@brief The key used to obtain the credential stored within the userInfo dictionary of the
error, if availalble.
*/
extern NSString *const FUIAuthCredentialKey;

/** @var FUIAuthErrorCode
@brief Error codes used by FUIAuth.
*/
Expand All @@ -50,6 +56,13 @@ typedef NS_ENUM(NSUInteger, FUIAuthErrorCode) {
key @c FUIAuthErrorUserInfoProviderIDKey).
*/
FUIAuthErrorCodeCantFindProvider = 3,

/** @var FUIAuthErrorCodeMergeConflict
@brief Indicates that a merge conflict occurred while trying to automatically upgrade an
anonymous user. The non-anonymous credential can be obtained from the userInfo dictionary
of the corresponding NSError using the @c FUIAuthCredentialKey.
*/
FUIAuthErrorCodeMergeConflict = 4,
};

NS_ASSUME_NONNULL_END
2 changes: 2 additions & 0 deletions FirebaseAuthUI/FUIAuthErrors.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@
NSString *const FUIAuthErrorDomain = @"FUIAuthErrorDomain";

NSString *const FUIAuthErrorUserInfoProviderIDKey = @"FUIAuthErrorUserInfoProviderIDKey";

NSString *const FUIAuthCredentialKey = @"FUIAuthCredentialKey";
7 changes: 6 additions & 1 deletion FirebaseAuthUI/FUIAuthProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,15 @@ __attribute__((deprecated("This is deprecated API and will be removed in a futur
@optional;

/** @property idToken
@brief User Id Token obtained during sign in. Not all providers can return, thus it's optional
@brief User Id Token obtained during sign in. Not all providers can return, thus it's optional.
*/
@property(nonatomic, copy, readonly) NSString *idToken;

/** @fn email
@brief The email address associated with this provider, if any.
*/
- (NSString *)email;

/** @fn handleOpenURL:
@brief May be used to help complete a sign-in flow which requires a callback from Safari.
@param URL The URL which may be handled by the auth provider if an URL is expected.
Expand Down
82 changes: 57 additions & 25 deletions FirebaseAuthUI/FUIPasswordSignInViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@
// limitations under the License.
//

#import "FUIPasswordSignInViewController.h"
#import "FUIPasswordSignInViewController_Internal.h"

#import <FirebaseAuth/FirebaseAuth.h>
#import "FUIAuthBaseViewController_Internal.h"
#import "FUIAuthErrorUtils.h"
#import "FUIAuthStrings.h"
#import "FUIAuthTableViewCell.h"
#import "FUIAuthUtils.h"
#import "FUIAuth_Internal.h"
#import "FUIAuthErrors.h"
#import "FUIPasswordRecoveryViewController.h"

/** @var kCellReuseIdentifier
Expand Down Expand Up @@ -78,6 +80,10 @@ - (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
_email = [email copy];

self.title = FUILocalizedString(kStr_SignInTitle);
__weak FUIPasswordSignInViewController *weakself = self;
_onDismissCallback = ^(FIRAuthDataResult *authResult, NSError *error){
[weakself.authUI invokeResultCallbackWithAuthDataResult:authResult error:error];
};
}
return self;
}
Expand Down Expand Up @@ -127,35 +133,61 @@ - (void)signInWithDefaultValue:(NSString *)email andPassword:(NSString *)passwor
}

[self incrementActivity];

FIRAuthCredential *credential =
[FIREmailAuthProvider credentialWithEmail:email password:password];
[self.auth signInAndRetrieveDataWithCredential:credential
completion:^(FIRAuthDataResult *_Nullable authResult,
NSError *_Nullable error) {
[self decrementActivity];

if (error) {
switch (error.code) {
case FIRAuthErrorCodeWrongPassword:
[self showAlertWithMessage:FUILocalizedString(kStr_WrongPasswordError)];
return;
case FIRAuthErrorCodeUserNotFound:
[self showAlertWithMessage:FUILocalizedString(kStr_UserNotFoundError)];
return;
case FIRAuthErrorCodeUserDisabled:
[self showAlertWithMessage:FUILocalizedString(kStr_AccountDisabledError)];
return;
case FIRAuthErrorCodeTooManyRequests:
[self showAlertWithMessage:FUILocalizedString(kStr_SignInTooManyTimesError)];

void (^completeSignInBlock)(FIRAuthDataResult *, NSError *) = ^(FIRAuthDataResult *authResult,
NSError *error) {
[self decrementActivity];

if (error) {
switch (error.code) {
case FIRAuthErrorCodeWrongPassword:
[self showAlertWithMessage:FUILocalizedString(kStr_WrongPasswordError)];
return;
case FIRAuthErrorCodeUserNotFound:
[self showAlertWithMessage:FUILocalizedString(kStr_UserNotFoundError)];
return;
case FIRAuthErrorCodeUserDisabled:
[self showAlertWithMessage:FUILocalizedString(kStr_AccountDisabledError)];
return;
case FIRAuthErrorCodeTooManyRequests:
[self showAlertWithMessage:FUILocalizedString(kStr_SignInTooManyTimesError)];
return;
}
}
[self.navigationController dismissViewControllerAnimated:YES completion:^{
if (self->_onDismissCallback) {
self->_onDismissCallback(authResult, error);
}
}];
};

// Check for the presence of an anonymous user and whether automatic upgrade is enabled.
if (self.auth.currentUser.isAnonymous &&
[FUIAuth defaultAuthUI].shouldAutoUpgradeAnonymousUsers) {

[self.auth.currentUser
linkAndRetrieveDataWithCredential:credential
completion:^(FIRAuthDataResult *_Nullable authResult,
NSError *_Nullable error) {
if (error) {
if (error.code == FIRAuthErrorCodeEmailAlreadyInUse) {
NSDictionary *userInfo = @{ FUIAuthCredentialKey : credential };
NSError *mergeError = [FUIAuthErrorUtils mergeConflictErrorWithUserInfo:userInfo];
[self.navigationController dismissViewControllerAnimated:YES completion:^{
[self.authUI invokeResultCallbackWithAuthDataResult:authResult error:mergeError];
}];
return;
}
completeSignInBlock(nil, error);
return;
}
}

[self dismissNavigationControllerAnimated:YES completion:^{
[self.authUI invokeResultCallbackWithAuthDataResult:authResult error:error];
completeSignInBlock(authResult, nil);
}];
}];
} else {
[self.auth signInAndRetrieveDataWithCredential:credential completion:completeSignInBlock];
}
}

- (void)signIn {
Expand Down
35 changes: 35 additions & 0 deletions FirebaseAuthUI/FUIPasswordSignInViewController_Internal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// Copyright (c) 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#import "FUIPasswordSignInViewController.h"
#import <FirebaseAuth/FirebaseAuth.h>

NS_ASSUME_NONNULL_BEGIN

@interface FUIPasswordSignInViewController ()

/** @property onDismissCallback:
@brief Sets an optional custom callback for FUIPasswordSigInViewController during dismissal. If
this property is set the default dismissal routine is not triggered and should be included
in this block if necessary. This block is NOT set to nil after use, set to nil after using
if you wish to avoid circular references.
*/
@property(nonatomic, strong, nullable) FIRAuthDataResultCallback onDismissCallback;

NS_ASSUME_NONNULL_END


@end
Loading