351 lines
12 KiB
Objective-C
351 lines
12 KiB
Objective-C
//
|
|
// Copyright (c) 2016 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 "FirebaseFacebookAuthUI/Sources/Public/FirebaseFacebookAuthUI/FUIFacebookAuth.h"
|
|
|
|
#import <FirebaseAuthUI/FirebaseAuthUI.h>
|
|
#import <FirebaseAuth/FirebaseAuth.h>
|
|
|
|
#if SWIFT_PACKAGE
|
|
@import FBSDKCoreKit;
|
|
@import FBSDKLoginKit;
|
|
#else
|
|
#import <FBSDKCoreKit/FBSDKCoreKit.h>
|
|
#import <FBSDKLoginKit/FBSDKLoginKit.h>
|
|
#endif // SWIFT_PACKAGE
|
|
|
|
/** @var kTableName
|
|
@brief The name of the strings table to search for localized strings.
|
|
*/
|
|
static NSString *const kTableName = @"FirebaseFacebookAuthUI";
|
|
|
|
/** @var kBundleName
|
|
@brief The name of the bundle to search for resources.
|
|
*/
|
|
#if SWIFT_PACKAGE
|
|
static NSString *const kBundleName = @"FirebaseUI_FirebaseFacebookAuthUI";
|
|
#else
|
|
static NSString *const kBundleName = @"FirebaseFacebookAuthUI";
|
|
#endif // SWIFT_PACKAGE
|
|
|
|
/** @var kSignInWithFacebook
|
|
@brief The string key for localized button text.
|
|
*/
|
|
static NSString *const kSignInWithFacebook = @"SignInWithFacebook";
|
|
|
|
/** @var kFacebookAppId
|
|
@brief The string key used to read Facebook App Id from Info.plist.
|
|
*/
|
|
static NSString *const kFacebookAppId = @"FacebookAppID";
|
|
|
|
/** @var kFacebookDisplayName
|
|
@brief The string key used to read Facebook App Name from Info.plist.
|
|
*/
|
|
static NSString *const kFacebookDisplayName = @"FacebookDisplayName";
|
|
|
|
@interface FUIFacebookAuth () <FUIAuthProvider>
|
|
|
|
/** @property authUI
|
|
@brief FUIAuth instance of the application.
|
|
*/
|
|
@property(nonatomic, strong) FUIAuth *authUI;
|
|
|
|
/** @property providerForEmulator
|
|
@brief The OAuth provider to be used when the emulator is enabled.
|
|
*/
|
|
@property(nonatomic, strong) FIROAuthProvider *providerForEmulator;
|
|
|
|
@end
|
|
|
|
@implementation FUIFacebookAuth {
|
|
|
|
/** @var _pendingSignInCallback
|
|
@brief The callback which should be invoked when the sign in flow completes (or is cancelled.)
|
|
*/
|
|
FUIAuthProviderSignInCompletionBlock _pendingSignInCallback;
|
|
|
|
/** @var _presentingViewController
|
|
@brief The presenting view controller for interactive sign-in.
|
|
*/
|
|
UIViewController *_presentingViewController;
|
|
|
|
/** @var _email
|
|
@brief The email address associated with this account.
|
|
*/
|
|
NSString *_email;
|
|
}
|
|
|
|
+ (NSBundle *)bundle {
|
|
return [FUIAuthUtils bundleNamed:kBundleName
|
|
inFrameworkBundle:[NSBundle bundleForClass:[self class]]];
|
|
}
|
|
|
|
- (instancetype)initWithAuthUI:(FUIAuth *)authUI
|
|
permissions:(NSArray *)permissions {
|
|
self = [super init];
|
|
if (self != nil) {
|
|
_authUI = authUI;
|
|
_scopes = permissions;
|
|
[self configureProvider];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithAuthUI:(FUIAuth *)authUI {
|
|
return [self initWithAuthUI:authUI permissions:@[ @"email" ]];
|
|
}
|
|
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
|
|
- (instancetype)initWithPermissions:(NSArray *)permissions {
|
|
self = [super init];
|
|
if (self != nil) {
|
|
_scopes = permissions;
|
|
[self configureProvider];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)init {
|
|
return [self initWithPermissions:@[ @"email" ]];
|
|
}
|
|
#pragma clang diagnostic pop
|
|
|
|
|
|
#pragma mark - FUIAuthProvider
|
|
|
|
- (nullable NSString *)providerID {
|
|
return FIRFacebookAuthProviderID;
|
|
}
|
|
|
|
- (nullable NSString *)accessToken {
|
|
if (self.authUI.isEmulatorEnabled) {
|
|
return nil;
|
|
}
|
|
return [FBSDKAccessToken currentAccessToken].tokenString;
|
|
}
|
|
|
|
/** @fn idToken:
|
|
@brief Facebook doesn't provide User Id Token during sign in flow
|
|
*/
|
|
- (nullable NSString *)idToken {
|
|
return nil;
|
|
}
|
|
|
|
- (NSString *)shortName {
|
|
return @"Facebook";
|
|
}
|
|
|
|
- (NSString *)signInLabel {
|
|
return FUILocalizedStringFromTableInBundle(kSignInWithFacebook,
|
|
kTableName,
|
|
[FUIFacebookAuth bundle]);
|
|
}
|
|
|
|
- (UIImage *)icon {
|
|
return [FUIAuthUtils imageNamed:@"ic_facebook" fromBundle:[FUIFacebookAuth bundle]];
|
|
}
|
|
|
|
- (UIColor *)buttonBackgroundColor {
|
|
return [UIColor colorWithRed:59.0f/255.0f green:89.0f/255.0f blue:152.0f/255.0f alpha:1.0f];
|
|
}
|
|
|
|
- (UIColor *)buttonTextColor {
|
|
return [UIColor whiteColor];
|
|
}
|
|
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
|
|
- (void)signInWithEmail:(nullable NSString *)email
|
|
presentingViewController:(nullable UIViewController *)presentingViewController
|
|
completion:(nullable FUIAuthProviderSignInCompletionBlock)completion {
|
|
[self signInWithDefaultValue:email
|
|
presentingViewController:presentingViewController
|
|
completion:completion];
|
|
}
|
|
#pragma clang diagnostic pop
|
|
|
|
- (void)signInWithDefaultValue:(nullable NSString *)defaultValue
|
|
presentingViewController:(nullable UIViewController *)presentingViewController
|
|
completion:(nullable FUIAuthProviderSignInCompletionBlock)completion {
|
|
_pendingSignInCallback = completion;
|
|
_presentingViewController = presentingViewController;
|
|
|
|
if (self.authUI.isEmulatorEnabled) {
|
|
[self signInWithOAuthProvider:self.providerForEmulator
|
|
presentingViewController:presentingViewController
|
|
completion:completion];
|
|
return;
|
|
}
|
|
|
|
[_loginManager logInWithPermissions:_scopes
|
|
fromViewController:presentingViewController
|
|
handler:^(FBSDKLoginManagerLoginResult *result,
|
|
NSError *error) {
|
|
if (error) {
|
|
NSError *newError =
|
|
[FUIAuthErrorUtils providerErrorWithUnderlyingError:error
|
|
providerID:FIRFacebookAuthProviderID];
|
|
[self completeSignInFlowWithAccessToken:nil error:newError];
|
|
} else if (result.isCancelled) {
|
|
NSError *newError = [FUIAuthErrorUtils userCancelledSignInError];
|
|
[self completeSignInFlowWithAccessToken:nil error:newError];
|
|
} else {
|
|
// Retrieve email.
|
|
[[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{ @"fields" : @"email" }] startWithCompletion:^(id<FBSDKGraphRequestConnecting> connection,
|
|
id result,
|
|
NSError *error) {
|
|
self->_email = result[@"email"];
|
|
}];
|
|
[self completeSignInFlowWithAccessToken:result.token.tokenString
|
|
error:nil];
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (void)signInWithOAuthProvider:(FIROAuthProvider *)oauthProvider
|
|
presentingViewController:(nullable UIViewController *)presentingViewController
|
|
completion:(nullable FUIAuthProviderSignInCompletionBlock)completion {
|
|
oauthProvider.scopes = self.scopes;
|
|
|
|
[oauthProvider getCredentialWithUIDelegate:nil
|
|
completion:^(FIRAuthCredential *_Nullable credential,
|
|
NSError *_Nullable error) {
|
|
if (error) {
|
|
[FUIAuthBaseViewController showAlertWithMessage:error.localizedDescription
|
|
presentingViewController:presentingViewController];
|
|
if (completion) {
|
|
completion(nil, error, nil, nil);
|
|
}
|
|
return;
|
|
}
|
|
if (completion) {
|
|
UIActivityIndicatorView *activityView =
|
|
[FUIAuthBaseViewController addActivityIndicator:presentingViewController.view];
|
|
[activityView startAnimating];
|
|
FIRAuthResultCallback result = ^(FIRUser *_Nullable user,
|
|
NSError *_Nullable error) {
|
|
[activityView stopAnimating];
|
|
[activityView removeFromSuperview];
|
|
};
|
|
completion(credential, nil, result, nil);
|
|
}
|
|
}];
|
|
}
|
|
|
|
- (NSString *)email {
|
|
return _email;
|
|
}
|
|
|
|
- (void)signOut {
|
|
if (self.authUI.isEmulatorEnabled) {
|
|
return;
|
|
}
|
|
[_loginManager logOut];
|
|
}
|
|
|
|
- (BOOL)handleOpenURL:(NSURL *)URL sourceApplication:(NSString *)sourceApplication {
|
|
if (self.authUI.isEmulatorEnabled) {
|
|
return NO;
|
|
}
|
|
return [[FBSDKApplicationDelegate sharedInstance] application:[UIApplication sharedApplication]
|
|
openURL:URL
|
|
sourceApplication:sourceApplication
|
|
annotation:nil];
|
|
}
|
|
|
|
#pragma mark -
|
|
|
|
/** @fn completeSignInFlowWithAccessToken:error:
|
|
@brief Called with the result of a Facebook sign-in attempt. Invokes and clears any pending
|
|
sign in callback block.
|
|
@param accessToken The Facebook access token, if successful.
|
|
@param error An error which occurred during the sign-in attempt.
|
|
*/
|
|
- (void)completeSignInFlowWithAccessToken:(nullable NSString *)accessToken
|
|
error:(nullable NSError *)error {
|
|
if (error) {
|
|
[self callbackWithCredential:nil error:error result:nil];
|
|
return;
|
|
}
|
|
// Assume accessToken cannot be nil if there's no error.
|
|
NSString *_Nonnull token = (id _Nonnull)accessToken;
|
|
FIRAuthCredential *credential = [FIRFacebookAuthProvider credentialWithAccessToken:token];
|
|
UIActivityIndicatorView *activityView =
|
|
[FUIAuthBaseViewController addActivityIndicator:_presentingViewController.view];
|
|
[activityView startAnimating];
|
|
[self callbackWithCredential:credential error:nil result:^(FIRUser *_Nullable user,
|
|
NSError *_Nullable error) {
|
|
[activityView stopAnimating];
|
|
[activityView removeFromSuperview];
|
|
}];
|
|
}
|
|
|
|
/** @fn callbackWithCredential:error:
|
|
@brief Ends the sign-in flow by cleaning up and calling back with given credential or error.
|
|
@param credential The credential to pass back, if any.
|
|
@param error The error to pass back, if any.
|
|
@param result The result of sign-in operation using provided @c FIRAuthCredential object.
|
|
@see @c FIRAuth.signInWithCredential:completion:
|
|
*/
|
|
- (void)callbackWithCredential:(nullable FIRAuthCredential *)credential
|
|
error:(nullable NSError *)error
|
|
result:(nullable FIRAuthResultCallback)result {
|
|
FUIAuthProviderSignInCompletionBlock callback = _pendingSignInCallback;
|
|
_pendingSignInCallback = nil;
|
|
if (callback) {
|
|
callback(credential, error, result, nil);
|
|
}
|
|
}
|
|
|
|
/** @fn callbackWithCredential:error:
|
|
@brief Validates that Facebook SDK data was filled in Info.plist and creates Facebook login manager
|
|
*/
|
|
- (void)configureProvider {
|
|
NSBundle *bundle = [NSBundle mainBundle];
|
|
NSString *facebookAppId = [bundle objectForInfoDictionaryKey:kFacebookAppId];
|
|
NSString *facebookDisplayName = [bundle objectForInfoDictionaryKey:kFacebookDisplayName];
|
|
|
|
if (facebookAppId == nil || facebookDisplayName == nil) {
|
|
// Executes in test targets only.
|
|
bundle = [FUIFacebookAuth bundle];
|
|
facebookAppId = facebookAppId ?: [bundle objectForInfoDictionaryKey:kFacebookAppId];
|
|
facebookDisplayName = facebookDisplayName ?:
|
|
[bundle objectForInfoDictionaryKey:kFacebookDisplayName];
|
|
}
|
|
|
|
if (!(facebookAppId && facebookDisplayName)) {
|
|
[NSException raise:NSInternalInconsistencyException
|
|
format:@"Please set FacebookAppID, FacebookDisplayName, and\nURL types > Url "
|
|
@"Schemes in `Supporting Files/Info.plist` according to "
|
|
@"https://developers.facebook.com/docs/ios/getting-started"];
|
|
}
|
|
|
|
if (self.authUI.isEmulatorEnabled) {
|
|
_providerForEmulator = [FIROAuthProvider providerWithProviderID:self.providerID];
|
|
} else {
|
|
_loginManager = [self createLoginManager];
|
|
}
|
|
}
|
|
|
|
#pragma mark - Private methods
|
|
|
|
- (FBSDKLoginManager *)createLoginManager {
|
|
return [[FBSDKLoginManager alloc] init];
|
|
}
|
|
|
|
@end
|