Skip to content

Commit 7eb4de5

Browse files
committed
Add support for Facebook Limited Login.
1 parent 9080f99 commit 7eb4de5

File tree

2 files changed

+121
-26
lines changed

2 files changed

+121
-26
lines changed

FirebaseFacebookAuthUI/Sources/FUIFacebookAuth.m

Lines changed: 116 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@
2020
#import <FirebaseAuth/FirebaseAuth.h>
2121

2222
#if SWIFT_PACKAGE
23+
@import CommonCrypto;
2324
@import FBSDKCoreKit;
2425
@import FBSDKLoginKit;
2526
#else
2627
#import <FBSDKCoreKit/FBSDKCoreKit.h>
2728
#import <FBSDKLoginKit/FBSDKLoginKit.h>
29+
#import <CommonCrypto/CommonCrypto.h>
2830
#endif // SWIFT_PACKAGE
2931

3032
/** @var kTableName
@@ -86,6 +88,11 @@ @implementation FUIFacebookAuth {
8688
@brief The email address associated with this account.
8789
*/
8890
NSString *_email;
91+
92+
/** @var _currentNonce
93+
@brief The current nonce for a Facebook Limited Login sign-in attempt.
94+
*/
95+
NSString *_currentNonce;
8996
}
9097

9198
+ (NSBundle *)bundle {
@@ -191,29 +198,59 @@ - (void)signInWithDefaultValue:(nullable NSString *)defaultValue
191198
return;
192199
}
193200

194-
[_loginManager logInWithPermissions:_scopes
195-
fromViewController:presentingViewController
196-
handler:^(FBSDKLoginManagerLoginResult *result,
197-
NSError *error) {
198-
if (error) {
199-
NSError *newError =
200-
[FUIAuthErrorUtils providerErrorWithUnderlyingError:error
201-
providerID:FIRFacebookAuthProviderID];
202-
[self completeSignInFlowWithAccessToken:nil error:newError];
203-
} else if (result.isCancelled) {
204-
NSError *newError = [FUIAuthErrorUtils userCancelledSignInError];
205-
[self completeSignInFlowWithAccessToken:nil error:newError];
206-
} else {
207-
// Retrieve email.
208-
[[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:@{ @"fields" : @"email" }] startWithCompletion:^(id<FBSDKGraphRequestConnecting> connection,
209-
id result,
210-
NSError *error) {
211-
self->_email = result[@"email"];
212-
}];
213-
[self completeSignInFlowWithAccessToken:result.token.tokenString
214-
error:nil];
215-
}
216-
}];
201+
if (self.useLimitedLogin) {
202+
// Facebook Limited Login
203+
NSString *nonce = [self randomNonce];
204+
self->_currentNonce = nonce;
205+
FBSDKLoginConfiguration *configuration =
206+
[[FBSDKLoginConfiguration alloc] initWithPermissions:_scopes
207+
tracking:FBSDKLoginTrackingLimited
208+
nonce:[self stringBySha256HashingString:nonce]];
209+
[_loginManager logInFromViewController:presentingViewController
210+
configuration:configuration
211+
completion:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
212+
if (error) {
213+
NSError *newError =
214+
[FUIAuthErrorUtils providerErrorWithUnderlyingError:error
215+
providerID:FIRFacebookAuthProviderID];
216+
[self completeSignInFlowWithAccessToken:nil idToken:nil error:newError];
217+
} else if (result.isCancelled) {
218+
NSError *newError = [FUIAuthErrorUtils userCancelledSignInError];
219+
[self completeSignInFlowWithAccessToken:nil idToken:nil error:newError];
220+
} else {
221+
self->_email = FBSDKProfile.currentProfile.email;
222+
NSString *idToken = FBSDKAuthenticationToken.currentAuthenticationToken.tokenString;
223+
[self completeSignInFlowWithAccessToken:nil idToken:idToken error:nil];
224+
}
225+
}];
226+
} else {
227+
[_loginManager logInWithPermissions:_scopes
228+
fromViewController:presentingViewController
229+
handler:^(FBSDKLoginManagerLoginResult *result,
230+
NSError *error) {
231+
if (error) {
232+
NSError *newError =
233+
[FUIAuthErrorUtils providerErrorWithUnderlyingError:error
234+
providerID:FIRFacebookAuthProviderID];
235+
[self completeSignInFlowWithAccessToken:nil idToken:nil error:newError];
236+
} else if (result.isCancelled) {
237+
NSError *newError = [FUIAuthErrorUtils userCancelledSignInError];
238+
[self completeSignInFlowWithAccessToken:nil idToken:nil error:newError];
239+
} else {
240+
// Retrieve email.
241+
[[[FBSDKGraphRequest alloc] initWithGraphPath:@"me"
242+
parameters:@{ @"fields" : @"email" }]
243+
startWithCompletion:^(id<FBSDKGraphRequestConnecting> connection,
244+
id result,
245+
NSError *error) {
246+
self->_email = result[@"email"];
247+
}];
248+
[self completeSignInFlowWithAccessToken:result.token.tokenString
249+
idToken:nil
250+
error:nil];
251+
}
252+
}];
253+
}
217254
}
218255

219256
- (void)signInWithOAuthProvider:(FIROAuthProvider *)oauthProvider
@@ -272,18 +309,28 @@ - (BOOL)handleOpenURL:(NSURL *)URL sourceApplication:(NSString *)sourceApplicati
272309
/** @fn completeSignInFlowWithAccessToken:error:
273310
@brief Called with the result of a Facebook sign-in attempt. Invokes and clears any pending
274311
sign in callback block.
275-
@param accessToken The Facebook access token, if successful.
312+
@param accessToken The Facebook access token, if the Facebook sign-in attempt with tracking enabled is successful.
313+
@param idToken The Facebook ID token, if the Facebook Limited Login attempt is successful.
276314
@param error An error which occurred during the sign-in attempt.
277315
*/
278316
- (void)completeSignInFlowWithAccessToken:(nullable NSString *)accessToken
317+
idToken:(nullable NSString *)idToken
279318
error:(nullable NSError *)error {
280319
if (error) {
281320
[self callbackWithCredential:nil error:error result:nil];
282321
return;
283322
}
284-
// Assume accessToken cannot be nil if there's no error.
323+
FIRAuthCredential *credential;
324+
if (idToken) {
325+
NSString *rawNonce = self->_currentNonce;
326+
credential = [FIROAuthProvider credentialWithProviderID:FIRFacebookAuthProviderID
327+
IDToken:idToken
328+
rawNonce:rawNonce];
329+
} else {
330+
// Assume accessToken cannot be nil if there's no error and idToken is nil.
285331
NSString *_Nonnull token = (id _Nonnull)accessToken;
286-
FIRAuthCredential *credential = [FIRFacebookAuthProvider credentialWithAccessToken:token];
332+
credential = [FIRFacebookAuthProvider credentialWithAccessToken:token];
333+
}
287334
UIActivityIndicatorView *activityView =
288335
[FUIAuthBaseViewController addActivityIndicator:_presentingViewController.view];
289336
[activityView startAnimating];
@@ -347,4 +394,47 @@ - (FBSDKLoginManager *)createLoginManager {
347394
return [[FBSDKLoginManager alloc] init];
348395
}
349396

397+
- (NSString *)randomNonce {
398+
NSString *characterSet = @"0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._";
399+
NSMutableString *result = [NSMutableString string];
400+
NSInteger remainingLength = 32;
401+
402+
while (remainingLength > 0) {
403+
NSMutableArray *randoms = [NSMutableArray arrayWithCapacity:16];
404+
for (NSInteger i = 0; i < 16; i++) {
405+
uint8_t random = 0;
406+
int errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random);
407+
NSAssert(errorCode == errSecSuccess, @"Unable to generate nonce: OSStatus %i", errorCode);
408+
409+
[randoms addObject:@(random)];
410+
}
411+
412+
for (NSNumber *random in randoms) {
413+
if (remainingLength == 0) {
414+
break;
415+
}
416+
417+
if (random.unsignedIntValue < characterSet.length) {
418+
unichar character = [characterSet characterAtIndex:random.unsignedIntValue];
419+
[result appendFormat:@"%C", character];
420+
remainingLength--;
421+
}
422+
}
423+
}
424+
425+
return result;
426+
}
427+
428+
- (NSString *)stringBySha256HashingString:(NSString *)input {
429+
const char *string = [input UTF8String];
430+
unsigned char result[CC_SHA256_DIGEST_LENGTH];
431+
CC_SHA256(string, (CC_LONG)strlen(string), result);
432+
433+
NSMutableString *hashed = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
434+
for (NSInteger i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
435+
[hashed appendFormat:@"%02x", result[i]];
436+
}
437+
return hashed;
438+
}
439+
350440
@end

FirebaseFacebookAuthUI/Sources/Public/FirebaseFacebookAuthUI/FUIFacebookAuth.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ NS_ASSUME_NONNULL_BEGIN
4848
*/
4949
@property(nonatomic, readwrite) FUIButtonAlignment buttonAlignment;
5050

51+
/** @property useLimitedLogin
52+
@brief Whether or not Facebook Login should use Limited Login mode.
53+
*/
54+
@property(nonatomic, assign) BOOL useLimitedLogin;
55+
5156
/** @fn initWithAuthUI
5257
@brief Convenience initializer. Uses a default permission of `@[ "email" ]`.
5358
@param authUI The @c FUIAuth instance that manages this provider.

0 commit comments

Comments
 (0)