diff --git a/Lock.podspec b/Lock.podspec index 1172d9578..e33ace6c8 100644 --- a/Lock.podspec +++ b/Lock.podspec @@ -60,33 +60,6 @@ Auth0 is a SaaS that helps you with Authentication and Authorization. You can us ui.resource_bundles = { 'Auth0' => ['Pod/Assets/UI/Images/*.png', 'Pod/Assets/UI/*.plist', 'Pod/Assets/UI/*.ttf']} end - s.subspec 'Facebook' do |facebook| - facebook.platform = :ios - facebook.public_header_files = 'Pod/Classes/Provider/Facebook/*.h' - facebook.source_files = 'Pod/Classes/Provider/Facebook/*.{h,m}' - facebook.dependency 'Lock/Core' - facebook.dependency 'Facebook-iOS-SDK', '~> 3.15' - end - - s.subspec 'Twitter' do |twitter| - twitter.platform = :ios - twitter.public_header_files = 'Pod/Classes/Twitter/*.h' - twitter.source_files = 'Pod/Classes/Provider/Twitter/*.{h,m}' - twitter.dependency 'Lock/Core' - twitter.dependency 'BDBOAuth1Manager', '~> 1.5.0' - twitter.dependency 'TWReverseAuth', '~> 0.1.0' - twitter.dependency 'PSAlertView', '~> 2.0' - twitter.frameworks = 'Social', 'Accounts', 'Twitter' - end - - s.subspec 'GooglePlus' do |gplus| - gplus.platform = :ios - gplus.public_header_files = 'Pod/Classes/Provider/GooglePlus/*.h' - gplus.source_files = 'Pod/Classes/Provider/GooglePlus/*.{h,m}' - gplus.dependency 'Lock/Core' - gplus.dependency 'googleplus-ios-sdk', '~> 1.7.1' - end - s.subspec 'TouchID' do |touchid| touchid.platform = :ios touchid.public_header_files = 'Pod/Classes/TouchID/*.h' diff --git a/Lock/Podfile.lock b/Lock/Podfile.lock index 6efd6af81..5429ddef8 100644 --- a/Lock/Podfile.lock +++ b/Lock/Podfile.lock @@ -202,7 +202,7 @@ SPEC CHECKSUMS: ISO8601DateFormatter: ab926648eebe497f4d167c0fd083992f959f1274 JWTDecode: bff190dc06ff9ee7a3a244c454dc8ef05962c994 libextobjc: a650fc1bf489a3d3a9bc2e621efa3e1006fc5471 - Lock: b9b0a1503e24008d311b572e4e4d224747bc0ab9 + Lock: 26649aca7524ee65f2fc420f7d4c3c7544ec096b Lock-Facebook: 02c315b7d15d6fed20c7a2ea8a5e5e27c072483a Lock-Twitter: 61168e95f6614535167621794cbada2d6606d4c5 LUKeychainAccess: 6479edb12cf06e11ff3336f796959eedbc58f714 diff --git a/Pod/Classes/Provider/Facebook/A0FacebookAuthenticator.h b/Pod/Classes/Provider/Facebook/A0FacebookAuthenticator.h deleted file mode 100644 index 190da462c..000000000 --- a/Pod/Classes/Provider/Facebook/A0FacebookAuthenticator.h +++ /dev/null @@ -1,49 +0,0 @@ -// A0FacebookAuthenticator.h -// -// Copyright (c) 2014 Auth0 (http://auth0.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import -#import "A0BaseAuthenticator.h" - -/** - * `A0FacebookAuthentication` performs Facebook authentication of a user using Facebook iOS SDK v3. - * @deprecated 1.12.0. Moved FB authenticator to a independent library called [Lock-Facebook](https://github.com/auth0/Lock-Facebook.iOS) - */ -__attribute__ ((deprecated("use A0FacebookAuthenticator from 'Lock-Facebook' library"))) -@interface A0FacebookAuthenticator : A0BaseAuthenticator - -/** - * Creates a new instance - * - * @param permissions list of permissions to ask the user when authenticating application - * - * @return a new instance - */ -+ (A0FacebookAuthenticator *)newAuthenticatorWithPermissions:(NSArray *)permissions; - -/** - * Creates a new instance with the default permissions ("public_profile"). - * - * @return a new instance - */ -+ (A0FacebookAuthenticator *)newAuthenticatorWithDefaultPermissions; - -@end diff --git a/Pod/Classes/Provider/Facebook/A0FacebookAuthenticator.m b/Pod/Classes/Provider/Facebook/A0FacebookAuthenticator.m deleted file mode 100644 index e3e0bdc2b..000000000 --- a/Pod/Classes/Provider/Facebook/A0FacebookAuthenticator.m +++ /dev/null @@ -1,148 +0,0 @@ -// A0FacebookAuthenticator.m -// -// Copyright (c) 2014 Auth0 (http://auth0.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "A0FacebookAuthenticator.h" - -#import -#import - -#import "A0Errors.h" -#import "A0Strategy.h" -#import "A0Application.h" -#import "A0APIClient.h" -#import "A0IdentityProviderCredentials.h" -#import "A0AuthParameters.h" -#import "NSObject+A0APIClientProvider.h" - -@interface A0FacebookAuthenticator () -@property (strong, nonatomic) NSArray *permissions; -@end - -@implementation A0FacebookAuthenticator - -AUTH0_DYNAMIC_LOGGER_METHODS - -- (instancetype)initWithPermissions:(NSArray *)permissions { - self = [super init]; - if (self) { - if (permissions) { - NSMutableSet *perms = [[NSMutableSet alloc] initWithArray:permissions]; - [perms addObject:@"public_profile"]; - self.permissions = [perms allObjects]; - } else { - self.permissions = @[@"public_profile"]; - } - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationActiveNotification:) name:UIApplicationDidBecomeActiveNotification object:nil]; - } - return self; -} - -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)applicationActiveNotification:(NSNotification *)notification { - [FBAppCall handleDidBecomeActive]; -} - -+ (A0FacebookAuthenticator *)newAuthenticatorWithPermissions:(NSArray *)permissions { - return [[A0FacebookAuthenticator alloc] initWithPermissions:permissions]; -} - -+ (A0FacebookAuthenticator *)newAuthenticatorWithDefaultPermissions { - return [self newAuthenticatorWithPermissions:nil]; -} - -#pragma mark - A0SocialProviderAuth - -- (NSString *)identifier { - return A0StrategyNameFacebook; -} - -- (void)clearSessions { - [[FBSession activeSession] closeAndClearTokenInformation]; -} - -- (BOOL)handleURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication { - A0LogVerbose(@"Received url %@ from source application %@", url, sourceApplication); - return [FBAppCall handleOpenURL:url sourceApplication:sourceApplication]; -} - --(void)authenticateWithParameters:(A0AuthParameters *)parameters success:(void (^)(A0UserProfile *, A0Token *))success failure:(void (^)(NSError *))failure { - A0LogVerbose(@"Starting Facebook authentication..."); - FBSession *active = [FBSession activeSession]; - if (active.state == FBSessionStateOpen || active.state == FBSessionStateOpenTokenExtended) { - A0LogDebug(@"Found FB Active Session"); - [self executeAuthenticationWithCredentials:[[A0IdentityProviderCredentials alloc] initWithAccessToken:active.accessTokenData.accessToken] parameters:parameters success:success failure:failure]; - } else { - @weakify(self); - NSArray *permissions = [self permissionsFromParameters:parameters]; - [FBSession openActiveSessionWithReadPermissions:permissions allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) { - if (error) { - if (failure) { - A0LogError(@"Failed to open FB Session with error %@", error); - NSError *errorParam = [FBErrorUtility errorCategoryForError:error] == FBErrorCategoryUserCancelled ? [A0Errors facebookCancelled] : error; - failure(errorParam); - } - } else { - switch (status) { - case FBSessionStateOpen: { - A0LogDebug(@"Successfully opened FB Session"); - @strongify(self); - [self executeAuthenticationWithCredentials:[[A0IdentityProviderCredentials alloc] initWithAccessToken:session.accessTokenData.accessToken] parameters:parameters success:success failure:failure]; - break; - } - case FBSessionStateClosedLoginFailed: - A0LogError(@"User cancelled FB Login"); - if (failure) { - failure([A0Errors facebookCancelled]); - } - default: - break; - } - } - }]; - } -} - -#pragma mark - Utility methods - -- (NSArray *)permissionsFromParameters:(A0AuthParameters *)parameters { - NSArray *connectionScopes = parameters.connectionScopes[A0StrategyNameFacebook]; - NSArray *permissions = connectionScopes.count > 0 ? connectionScopes : self.permissions; - A0LogDebug(@"Facebook Permissions %@", permissions); - return permissions; -} - -- (void)executeAuthenticationWithCredentials:(A0IdentityProviderCredentials *)credentials - parameters:(A0AuthParameters *)parameters - success:(void(^)(A0UserProfile *, A0Token *))success - failure:(void(^)(NSError *))failure { - A0APIClient *client = [self a0_apiClientFromProvider:self.clientProvider]; - [client authenticateWithSocialConnectionName:self.identifier - credentials:credentials - parameters:parameters - success:success - failure:failure]; -} - -@end diff --git a/Pod/Classes/Provider/GooglePlus/A0GooglePlusAuthenticator.h b/Pod/Classes/Provider/GooglePlus/A0GooglePlusAuthenticator.h deleted file mode 100644 index d8d0ad55b..000000000 --- a/Pod/Classes/Provider/GooglePlus/A0GooglePlusAuthenticator.h +++ /dev/null @@ -1,51 +0,0 @@ -// A0GooglePlusAuthenticator.h -// -// Copyright (c) 2015 Auth0 (http://auth0.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import -#import "A0BaseAuthenticator.h" - -/** - * `A0GooglePlusAuthenticator` performs Google+ authentication using Google's official SDK. - * @deprecated 1.12.0. Moved G+ authenticator to an independent library called Lock-GooglePlus (https://github.com/auth0/Lock-GooglePlus.iOS) - */ -__attribute__ ((deprecated("use A0GooglePlusAuthenticator from 'Lock-GooglePlus' library"))) -@interface A0GooglePlusAuthenticator : A0BaseAuthenticator - -/** - * Creates a new authenticator with default scopes (login and email) and a clientId. - * - * @param clientId application clientId in Google+ - * - * @return a new instance - */ -+ (instancetype)newAuthenticatorWithClientId:(NSString *)clientId; - -/** - * Creates a new authenticator with a list of scopes and a clientId. - * - * @param clientId application clientId in Google+ - * @param scopes list of scopes to send to Google+ API. - * - * @return a new instance - */ -+ (instancetype)newAuthenticatorWithClientId:(NSString *)clientId andScopes:(NSArray *)scopes; -@end diff --git a/Pod/Classes/Provider/GooglePlus/A0GooglePlusAuthenticator.m b/Pod/Classes/Provider/GooglePlus/A0GooglePlusAuthenticator.m deleted file mode 100644 index a91059969..000000000 --- a/Pod/Classes/Provider/GooglePlus/A0GooglePlusAuthenticator.m +++ /dev/null @@ -1,157 +0,0 @@ -// A0GooglePlusAuthenticator.m -// -// Copyright (c) 2015 Auth0 (http://auth0.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "A0GooglePlusAuthenticator.h" -#import "A0Strategy.h" -#import -#import -#import "A0APIClient.h" -#import "A0IdentityProviderCredentials.h" -#import "A0Errors.h" -#import "A0AuthParameters.h" -#import "NSObject+A0APIClientProvider.h" - -@interface A0GooglePlusAuthenticator () -@property (copy, nonatomic) void (^successBlock)(A0UserProfile *, A0Token *); -@property (copy, nonatomic) void (^failureBlock)(NSError *); -@property (strong, nonatomic) A0AuthParameters *parameters; -@property (assign, nonatomic) BOOL authenticating; -@property (strong, nonatomic) NSArray *scopes; -@end - -@implementation A0GooglePlusAuthenticator - -AUTH0_DYNAMIC_LOGGER_METHODS - -- (instancetype)initWithClientId:(NSString *)clientId { - return [self initWithClientId:clientId scopes:nil]; -} - -- (instancetype)initWithClientId:(NSString *)clientId scopes:(NSArray *)scopes { - self = [super init]; - if (self) { - GPPSignIn *signIn = [GPPSignIn sharedInstance]; - signIn.clientID = clientId; - self.scopes = [scopes copy]; - signIn.delegate = self; - [self clearCallbacks]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; - } - return self; -} - -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -+ (instancetype)newAuthenticatorWithClientId:(NSString *)clientId { - return [[A0GooglePlusAuthenticator alloc] initWithClientId:clientId]; -} - -+ (instancetype)newAuthenticatorWithClientId:(NSString *)clientId andScopes:(NSArray *)scopes { - return [[A0GooglePlusAuthenticator alloc] initWithClientId:clientId scopes:scopes]; -} - -- (NSString *)identifier { - return A0StrategyNameGooglePlus; -} - -- (void)authenticateWithParameters:(A0AuthParameters *)parameters - success:(void (^)(A0UserProfile *, A0Token *))success - failure:(void (^)(NSError *))failure { - NSAssert(success != nil, @"Must provide a non-nil success block"); - NSAssert(failure != nil, @"Must provide a non-nil failure block"); - self.successBlock = success; - self.failureBlock = failure; - self.parameters = parameters; - GPPSignIn *signIn = [GPPSignIn sharedInstance]; - signIn.scopes = [self scopesFromParameters:parameters]; - self.authenticating = YES; - [signIn authenticate]; - A0LogVerbose(@"Starting Google+ Authentication..."); -} - -- (void)clearSessions { - self.parameters = nil; - self.authenticating = NO; - [self clearCallbacks]; - [[GPPSignIn sharedInstance] signOut]; - A0LogVerbose(@"Cleared Google+ session"); -} - -- (BOOL)handleURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication { - return [GPPURLHandler handleURL:url sourceApplication:sourceApplication annotation:nil]; -} - -#pragma mark - GPPSignInDelegate - -- (void)finishedWithAuth:(GTMOAuth2Authentication *)auth error:(NSError *)error { - dispatch_async(dispatch_get_main_queue(), ^{ - self.authenticating = NO; - }); - if (error) { - A0LogError(@"Failed to authenticate with Google+ with error %@", error); - self.failureBlock([A0Errors googleplusFailed]); - } else { - A0LogVerbose(@"Authenticated with Google+"); - A0IdentityProviderCredentials *credentials = [[A0IdentityProviderCredentials alloc] initWithAccessToken:auth.accessToken]; - A0APIClient *client = [self a0_apiClientFromProvider:self.clientProvider]; - [client authenticateWithSocialConnectionName:A0StrategyNameGooglePlus - credentials:credentials - parameters:self.parameters - success:self.successBlock - failure:self.failureBlock]; - [self clearCallbacks]; - self.parameters = nil; - } -} - -#pragma mark - Utility methods - -- (NSArray *)scopesFromParameters:(A0AuthParameters *)parameters { - NSMutableSet *scopeSet = [[NSMutableSet alloc] init]; - [scopeSet addObject:kGTLAuthScopePlusLogin]; - [scopeSet addObject:kGTLAuthScopePlusUserinfoEmail]; - NSArray *connectionScopes = parameters.connectionScopes[A0StrategyNameFacebook]; - NSArray *scopes = connectionScopes.count > 0 ? connectionScopes : self.scopes; - if (scopes) { - [scopeSet addObjectsFromArray:scopes]; - } - A0LogDebug(@"Google+ scopes %@", scopeSet); - return [scopeSet allObjects]; -} - -- (void)clearCallbacks { - self.failureBlock = ^(NSError *error) {}; - self.successBlock = ^(A0UserProfile* profile, A0Token *token) {}; -} - -- (void)handleDidBecomeActive:(NSNotification *)notification { - if (self.authenticating) { - self.authenticating = NO; - self.failureBlock([A0Errors googleplusCancelled]); - [self clearCallbacks]; - self.parameters = nil; - } - -} -@end diff --git a/Pod/Classes/Provider/Twitter/A0TwitterAuthenticator.h b/Pod/Classes/Provider/Twitter/A0TwitterAuthenticator.h deleted file mode 100644 index e64988b4f..000000000 --- a/Pod/Classes/Provider/Twitter/A0TwitterAuthenticator.h +++ /dev/null @@ -1,44 +0,0 @@ -// A0TwitterAuthenticator.h -// -// Copyright (c) 2014 Auth0 (http://auth0.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import -#import "A0BaseAuthenticator.h" - -/** - * `A0TwitterAuthentication` handles the authentication using Twitter as an indentity provider. In order to obtain a valid token to send to Auth0 API, it uses reverse authentication with the user's login information obtained form iOS Twitter integration or from OAuth Web Flow performed in Safari. - * @deprecated 1.12.0. Moved Twitter authenticator to an independent library called Lock-Twitter (https://github.com/auth0/Lock-Twitter.iOS) - */ -__attribute__ ((deprecated("use A0TwitterAuthenticator from 'Lock-Twitter' library"))) -@interface A0TwitterAuthenticator : A0BaseAuthenticator - -/** - * Returns a new instance with your Twitter's app key & secret. Also sepcifies the callback used to go back from Safari after Oauth Web Flow. - * - * @param key twitter app' API key. - * @param secret twitter app' API secret. - * - * @return a new instance. - */ -+ (A0TwitterAuthenticator *)newAuthenticatorWithKey:(NSString *)key - andSecret:(NSString *)secret; - -@end diff --git a/Pod/Classes/Provider/Twitter/A0TwitterAuthenticator.m b/Pod/Classes/Provider/Twitter/A0TwitterAuthenticator.m deleted file mode 100644 index 29d794819..000000000 --- a/Pod/Classes/Provider/Twitter/A0TwitterAuthenticator.m +++ /dev/null @@ -1,330 +0,0 @@ -// A0TwitterAuthenticator.m -// -// Copyright (c) 2014 Auth0 (http://auth0.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#import "A0TwitterAuthenticator.h" -#import "A0Errors.h" -#import "A0Strategy.h" -#import "A0APIClient.h" -#import "A0Application.h" -#import "A0IdentityProviderCredentials.h" -#import "NSObject+A0APIClientProvider.h" - -#import -#import -#import -#import -#import -#import -#import - -#define kCallbackURLString @"a0%@://%@.auth0.com/authorize" - -@interface A0TwitterAuthenticator () - -@property (strong, nonatomic) A0AuthParameters *parameters; -@property (strong, nonatomic) BDBOAuth1RequestOperationManager *manager; -@property (strong, nonatomic) NSURL *callbackURL; -@property (strong, nonatomic) ACAccountStore *accountStore; -@property (strong, nonatomic) ACAccountType *accountType; -@property (assign, atomic) BOOL authenticating; - -@property (copy, nonatomic) void(^successBlock)(A0UserProfile *profile, A0Token *token); -@property (copy, nonatomic) void(^failureBlock)(NSError *); - -@end - -@implementation A0TwitterAuthenticator - -AUTH0_DYNAMIC_LOGGER_METHODS - -+ (A0TwitterAuthenticator *)newAuthenticatorWithKey:(NSString *)key andSecret:(NSString *)secret { - return [[A0TwitterAuthenticator alloc] initWithKey:key andSecret:secret]; -} - -- (instancetype)initWithKey:(NSString *)key andSecret:(NSString *)secret { - self = [super init]; - if (self) { - _authenticating = NO; - _manager = [[BDBOAuth1RequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://api.twitter.com/"] - consumerKey:key - consumerSecret:secret]; - [_manager deauthorize]; - [TWAPIManager registerTwitterAppKey:key andAppSecret:secret]; - NSDictionary *info = [[NSBundle mainBundle] infoDictionary]; - NSString *clientId = info[@"Auth0ClientId"]; - NSString *callbackURLString = [NSString stringWithFormat:kCallbackURLString, clientId, @"twitter"].lowercaseString; - _callbackURL = [NSURL URLWithString:callbackURLString]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationActiveNotification:) name:UIApplicationDidBecomeActiveNotification object:nil]; - } - return self; -} - -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (void)applicationActiveNotification:(NSNotification *)notification { - if (!self.authenticating) { - if (self.failureBlock) { - self.failureBlock([A0Errors twitterCancelled]); - } - self.successBlock = nil; - self.failureBlock = nil; - } -} - -#pragma mark - A0SocialProviderAuth - -- (NSString *)identifier { - return A0StrategyNameTwitter; -} - -- (void)clearSessions { - self.successBlock = nil; - self.failureBlock = nil; -} - -- (BOOL)handleURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication { - BOOL handled = NO; - A0LogVerbose(@"Received url %@ from source application %@", url, sourceApplication); - if ([url.scheme.lowercaseString isEqualToString:self.callbackURL.scheme.lowercaseString] && [url.host isEqualToString:self.callbackURL.host]) { - handled = YES; - self.authenticating = YES; - NSDictionary *parameters = [NSURL ab_parseURLQueryString:url.query]; - @weakify(self); - if (parameters[@"oauth_token"] && parameters[@"oauth_verifier"]) { - A0LogVerbose(@"Requesting access token from twitter..."); - [self.manager fetchAccessTokenWithPath:@"/oauth/access_token" - method:@"POST" - requestToken:[BDBOAuth1Credential credentialWithQueryString:url.query] - success:^(BDBOAuth1Credential *accessToken) { - @strongify(self); - A0LogDebug(@"Received token %@ with userInfo %@", accessToken.token, accessToken.userInfo); - [self reverseAuthWithNewAccountWithInfo:accessToken]; - } failure:^(NSError *error) { - A0LogError(@"Failed to request access token with error %@", error); - [self executeFailureWithError:error]; - }]; - } else { - A0LogError(@"Twitter OAuth 1.1 flow was cancelled by the user"); - [self executeFailureWithError:[A0Errors twitterCancelled]]; - } - } - return handled; -} - -- (void)authenticateWithParameters:(A0AuthParameters *)parameters success:(void (^)(A0UserProfile *, A0Token *))success failure:(void (^)(NSError *))failure { - self.successBlock = success; - self.failureBlock = failure; - self.accountStore = [[ACAccountStore alloc] init]; - self.accountType = [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter]; - self.parameters = parameters; - A0LogVerbose(@"Starting Twitter authentication..."); - @weakify(self); - if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter]) { - A0LogVerbose(@"Requesting access to iOS Twitter integration for Accounts"); - [self.accountStore requestAccessToAccountsWithType:self.accountType options:nil completion:^(BOOL granted, NSError *error) { - @strongify(self); - if (granted && !error) { - NSArray *accounts = [self.accountStore accountsWithAccountType:self.accountType]; - A0LogDebug(@"Obtained %lu accounts from iOS Twitter integration", (unsigned long)accounts.count); - if (accounts.count > 1) { - A0LogVerbose(@"Asking the user to choose one account from the list..."); - dispatch_async(dispatch_get_main_queue(), ^{ - PSPDFActionSheet *sheet = [[PSPDFActionSheet alloc] initWithTitle:nil]; - for (ACAccount *account in accounts) { - [sheet addButtonWithTitle:[@"@" stringByAppendingString:account.username] block:^(NSInteger buttonIndex) { - A0LogDebug(@"User picked account with screen name @%@", account.username); - [self reverseAuthForAccount:account]; - }]; - } - [sheet setCancelButtonWithTitle:A0LocalizedString(@"Cancel") block:^(NSInteger buttonIndex) { - A0LogDebug(@"User did not pick an account"); - [self executeFailureWithError:[A0Errors twitterCancelled]]; - }]; - [sheet showInView:[[UIApplication sharedApplication] keyWindow]]; - }); - } else { - A0LogVerbose(@"Only one account found, no need for the user to pick one"); - [self reverseAuthForAccount:accounts.firstObject]; - } - } else { - A0LogError(@"Failed to obtain accounts from iOS Twitter integration with error %@", error); - [self executeFailureWithError:[A0Errors twitterAppNotAuthorized]]; - } - }]; - } else { - A0LogVerbose(@"No account was found in iOS Twitter integration. Starting with OAuth web flow..."); - [self.manager deauthorize]; - [self.manager fetchRequestTokenWithPath:@"/oauth/request_token" - method:@"POST" - callbackURL:self.callbackURL - scope:nil - success:^(BDBOAuth1Credential *requestToken) { - A0LogDebug(@"Obtained request token %@ with user info %@", requestToken.token, requestToken.userInfo); - NSString *authURL = [NSString stringWithFormat:@"https://api.twitter.com/oauth/authorize?oauth_token=%@", requestToken.token]; - A0LogVerbose(@"Opening in Safari URL: %@", authURL); - [[UIApplication sharedApplication] openURL:[NSURL URLWithString:authURL]]; - } failure:^(NSError *error) { - A0LogError(@"Failed to obtain request token with error %@", error); - [self executeFailureWithError:error]; - }]; - } -} - -#pragma mark - Twitter Reverse Auth -- (void)reverseAuthWithNewAccountWithInfo:(BDBOAuth1Credential *)info { - ACAccountCredential * credential = [[ACAccountCredential alloc] initWithOAuthToken:info.token tokenSecret:info.secret]; - ACAccount * account = [[ACAccount alloc] initWithAccountType:self.accountType]; - account.accountType = self.accountType; - account.credential = credential; - account.username = [NSString stringWithFormat:@"@%@", info.userInfo[@"screen_name"]]; - - A0LogDebug(@"About to save Twitter account @%@ in iOS Twitter integration.", account.username); - @weakify(self); - [self.accountStore requestAccessToAccountsWithType:self.accountType options:nil completion:^(BOOL granted, NSError *error) { - if (granted) { - A0LogVerbose(@"Saving new twitter account in iOS..."); - [self.accountStore saveAccount:account withCompletionHandler:^(BOOL success, NSError *error) { - @strongify(self); - if (success && !error) { - ACAccount *account = [[self.accountStore accountsWithAccountType:self.accountType] firstObject]; - A0LogDebug(@"Saved twitter account @%@", account.username); - [self reverseAuthForAccount:account]; - } else { - A0LogError(@"Failed to save twitter account with error %@", error); - [self executeFailureWithError:error]; - } - }]; - } - else { - A0LogError(@"Failed to access iOS Twitter integration with error %@", error); - [self executeFailureWithError:[A0Errors twitterAppNotAuthorized]]; - } - }]; -} - -- (void)reverseAuthForAccount:(ACAccount *)account { - account.accountType = self.accountType; - A0LogVerbose(@"Starting reverse authentication with Twitter account @%@...", account.username); - - @weakify(self); - [TWAPIManager performReverseAuthForAccount:account withHandler:^(NSData *responseData, NSError *error) { - @strongify(self); - if (error || !responseData) { - A0LogError(@"Failed to perform reverse auth with error %@", error); - [self executeFailureWithError:error]; - } else { - NSError *payloadError; - NSDictionary *response = [A0TwitterAuthenticator payloadFromResponseData:responseData error:&payloadError]; - if (!payloadError) { - A0LogDebug(@"Reverse Auth successful. Received payload %@", response); - NSString *oauthToken = response[@"oauth_token"]; - NSString *oauthTokenSecret = response[@"oauth_token_secret"]; - NSString *userId = response[@"user_id"]; - NSDictionary *extraInfo = @{ - A0StrategySocialTokenParameter: oauthToken, - A0StrategySocialTokenSecretParameter: oauthTokenSecret, - A0StrategySocialUserIdParameter: userId, - }; - A0IdentityProviderCredentials *credentials = [[A0IdentityProviderCredentials alloc] initWithAccessToken:response[@"oauth_token"] extraInfo:extraInfo]; - A0LogDebug(@"Successful Twitter auth with credentials %@", credentials); - [self executeSuccessWithCredentials:credentials parameters:self.parameters]; - } else { - [self executeFailureWithError:payloadError]; - } - } - }]; -} - -+ (NSDictionary *)payloadFromResponseData:(NSData *)responseData error:(NSError **)error { - NSString *responseStr = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]; - BOOL failed = responseStr && [responseStr rangeOfString:@" - // - // Client is not permitted to perform this action - // - BOOL error87 = responseStr && [responseStr rangeOfString:@""].location != NSNotFound; - // - // - // Error processing your OAuth request: invalid signature or token - // - BOOL error89 = responseStr && [responseStr rangeOfString:@""].location != NSNotFound; - if (error != NULL) { - *error = [A0Errors twitterAppNotAuthorized]; - if (error87) { - A0LogError(@"Twitter app not configured in Auth0"); - *error = [A0Errors twitterNotConfigured]; - } - if (error89) { - A0LogError(@"Twitter Account in iOS is invalid. Re-enter credentials in Settings > Twitter"); - *error = [A0Errors twitterInvalidAccount]; - } - } - - } else { - payload = [NSURL ab_parseURLQueryString:responseStr]; - NSString *oauthToken = payload[@"oauth_token"]; - NSString *oauthTokenSecret = payload[@"oauth_token_secret"]; - NSString *userId = payload[@"user_id"]; - if (!(oauthToken || oauthTokenSecret || userId)) { - A0LogError(@"Reverse auth didnt return all credential info (token, token_secret & user_id)"); - if (error != NULL) { - *error = [A0Errors twitterAppNotAuthorized]; - } - } - } - return payload; -} - -#pragma mark - Block handling - -- (void)executeSuccessWithCredentials:(A0IdentityProviderCredentials *)credentials parameters:(A0AuthParameters *)parameters { - A0APIClient *client = [self a0_apiClientFromProvider:self.clientProvider]; - [client authenticateWithSocialConnectionName:self.identifier - credentials:credentials - parameters:parameters - success:self.successBlock - failure:self.failureBlock]; - self.authenticating = NO; - self.successBlock = nil; - self.failureBlock = nil; - self.accountStore = nil; - self.accountType = nil; -} - -- (void)executeFailureWithError:(NSError *)error { - dispatch_async(dispatch_get_main_queue(), ^{ - if (self.failureBlock) { - self.failureBlock(error); - } - self.authenticating = NO; - self.successBlock = nil; - self.failureBlock = nil; - self.accountStore = nil; - self.accountType = nil; - }); -} -@end