|
20 | 20 | #import <FirebaseAuth/FirebaseAuth.h> |
21 | 21 |
|
22 | 22 | #if SWIFT_PACKAGE |
| 23 | +@import CommonCrypto; |
23 | 24 | @import FBSDKCoreKit; |
24 | 25 | @import FBSDKLoginKit; |
25 | 26 | #else |
26 | 27 | #import <FBSDKCoreKit/FBSDKCoreKit.h> |
27 | 28 | #import <FBSDKLoginKit/FBSDKLoginKit.h> |
| 29 | +#import <CommonCrypto/CommonCrypto.h> |
28 | 30 | #endif // SWIFT_PACKAGE |
29 | 31 |
|
30 | 32 | /** @var kTableName |
@@ -86,6 +88,11 @@ @implementation FUIFacebookAuth { |
86 | 88 | @brief The email address associated with this account. |
87 | 89 | */ |
88 | 90 | NSString *_email; |
| 91 | + |
| 92 | + /** @var _currentNonce |
| 93 | + @brief The current nonce for a Facebook Limited Login sign-in attempt. |
| 94 | + */ |
| 95 | + NSString *_currentNonce; |
89 | 96 | } |
90 | 97 |
|
91 | 98 | + (NSBundle *)bundle { |
@@ -191,29 +198,59 @@ - (void)signInWithDefaultValue:(nullable NSString *)defaultValue |
191 | 198 | return; |
192 | 199 | } |
193 | 200 |
|
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 | + } |
217 | 254 | } |
218 | 255 |
|
219 | 256 | - (void)signInWithOAuthProvider:(FIROAuthProvider *)oauthProvider |
@@ -272,18 +309,28 @@ - (BOOL)handleOpenURL:(NSURL *)URL sourceApplication:(NSString *)sourceApplicati |
272 | 309 | /** @fn completeSignInFlowWithAccessToken:error: |
273 | 310 | @brief Called with the result of a Facebook sign-in attempt. Invokes and clears any pending |
274 | 311 | 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. |
276 | 314 | @param error An error which occurred during the sign-in attempt. |
277 | 315 | */ |
278 | 316 | - (void)completeSignInFlowWithAccessToken:(nullable NSString *)accessToken |
| 317 | + idToken:(nullable NSString *)idToken |
279 | 318 | error:(nullable NSError *)error { |
280 | 319 | if (error) { |
281 | 320 | [self callbackWithCredential:nil error:error result:nil]; |
282 | 321 | return; |
283 | 322 | } |
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. |
285 | 331 | NSString *_Nonnull token = (id _Nonnull)accessToken; |
286 | | - FIRAuthCredential *credential = [FIRFacebookAuthProvider credentialWithAccessToken:token]; |
| 332 | + credential = [FIRFacebookAuthProvider credentialWithAccessToken:token]; |
| 333 | + } |
287 | 334 | UIActivityIndicatorView *activityView = |
288 | 335 | [FUIAuthBaseViewController addActivityIndicator:_presentingViewController.view]; |
289 | 336 | [activityView startAnimating]; |
@@ -347,4 +394,47 @@ - (FBSDKLoginManager *)createLoginManager { |
347 | 394 | return [[FBSDKLoginManager alloc] init]; |
348 | 395 | } |
349 | 396 |
|
| 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 | + |
350 | 440 | @end |
0 commit comments