123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- /*
- * Copyright 2017 Google
- *
- * 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.
- */
-
- #include <CommonCrypto/CommonCrypto.h>
- #import "FIROAuthProvider.h"
-
- #import <FirebaseCore/FIRApp.h>
- #import <FirebaseCore/FIROptions.h>
-
- #import "FIRAuthBackend.h"
- #import "FIRAuth_Internal.h"
- #import "FIRAuthErrorUtils.h"
- #import "FIRAuthGlobalWorkQueue.h"
- #import "FIRAuthRequestConfiguration.h"
- #import "FIRAuthWebUtils.h"
- #import "FIRFacebookAuthProvider.h"
- #import "FIROAuthCredential_Internal.h"
- #import "FIROAuthCredential.h"
-
- #if TARGET_OS_IOS
- #import "FIRAuthURLPresenter.h"
- #endif
-
- NS_ASSUME_NONNULL_BEGIN
-
- /** @typedef FIRHeadfulLiteURLCallBack
- @brief The callback invoked at the end of the flow to fetch a headful-lite URL.
- @param headfulLiteURL The headful lite URL.
- @param error The error that occurred while fetching the headful-lite, if any.
- */
- typedef void (^FIRHeadfulLiteURLCallBack)(NSURL *_Nullable headfulLiteURL,
- NSError *_Nullable error);
-
- /** @var kHeadfulLiteURLStringFormat
- @brief The format of the URL used to open the headful lite page during sign-in.
- */
- NSString *const kHeadfulLiteURLStringFormat = @"https://%@/__/auth/handler?%@";
-
- /** @var kauthTypeSignInWithRedirect
- @brief The auth type to be specified in the sign-in request with redirect request and response.
- */
- static NSString *const kAuthTypeSignInWithRedirect = @"signInWithRedirect";
-
- @implementation FIROAuthProvider {
- /** @var _auth
- @brief The auth instance used for launching the URL presenter.
- */
- FIRAuth *_auth;
-
- /** @var _callbackScheme
- @brief The callback URL scheme used for headful-lite sign-in.
- */
- NSString *_callbackScheme;
- }
-
- + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
- IDToken:(NSString *)IDToken
- accessToken:(nullable NSString *)accessToken {
- return [[FIROAuthCredential alloc] initWithProviderID:providerID
- IDToken:IDToken
- rawNonce:nil
- accessToken:accessToken
- secret:nil
- pendingToken:nil];
- }
-
- + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
- accessToken:(NSString *)accessToken {
- return [[FIROAuthCredential alloc] initWithProviderID:providerID
- IDToken:nil
- rawNonce:nil
- accessToken:accessToken
- secret:nil
- pendingToken:nil];
- }
-
- + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
- IDToken:(NSString *)IDToken
- rawNonce:(nullable NSString *)rawNonce
- accessToken:(nullable NSString *)accessToken {
- return [[FIROAuthCredential alloc] initWithProviderID:providerID
- IDToken:IDToken
- rawNonce:rawNonce
- accessToken:accessToken
- secret:nil
- pendingToken:nil];
- }
-
- + (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID
- IDToken:(NSString *)IDToken
- rawNonce:(nullable NSString *)rawNonce {
- return [[FIROAuthCredential alloc] initWithProviderID:providerID
- IDToken:IDToken
- rawNonce:rawNonce
- accessToken:nil
- secret:nil
- pendingToken:nil];
- }
-
- + (instancetype)providerWithProviderID:(NSString *)providerID {
- return [[self alloc]initWithProviderID:providerID auth:[FIRAuth auth]];
- }
-
- + (instancetype)providerWithProviderID:(NSString *)providerID auth:(FIRAuth *)auth {
- return [[self alloc] initWithProviderID:providerID auth:auth];
- }
-
- #if TARGET_OS_IOS
- - (void)getCredentialWithUIDelegate:(nullable id<FIRAuthUIDelegate>)UIDelegate
- completion:(nullable FIRAuthCredentialCallback)completion {
- if (![FIRAuthWebUtils isCallbackSchemeRegisteredForCustomURLScheme:self->_callbackScheme]) {
- [NSException raise:NSInternalInconsistencyException
- format:@"Please register custom URL scheme '%@' in the app's Info.plist file.",
- self->_callbackScheme];
- }
- __weak __typeof__(self) weakSelf = self;
- __weak FIRAuth *weakAuth = _auth;
- __weak NSString *weakProviderID = _providerID;
- dispatch_async(FIRAuthGlobalWorkQueue(), ^{
- FIRAuthCredentialCallback callbackOnMainThread = ^(FIRAuthCredential *_Nullable credential,
- NSError *_Nullable error) {
- if (completion) {
- dispatch_async(dispatch_get_main_queue(), ^{
- completion(credential, error);
- });
- }
- };
- NSString *eventID = [FIRAuthWebUtils randomStringWithLength:10];
- NSString *sessionID = [FIRAuthWebUtils randomStringWithLength:10];
- __strong __typeof__(self) strongSelf = weakSelf;
- [strongSelf getHeadFulLiteURLWithEventID:eventID
- sessionID:sessionID
- completion:^(NSURL *_Nullable headfulLiteURL,
- NSError *_Nullable error) {
- if (error) {
- callbackOnMainThread(nil, error);
- return;
- }
- FIRAuthURLCallbackMatcher callbackMatcher = ^BOOL(NSURL *_Nullable callbackURL) {
- return [FIRAuthWebUtils isExpectedCallbackURL:callbackURL
- eventID:eventID
- authType:kAuthTypeSignInWithRedirect
- callbackScheme:strongSelf->_callbackScheme];
- };
- __strong FIRAuth *strongAuth = weakAuth;
- [strongAuth.authURLPresenter presentURL:headfulLiteURL
- UIDelegate:UIDelegate
- callbackMatcher:callbackMatcher
- completion:^(NSURL *_Nullable callbackURL,
- NSError *_Nullable error) {
- if (error) {
- callbackOnMainThread(nil, error);
- return;
- }
- NSString *OAuthResponseURLString =
- [strongSelf OAuthResponseForURL:callbackURL error:&error];
- if (error) {
- callbackOnMainThread(nil, error);
- return;
- }
- __strong NSString *strongProviderID = weakProviderID;
- FIROAuthCredential *credential =
- [[FIROAuthCredential alloc] initWithProviderID:strongProviderID
- sessionID:sessionID
- OAuthResponseURLString:OAuthResponseURLString];
- callbackOnMainThread(credential, nil);
- }];
- }];
- });
- }
- #endif // TARGET_OS_IOS
-
- #pragma mark - Internal Methods
-
- /** @fn initWithProviderID:auth:
- @brief returns an instance of @c FIROAuthProvider associated with the provided auth instance.
- @param auth The Auth instance to be associated with the OAuthProvider instance.
- @return An Instance of @c FIROAuthProvider.
- */
- - (nullable instancetype)initWithProviderID:(NSString *)providerID auth:(FIRAuth *)auth {
- NSAssert(![providerID isEqual:FIRFacebookAuthProviderID],
- @"Sign in with Facebook is not supported via generic IDP; the Facebook TOS "
- "dictate that you must use the Facebook iOS SDK for Facebook login.");
- NSAssert(![providerID isEqual:@"apple.com"],
- @"Sign in with Apple is not supported via generic IDP; You must use the Apple iOS SDK"
- " for Sign in with Apple.");
- self = [super init];
- if (self) {
- _auth = auth;
- _providerID = providerID;
- _callbackScheme = [[[_auth.app.options.clientID componentsSeparatedByString:@"."]
- reverseObjectEnumerator].allObjects componentsJoinedByString:@"."];
- }
- return self;
- }
-
- /** @fn OAuthResponseForURL:error:
- @brief Parses the redirected URL and returns a string representation of the OAuth response URL.
- @param URL The url to be parsed for an OAuth response URL.
- @param error The error that occurred if any.
- @return The OAuth response if successful.
- */
- - (nullable NSString *)OAuthResponseForURL:(NSURL *)URL error:(NSError *_Nullable *_Nullable)error {
- NSDictionary<NSString *, NSString *> *URLQueryItems =
- [FIRAuthWebUtils dictionaryWithHttpArgumentsString:URL.query];
- NSURL *deepLinkURL = [NSURL URLWithString:URLQueryItems[@"deep_link_id"]];
- URLQueryItems =
- [FIRAuthWebUtils dictionaryWithHttpArgumentsString:deepLinkURL.query];
- NSString *queryItemLink = URLQueryItems[@"link"];
- if (queryItemLink) {
- return queryItemLink;
- }
- if (!error) {
- return nil;
- }
- NSData *errorData = [URLQueryItems[@"firebaseError"] dataUsingEncoding:NSUTF8StringEncoding];
- NSError *jsonError;
- NSDictionary *errorDict = [NSJSONSerialization JSONObjectWithData:errorData
- options:0
- error:&jsonError];
- if (jsonError) {
- *error = [FIRAuthErrorUtils JSONSerializationErrorWithUnderlyingError:jsonError];
- return nil;
- }
- *error = [FIRAuthErrorUtils URLResponseErrorWithCode:errorDict[@"code"]
- message:errorDict[@"message"]];
- if (!*error) {
- NSString *reason;
- if(errorDict[@"code"] && errorDict[@"message"]) {
- reason = [NSString stringWithFormat:@"[%@] - %@",errorDict[@"code"], errorDict[@"message"]];
- }
- *error = [FIRAuthErrorUtils webSignInUserInteractionFailureWithReason:reason];
- }
- return nil;
- }
-
- /** @fn getHeadFulLiteURLWithEventID:completion:
- @brief Constructs a URL used for opening a headful-lite flow using a given event
- ID and session ID.
- @param eventID The event ID used for this purpose.
- @param sessionID The session ID used when completing the headful lite flow.
- @param completion The callback invoked after the URL has been constructed or an error
- has been encountered.
- */
- - (void)getHeadFulLiteURLWithEventID:(NSString *)eventID
- sessionID:(NSString *)sessionID
- completion:(FIRHeadfulLiteURLCallBack)completion {
- __weak __typeof__(self) weakSelf = self;
- [FIRAuthWebUtils fetchAuthDomainWithRequestConfiguration:_auth.requestConfiguration
- completion:^(NSString *_Nullable authDomain,
- NSError *_Nullable error) {
- if (error) {
- if (completion) {
- completion(nil, error);
- }
- return;
- }
- __strong __typeof__(self) strongSelf = weakSelf;
- NSString *bundleID = [NSBundle mainBundle].bundleIdentifier;
- NSString *clienID = strongSelf->_auth.app.options.clientID;
- NSString *apiKey = strongSelf->_auth.requestConfiguration.APIKey;
- NSMutableDictionary *urlArguments = [@{
- @"apiKey" : apiKey,
- @"authType" : @"signInWithRedirect",
- @"ibi" : bundleID ?: @"",
- @"clientId" : clienID,
- @"sessionId" : [strongSelf hashforString:sessionID],
- @"v" : [FIRAuthBackend authUserAgent],
- @"eventId" : eventID,
- @"providerId" : strongSelf->_providerID,
- } mutableCopy];
- if (strongSelf.scopes.count) {
- urlArguments[@"scopes"] = [strongSelf.scopes componentsJoinedByString:@","];
- }
- if (strongSelf.customParameters.count) {
- NSString *customParameters = [strongSelf customParametersStringWithError:&error];
- if (error) {
- completion(nil, error);
- return;
- }
- if (customParameters) {
- urlArguments[@"customParameters"] = customParameters;
- }
- }
- if (strongSelf->_auth.requestConfiguration.languageCode) {
- urlArguments[@"hl"] = strongSelf->_auth.requestConfiguration.languageCode;
- }
- NSString *argumentsString = [strongSelf httpArgumentsStringForArgsDictionary:urlArguments];
- NSString *URLString =
- [NSString stringWithFormat:kHeadfulLiteURLStringFormat, authDomain, argumentsString];
- if (completion) {
- NSCharacterSet *set = [NSCharacterSet URLFragmentAllowedCharacterSet];
- completion([NSURL URLWithString:
- [URLString stringByAddingPercentEncodingWithAllowedCharacters:set]], nil);
- }
- }];
- }
-
- /** @fn customParametersString
- @brief Returns a JSON string representation of the custom parameters dictionary corresponding
- to the OAuthProvider.
- @return The JSON string representation of the custom parameters dictionary corresponding
- to the OAuthProvider.
- */
- - (nullable NSString *)customParametersStringWithError:(NSError *_Nullable *_Nullable)error {
- if (!_customParameters.count) {
- return nil;
- }
-
- if (!error) {
- return nil;
- }
- NSError *jsonError;
- NSData *customParametersJSONData =
- [NSJSONSerialization dataWithJSONObject:_customParameters
- options:0
- error:&jsonError];
- if (jsonError) {
- *error = [FIRAuthErrorUtils JSONSerializationErrorWithUnderlyingError:jsonError];
- return nil;
- }
-
- NSString *customParamsRawJSON =
- [[NSString alloc] initWithData:customParametersJSONData encoding:NSUTF8StringEncoding];
- return customParamsRawJSON;
- }
-
- /** @fn hashforString:
- @brief Returns the SHA256 hash representation of a given string object.
- @param string The string for which a SHA256 hash is desired.
- @return An hexadecimal string representation of the SHA256 hash.
- */
- - (NSString *)hashforString:(NSString *)string {
- NSData *sessionIDData = [string dataUsingEncoding:NSUTF8StringEncoding];
- NSMutableData *hashOutputData = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
- if (CC_SHA256(sessionIDData.bytes,
- (CC_LONG)[sessionIDData length],
- hashOutputData.mutableBytes)) {
- }
- return [self hexStringFromData:hashOutputData];;
- }
-
- /** @fn hexStringFromData:
- @brief Returns the hexadecimal string representation of an NSData object.
- @param data The NSData object for which a hexadecical string is desired.
- @return The hexadecimal string representation of the supplied NSData object.
- */
- - (NSString *)hexStringFromData:(NSData *)data {
- const unsigned char *dataBuffer = (const unsigned char *)[data bytes];
- NSMutableString *string = [[NSMutableString alloc] init];
- for (unsigned int i = 0; i < data.length; i++){
- [string appendFormat:@"%02lx", (unsigned long)dataBuffer[i]];
- }
- return [string copy];
- }
-
- - (NSString *)httpArgumentsStringForArgsDictionary:(NSDictionary *)argsDictionary {
- NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:argsDictionary.count];
- NSString* key;
- for (key in argsDictionary) {
- NSString *description = [argsDictionary[key] description];
- [arguments addObject:[NSString stringWithFormat:@"%@=%@",
- [FIRAuthWebUtils stringByUnescapingFromURLArgument:key],
- [FIRAuthWebUtils stringByUnescapingFromURLArgument:description]]] ;
- }
- return [arguments componentsJoinedByString:@"&"];
- }
-
- @end
-
- NS_ASSUME_NONNULL_END
|