diff --git a/src/FBSession+Protected.h b/src/FBSession+Protected.h index 8acfa367ac..eabf38c78f 100644 --- a/src/FBSession+Protected.h +++ b/src/FBSession+Protected.h @@ -23,12 +23,14 @@ - (BOOL)transitionToState:(FBSessionState)state andUpdateToken:(NSString*)token andExpirationDate:(NSDate*)date - shouldCache:(BOOL)shouldCache; + shouldCache:(BOOL)shouldCache + loginType:(FBSessionLoginType)loginType; - (void)transitionAndCallHandlerWithState:(FBSessionState)status error:(NSError*)error token:(NSString*)token expirationDate:(NSDate*)date - shouldCache:(BOOL)shouldCache; + shouldCache:(BOOL)shouldCache + loginType:(FBSessionLoginType)loginType; - (void)authorizeWithPermissions:(NSArray*)permissions behavior:(FBSessionLoginBehavior)behavior defaultAudience:(FBSessionDefaultAudience)audience diff --git a/src/FBSession.h b/src/FBSession.h index 9daa07b059..6b8f8bb74b 100644 --- a/src/FBSession.h +++ b/src/FBSession.h @@ -131,6 +131,34 @@ typedef enum { FBSessionDefaultAudienceEveryone = 30, } FBSessionDefaultAudience; +/*! + @typedef FBSessionLoginType enum + + @abstract + Used as the type of the loginType property in order to specify what underlying technology was used to + login the user. + + @discussion + The FBSession object is an abstraction over five distinct mechanisms. This enum allows an application + to test for the mechanism used by a particular instance of FBSession. Usually the mechanism used for a + given login does not matter, however for certain capabilities, the type of login can impact the behavior + of other Facebook functionality. + */ +typedef enum { + /*! A login type has not yet been established */ + FBSessionLoginTypeNone = 0, + /*! A system integrated account was used to log the user into the application */ + FBSessionLoginTypeSystemAccount = 1, + /*! The Facebook native application was used to log the user into the application */ + FBSessionLoginTypeFacebookApplication = 2, + /*! Safari was used to log the user into the application */ + FBSessionLoginTypeFacebookViaSafari = 3, + /*! A web view was used to log the user into the application */ + FBSessionLoginTypeWebView = 4, + /*! A test user was used to create an open session */ + FBSessionLoginTypeTestUser = 5, +} FBSessionLoginType; + /*! @typedef @@ -276,6 +304,9 @@ typedef void (^FBSessionReauthorizeResultHandler)(FBSession *session, /*! @abstract The permissions granted to the access token during the authentication flow. */ @property(readonly, copy) NSArray *permissions; +/*! @abstract Specifies the login type used to authenticate the user. */ +@property(readonly) FBSessionLoginType loginType; + /*! @methodgroup Instance methods */ diff --git a/src/FBSession.m b/src/FBSession.m index 76e51d8cd6..2e04d483b4 100644 --- a/src/FBSession.m +++ b/src/FBSession.m @@ -87,8 +87,7 @@ @interface FBSession () { BOOL _isInStateTransition; BOOL _isFacebookLoginToken; BOOL _isOSIntegratedFacebookLoginToken; - BOOL _isPendingReauthorization; - BOOL _isPendingOpenUrlCallback; + FBSessionLoginType _loginTypeOfPendingOpenUrlCallback; FBSessionDefaultAudience _defaultDefaultAudience; } @@ -99,6 +98,7 @@ @interface FBSession () { @property(readwrite, copy) NSString *accessToken; @property(readwrite, copy) NSDate *expirationDate; @property(readwrite, copy) NSArray *permissions; +@property(readwrite) FBSessionLoginType loginType; // private properties @property(readwrite, retain) FBSessionTokenCachingStrategy *tokenCachingStrategy; @@ -125,7 +125,8 @@ - (void)authorizeUsingSystemAccountStore:(id)accountStore defaultAudience:(FBSessionDefaultAudience)defaultAudience isReauthorize:(BOOL)isReauthorize; - (BOOL)handleOpenURLPreOpen:(NSDictionary*)parameters - accessToken:(NSString*)accessToken; + accessToken:(NSString*)accessToken + loginType:(FBSessionLoginType)loginType; - (BOOL)handleOpenURLReauthorize:(NSDictionary*)parameters accessToken:(NSString*)accessToken; - (void)completeReauthorizeWithAccessToken:(NSString*)accessToken @@ -166,6 +167,7 @@ @implementation FBSession : NSObject // public properties appID = _appID, permissions = _permissions, + loginType = _loginType, // following properties use manual KVO -- changes to names require // changes to static property name variables (e.g. FBisOpenPropertyName) @@ -248,13 +250,13 @@ - (id)initWithAppID:(NSString*)appID // additional setup _isInStateTransition = NO; _isFacebookLoginToken = NO; - _isPendingReauthorization = NO; - _isPendingOpenUrlCallback = NO; + _loginTypeOfPendingOpenUrlCallback = FBSessionLoginTypeNone; _isOSIntegratedFacebookLoginToken = NO; _defaultDefaultAudience = defaultAudience; self.attemptedRefreshDate = [NSDate distantPast]; self.refreshDate = nil; self.state = FBSessionStateCreated; + self.loginType = FBSessionLoginTypeNone; self.affinitizedThread = [NSThread currentThread]; [FBLogger registerCurrentTime:FBLoggingBehaviorPerformanceCharacteristics withTag:self]; @@ -281,13 +283,15 @@ - (id)initWithAppID:(NSString*)appID // if we have cached an optional refresh date or Facebook Login indicator, pick them up here self.refreshDate = [tokenInfo objectForKey:FBTokenInformationRefreshDateKey]; _isFacebookLoginToken = [[tokenInfo objectForKey:FBTokenInformationIsFacebookLoginKey] boolValue]; - _isOSIntegratedFacebookLoginToken = [[tokenInfo objectForKey:FBTokenInformationIsOSIntegratedFacebookLoginKey] boolValue]; + FBSessionLoginType loginType = [[tokenInfo objectForKey:FBTokenInformationLoginTypeLoginKey] intValue]; + _isOSIntegratedFacebookLoginToken = loginType == FBSessionLoginTypeSystemAccount; // set the state and token info [self transitionToState:FBSessionStateCreatedTokenLoaded andUpdateToken:cachedToken andExpirationDate:cachedTokenExpirationDate - shouldCache:NO]; + shouldCache:NO + loginType:loginType]; } else { // else this token is expired and should be cleared from cache [tokenCachingStrategy clearToken]; @@ -346,7 +350,8 @@ - (void)openWithBehavior:(FBSessionLoginBehavior)behavior [self transitionToState:FBSessionStateCreatedOpening andUpdateToken:nil andExpirationDate:nil - shouldCache:NO]; + shouldCache:NO + loginType:FBSessionLoginTypeNone]; [self authorizeWithPermissions:self.permissions behavior:behavior @@ -361,7 +366,8 @@ - (void)openWithBehavior:(FBSessionLoginBehavior)behavior error:nil token:nil expirationDate:nil - shouldCache:NO]; + shouldCache:NO + loginType:FBSessionLoginTypeNone]; } } @@ -408,7 +414,8 @@ - (void)close { error:nil token:nil expirationDate:nil - shouldCache:NO]; + shouldCache:NO + loginType:FBSessionLoginTypeNone]; } - (void)closeAndClearTokenInformation { @@ -423,7 +430,8 @@ - (void)closeAndClearTokenInformation { error:nil token:nil expirationDate:nil - shouldCache:NO]; + shouldCache:NO + loginType:FBSessionLoginTypeNone]; } } @@ -434,7 +442,8 @@ - (BOOL)handleOpenURL:(NSURL *)url { if (![[url absoluteString] hasPrefix:self.appBaseUrl]) { return NO; } - _isPendingOpenUrlCallback = NO; + FBSessionLoginType loginType = _loginTypeOfPendingOpenUrlCallback; + _loginTypeOfPendingOpenUrlCallback = FBSessionLoginTypeNone; // version 3.2.3 of the Facebook app encodes the parameters in the query but // version 3.3 and above encode the parameters in the fragment; check first for @@ -450,7 +459,8 @@ - (BOOL)handleOpenURL:(NSURL *)url { switch (self.state) { case FBSessionStateCreatedOpening: return [self handleOpenURLPreOpen:params - accessToken:accessToken]; + accessToken:accessToken + loginType:loginType]; case FBSessionStateOpen: case FBSessionStateOpenTokenExtended: return [self handleOpenURLReauthorize:params @@ -474,12 +484,11 @@ - (void)handleDidBecomeActive{ return; } - if (_isPendingOpenUrlCallback){ + if (_loginTypeOfPendingOpenUrlCallback != FBSessionLoginTypeNone){ if (state == FBSessionStateCreatedOpening){ //if we're here, user had declined a fast app switch login. [FBSession.activeSession close]; - } - else if (_isPendingReauthorization){ + } else { //this means the user declined a 'reauthorization' so we need // to clean out the in-flight request. NSError *error = [FBSession errorLoginFailedWithReason:FBErrorReauthorizeFailedReasonUserCancelled @@ -487,7 +496,7 @@ - (void)handleDidBecomeActive{ innerError:nil]; [self callReauthorizeHandlerAndClearState:error]; } - _isPendingOpenUrlCallback = NO; + _loginTypeOfPendingOpenUrlCallback = FBSessionLoginTypeNone; } } @@ -625,7 +634,7 @@ - (BOOL)transitionToState:(FBSessionState)state andUpdateToken:(NSString*)token andExpirationDate:(NSDate*)date shouldCache:(BOOL)shouldCache -{ + loginType:(FBSessionLoginType)loginType { // is this a valid transition? BOOL isValidTransition; @@ -642,6 +651,11 @@ - (BOOL)transitionToState:(FBSessionState)state statePrior == FBSessionStateCreatedTokenLoaded || statePrior == FBSessionStateCreatedOpening ); + // if we are just about to transition to open, and the caller + // wants to specify a login type, then we set the type + if (isValidTransition && loginType != FBSessionLoginTypeNone) { + self.loginType = loginType; + } break; case FBSessionStateCreatedOpening: case FBSessionStateCreatedTokenLoaded: @@ -755,9 +769,7 @@ - (BOOL)transitionToState:(FBSessionState)state [tokenInfo setObject:[NSNumber numberWithBool:YES] forKey:FBTokenInformationIsFacebookLoginKey]; } - if (_isOSIntegratedFacebookLoginToken) { - [tokenInfo setObject:[NSNumber numberWithBool:YES] forKey:FBTokenInformationIsOSIntegratedFacebookLoginKey]; - } + [tokenInfo setObject:[NSNumber numberWithInt:self.loginType] forKey:FBTokenInformationLoginTypeLoginKey]; if (self.permissions) { [tokenInfo setObject:self.permissions forKey:FBTokenInformationPermissionsKey]; @@ -890,7 +902,7 @@ - (void)authorizeWithPermissions:(NSArray*)permissions NSString *urlPrefix = [NSString stringWithFormat:@"%@://%@", scheme, FBAuthURLPath]; NSString *fbAppUrl = [FBRequest serializeURL:urlPrefix params:params]; - _isPendingOpenUrlCallback = YES; + _loginTypeOfPendingOpenUrlCallback = FBSessionLoginTypeFacebookApplication; didAuthNWithSystemAccount = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:fbAppUrl]]; } @@ -899,12 +911,12 @@ - (void)authorizeWithPermissions:(NSArray*)permissions [params setValue:nextUrl forKey:@"redirect_uri"]; NSString *fbAppUrl = [FBRequest serializeURL:loginDialogURL params:params]; - _isPendingOpenUrlCallback = YES; + _loginTypeOfPendingOpenUrlCallback = FBSessionLoginTypeFacebookViaSafari; didAuthNWithSystemAccount = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:fbAppUrl]]; } //In case openURL failed, make sure we don't still expect a openURL callback. if (!didAuthNWithSystemAccount){ - _isPendingOpenUrlCallback = NO; + _loginTypeOfPendingOpenUrlCallback = FBSessionLoginTypeNone; } } @@ -928,7 +940,8 @@ - (void)authorizeWithPermissions:(NSArray*)permissions error:error token:nil expirationDate:nil - shouldCache:NO]; + shouldCache:NO + loginType:FBSessionLoginTypeNone]; } } } @@ -1018,7 +1031,8 @@ - (void)authorizeUsingSystemAccountStore:(ACAccountStore*)accountStore token:oauthToken // BUG: we need a means for fetching the expiration date of the token expirationDate:[NSDate distantFuture] - shouldCache:YES]; + shouldCache:YES + loginType:FBSessionLoginTypeSystemAccount]; } else if (isUntosedDevice) { // even when OS integrated auth is possible we use native-app/safari // login if the user has not signed on to Facebook via the OS @@ -1040,7 +1054,8 @@ - (void)authorizeUsingSystemAccountStore:(ACAccountStore*)accountStore error:err token:nil expirationDate:nil - shouldCache:NO]; + shouldCache:NO + loginType:FBSessionLoginTypeNone]; } } else { // reauth case if (oauthToken) { @@ -1073,7 +1088,8 @@ - (void)authorizeUsingSystemAccountStore:(ACAccountStore*)accountStore } - (BOOL)handleOpenURLPreOpen:(NSDictionary*)parameters - accessToken:(NSString*)accessToken { + accessToken:(NSString*)accessToken + loginType:(FBSessionLoginType)loginType { // if the URL doesn't contain the access token, an error has occurred. if (!accessToken) { NSString *errorReason = [parameters objectForKey:@"error"]; @@ -1118,7 +1134,8 @@ - (BOOL)handleOpenURLPreOpen:(NSDictionary*)parameters error:error token:nil expirationDate:nil - shouldCache:NO]; + shouldCache:NO + loginType:FBSessionLoginTypeNone]; } else { // we have an access token, so parse the expiration date. @@ -1139,7 +1156,8 @@ - (BOOL)handleOpenURLPreOpen:(NSDictionary*)parameters error:nil token:accessToken expirationDate:expirationDate - shouldCache:YES]; + shouldCache:YES + loginType:loginType]; } return YES; } @@ -1277,7 +1295,6 @@ - (void)reauthorizeWithPermissions:(NSArray*)permissions // setup handler and permissions and perform the actual reauthorize self.reauthorizePermissions = permissions; self.reauthorizeHandler = handler; - _isPendingReauthorization = YES; [self authorizeWithPermissions:permissions behavior:behavior defaultAudience:audience @@ -1299,7 +1316,8 @@ - (void)completeReauthorizeWithAccessToken:(NSString*)accessToken error:nil token:accessToken expirationDate:expirationDate - shouldCache:YES]; + shouldCache:YES + loginType:FBSessionLoginTypeNone]; // no error, ack a completed permission upgrade [self callReauthorizeHandlerAndClearState:nil]; @@ -1315,7 +1333,8 @@ - (void)refreshAccessToken:(NSString*)token error:nil token:token ? token : self.accessToken expirationDate:expireDate - shouldCache:YES]; + shouldCache:YES + loginType:FBSessionLoginTypeNone]; } - (BOOL)shouldExtendAccessToken { @@ -1344,7 +1363,8 @@ - (void)fbDialogLogin:(NSString *)accessToken expirationDate:(NSDate *)expiratio error:nil token:accessToken expirationDate:expirationDate - shouldCache:YES]; + shouldCache:YES + loginType:FBSessionLoginTypeWebView]; } // core handler for inline UX flow @@ -1366,7 +1386,8 @@ - (void)fbDialogNotLogin:(BOOL)cancelled { error:error token:nil expirationDate:nil - shouldCache:NO]; + shouldCache:NO + loginType:FBSessionLoginTypeNone]; } // private helpers @@ -1376,14 +1397,16 @@ - (void)transitionAndCallHandlerWithState:(FBSessionState)status error:(NSError*)error token:(NSString*)token expirationDate:(NSDate*)date - shouldCache:(BOOL)shouldCache { + shouldCache:(BOOL)shouldCache + loginType:(FBSessionLoginType)loginType { // lets get the state transition out of the way BOOL didTransition = [self transitionToState:status andUpdateToken:token andExpirationDate:date - shouldCache:shouldCache]; + shouldCache:shouldCache + loginType:loginType]; // if we are given a handler, we promise to call it once per transition from open to close @@ -1418,7 +1441,6 @@ - (void)transitionAndCallHandlerWithState:(FBSessionState)status } - (void)callReauthorizeHandlerAndClearState:(NSError*)error { - _isPendingReauthorization = NO; // clear state and call handler FBSessionReauthorizeResultHandler reauthorizeHandler = [self.reauthorizeHandler retain]; diff --git a/src/FBSessionTokenCachingStrategy.h b/src/FBSessionTokenCachingStrategy.h index 7f15b2fa69..ded1a6829b 100644 --- a/src/FBSessionTokenCachingStrategy.h +++ b/src/FBSessionTokenCachingStrategy.h @@ -115,7 +115,7 @@ extern NSString *const FBTokenInformationUserFBIDKey; extern NSString *const FBTokenInformationIsFacebookLoginKey; // The key to use with token information dictionaries to determine whether the token was fetched via the OS -extern NSString *const FBTokenInformationIsOSIntegratedFacebookLoginKey; +extern NSString *const FBTokenInformationLoginTypeLoginKey; // The key to use with token information dictionaries to get the latest known permissions extern NSString *const FBTokenInformationPermissionsKey; \ No newline at end of file diff --git a/src/FBSessionTokenCachingStrategy.m b/src/FBSessionTokenCachingStrategy.m index 04fb1d5e23..d798c9fb9f 100644 --- a/src/FBSessionTokenCachingStrategy.m +++ b/src/FBSessionTokenCachingStrategy.m @@ -24,7 +24,7 @@ NSString *const FBTokenInformationRefreshDateKey = @"com.facebook.sdk:TokenInformationRefreshDateKey"; NSString *const FBTokenInformationUserFBIDKey = @"com.facebook.sdk:TokenInformationUserFBIDKey"; NSString *const FBTokenInformationIsFacebookLoginKey = @"com.facebook.sdk:TokenInformationIsFacebookLoginKey"; -NSString *const FBTokenInformationIsOSIntegratedFacebookLoginKey = @"com.facebook.sdk:TokenInformationIsOSIntegratedFacebookLoginKey"; +NSString *const FBTokenInformationLoginTypeLoginKey = @"com.facebook.sdk:TokenInformationLoginTypeLoginKey"; NSString *const FBTokenInformationPermissionsKey = @"com.facebook.sdk:TokenInformationPermissionsKey"; @implementation FBSessionTokenCachingStrategy { diff --git a/src/FBTestSession.m b/src/FBTestSession.m index cc5f093a3c..70dc317c14 100644 --- a/src/FBTestSession.m +++ b/src/FBTestSession.m @@ -228,7 +228,8 @@ - (void)createNewTestUser error:error token:nil expirationDate:nil - shouldCache:NO]; + shouldCache:NO + loginType:FBSessionLoginTypeNone]; } }]; } @@ -239,7 +240,8 @@ - (void)transitionToOpenWithToken:(NSString*)token error:nil token:token expirationDate:[NSDate distantFuture] - shouldCache:NO]; + shouldCache:NO + loginType:FBSessionLoginTypeTestUser]; } // We raise exceptions when things go wrong here, because this is intended for use only @@ -411,14 +413,15 @@ - (BOOL)transitionToState:(FBSessionState)state andUpdateToken:(NSString*)token andExpirationDate:(NSDate*)date shouldCache:(BOOL)shouldCache -{ + loginType:(FBSessionLoginType)loginType { // in case we need these after the transition NSString *userID = self.testUserID; BOOL didTransition = [super transitionToState:state andUpdateToken:token andExpirationDate:date - shouldCache:shouldCache]; + shouldCache:shouldCache + loginType:loginType]; if (didTransition && FB_ISSESSIONSTATETERMINAL(self.state)) { if (self.mode == FBTestSessionModePrivate) { diff --git a/src/facebook-ios-sdk.xcodeproj/project.pbxproj b/src/facebook-ios-sdk.xcodeproj/project.pbxproj index 93355cdd28..d08a754f46 100644 --- a/src/facebook-ios-sdk.xcodeproj/project.pbxproj +++ b/src/facebook-ios-sdk.xcodeproj/project.pbxproj @@ -348,7 +348,6 @@ 0867D691FE84028FC02AAC07 /* facebook-ios-sdk */ = { isa = PBXGroup; children = ( - 85A928981611272D008699F1 /* Social.framework */, 08FB77AEFE84172EC02AAC07 /* FacebookSDK */, 32C88DFF0371C24200C91783 /* Other Sources */, B9CBC54015254CAE0036AA71 /* FacebookSDKTests */, @@ -362,6 +361,7 @@ isa = PBXGroup; children = ( 845857A116096ED000CC89E5 /* Accounts.framework */, + 85A928981611272D008699F1 /* Social.framework */, B9CC137215266B9000443948 /* QuartzCore.framework */, B9CBC53715254AC50036AA71 /* libsqlite3.dylib */, B9CBC53115253F6D0036AA71 /* SenTestingKit.framework */, @@ -422,6 +422,8 @@ AEA93B0711D5293B000A4545 /* FBLoginDialog.m */, 840F658B159B3A47005D41AA /* FBLoginView.h */, 840F658E159B3A64005D41AA /* FBLoginView.m */, + 85A9288E1611187F008699F1 /* FBNativeDialogs.h */, + 85A9288F1611187F008699F1 /* FBNativeDialogs.m */, 8409694B1541F41100479AD9 /* FBOpenGraphAction.h */, 84D0A6571581A1C000A2FA5E /* FBPlacePickerCacheDescriptor.h */, 84D0A6551581A1A600A2FA5E /* FBPlacePickerCacheDescriptor.m */, @@ -448,8 +450,6 @@ DDB7C34A15A6181100C8DCE6 /* FBSettings.h */, 2A68590515C1E37E001D4EDD /* FBSettings+Internal.h */, DDB7C34B15A6181100C8DCE6 /* FBSettings.m */, - 85A9288E1611187F008699F1 /* FBNativeDialogs.h */, - 85A9288F1611187F008699F1 /* FBNativeDialogs.m */, 8525A5B8156F2049009F6F3F /* FBTestSession.h */, 85F29E9315785D72001F0531 /* FBTestSession+Internal.h */, 8525A5B9156F2049009F6F3F /* FBTestSession.m */,