diff options
Diffstat (limited to 'Firebase/Auth')
155 files changed, 16661 insertions, 0 deletions
diff --git a/Firebase/Auth/CHANGELOG.md b/Firebase/Auth/CHANGELOG.md new file mode 100644 index 0000000..d2eb535 --- /dev/null +++ b/Firebase/Auth/CHANGELOG.md @@ -0,0 +1,62 @@ +# 2017-05-17 -- v4.0.0 +- Adds Phone Number Authentication. +- Adds support for generic OAuth2 identity providers. +- Adds methods that return additional user data from identity providers if + available when authenticating users. +- Improves session management by automatically refreshing tokens if possible + and signing out users if the session is detected invalidated, for example, + after the user changed password or deleted account from another device. +- Fixes an issue that reauthentication creates new user account if the user + credential is valid but does not match the currently signed in user. +- Fixes an issue that the "password" provider is not immediately listed on the + client side after adding a password to an account. +- Changes factory methods to return non-null FIRAuth instances or raises an + exception, instead of returning nullable instances. +- Changes auth state change listener to only be triggered when the user changes. +- Adds a new listener which is triggered whenever the ID token is changed. +- Switches ERROR_EMAIL_ALREADY_IN_USE to + ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL when the email used in the + signInWithCredential: call is already in use by another account. +- Deprecates FIREmailPasswordAuthProvider in favor of FIREmailAuthProvider. +- Deprecates getTokenWithCompletion in favor of getIDTokenWithCompletion on + FIRUser. +- Changes Swift API names to better align with Swift convention. + +# 2017-02-06 -- v3.1.1 +- Allows handling of additional errors when sending OOB action emails. The + server can respond with the following new error messages: + INVALID_MESSAGE_PAYLOAD,INVALID_SENDER and INVALID_RECIPIENT_EMAIL. +- Removes incorrect reference to FIRAuthErrorCodeCredentialTooOld in FIRUser.h. +- Provides additional error information from server if available. + +# 2016-12-13 -- v3.1.0 +- Adds FIRAuth methods that enable the app to follow up with user actions + delivered by email, such as verifying email address or reset password. +- No longer applies the keychain workaround introduced in v3.0.5 on iOS 10.2 + simulator or above since the issue has been fixed. +- Fixes nullability compilation warnings when used in Swift. +- Better reports missing password error. + +# 2016-10-24 -- v3.0.6 +- Switches to depend on open sourced GoogleToolboxForMac and GTMSessionFetcher. +- Improves logging of keychain error when initializing. + +# 2016-09-14 -- v3.0.5 +- Works around a keychain issue in iOS 10 simulator. +- Reports the correct error for invalid email when signing in with email and + password. + +# 2016-07-18 -- v3.0.4 +- Fixes a race condition bug that could crash the app with an exception from + NSURLSession on iOS 9. + +# 2016-06-20 -- v3.0.3 +- Adds documentation for all possible errors returned by each method. +- Improves error handling and messages for a variety of error conditions. +- Whether or not an user is considered anonymous is now consistent with other + platforms. +- A saved signed in user is now siloed between different Firebase projects + within the same app. + +# 2016-05-18 -- v3.0.2 +- Initial public release. diff --git a/Firebase/Auth/Docs/threading.md b/Firebase/Auth/Docs/threading.md new file mode 100644 index 0000000..2f5b782 --- /dev/null +++ b/Firebase/Auth/Docs/threading.md @@ -0,0 +1,119 @@ +# Firebase Auth Thread Safety + +This document describes how Firebase Auth maintains thread-safety. The Firebase +Auth library (not including Firebase Auth UI and Auth Provider UIs for now) +must be thread-safe, meaning deveopers are free to call any method in any +thread at any time. Thus, all code that may take part in race conditions must +be protected in some way. + +## Local Synchronization + +When contested data and accessing code is limited in scope, for example, +a mutable array accessed only by two methods, a `@synchronized` directive is +probably the simplest solution. Make sure the object to be locked on is not +`nil`, e.g., `self`. + +## Global Work Queue + +A more scalable solution used throughout the current code base is to execute +all potentially conflicting code in the same serial dispatch queue, which is +referred as "the auth global work queue", or in some other serial queue that +has its target queue set to this auth global work queue. This way we don't +have to think about which variables may be contested. We only need to make +sure all public APIs that may have thread-safety issues make the dispatch. +The auth global work queue is defined in +[FIRAuthGlobalWorkQueue.h](../Source/Private/FIRAuthGlobalWorkQueue.h) +and any serial task queue created by +[FIRAuthSerialTaskQueue.h](../Source/Private/FIRAuthSerialTaskQueue.h) +already has its target set properly. + +In following sub-sections, we divided methods into three categories, according +to the two criteria below: + +1. Whether the method is public or private: + * A public method can be directly called by developers. + * A private method can only be called by our own code. +2. Whether the method is synchronous or asynchronous. + * A synchronous method returns some value or object in the calling + thread immediately. + * An asynchronous method returns nothing but calls the callback provided + by the caller at some point in future. + +### Public Asynchronous Methods + +Unless it's a simple wrapper of another public asynchronous method, a public +asynchronous method shall + +* Dispatch asynchronously to the auth global work queue immediately. +* Dispatch asynchronously to the main queue before calling the callback. + This is to make developers' life easier so they don't have to manage + thread-safety. + +The code would look like: + +```objectivec +- (void)doSomethingWithCompletion:(nullable CompletionBlock)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + // Do things... + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(args); + }); + } + }); +} +``` + +### Public Synchronous Methods + +A public synchronous method that needs protection shall dispatch +*synchronously* to the auth global work queue for its work. The code would +look like: + +```objectivec +- (ReturnType)something { + __block ReturnType result; + dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ + // Compute result. + result = computedResult; + }); + return result; +} +``` + +**But don't call methods protected this way from private methods, or a +deadlock would occur.** This is because you are not supposed to +`dispatch_sync` to the queue you're already in. This can be easily worked +around by creating an equivalent private synchronous method to be called by +both public and private methods and making the public synchronous method a +wrapper of that. For example, + +```objectivec +- (ReturnType)somethingInternal { + // Compute result. + return computedResult; +} + +- (ReturnType)something { + __block ReturnType result; + dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ + result = [self somethingInternal]; + }); + return result; +} +``` + +### Private Methods + +Generally speaking there is nothing special needed to be done for private +methods: + +* The calling code should already be in the auth global work queue. +* The callback, if any, is provided by our own code, so it expects to called + in the auth global work queue as well. This is usually already the case, + unless the method pass the callback to some other asychronous methods + outside our library, in which case we need to manually make the callback + called in the auth global work queue. + +Just beware you can't call public synchronous methods protected by the auth +global work queue from private methods as stated in the preceding sub-section. diff --git a/Firebase/Auth/FirebaseAuth.podspec b/Firebase/Auth/FirebaseAuth.podspec new file mode 100644 index 0000000..74aa07c --- /dev/null +++ b/Firebase/Auth/FirebaseAuth.podspec @@ -0,0 +1,56 @@ +# This podspec is not intended to be deployed. It is solely for the static +# library framework build process at +# https://github.com/firebase/firebase-ios-sdk/tree/master/BuildFrameworks + +Pod::Spec.new do |s| + s.name = 'FirebaseAuth' + s.version = '4.0.0' + s.summary = 'Firebase Open Source Libraries for iOS.' + + s.description = <<-DESC +Simplify your iOS development, grow your user base, and monetize more effectively with Firebase. + DESC + + s.homepage = 'https://firebase.google.com' + s.license = { :type => 'Apache', :file => '../../LICENSE' } + s.authors = 'Google, Inc.' + + # NOTE that the FirebaseDev pod is neither publicly deployed nor yet interchangeable with the + # Firebase pod + s.source = { :git => 'https://github.com/firebase/firebase-ios-sdk.git', :tag => s.version.to_s } + s.social_media_url = 'https://twitter.com/Firebase' + s.ios.deployment_target = '7.0' + + s.source_files = '**/*.[mh]' + s.public_header_files = + 'Source/FirebaseAuth.h', + 'Source/FirebaseAuthVersion.h', + 'Source/FIRAdditionalUserInfo.h', + 'Source/FIRAuth.h', + 'Source/FIRAuthAPNSTokenType.h', + 'Source/FIRAuthCredential.h', + 'Source/FIRAuthDataResult.h', + 'Source/FIRAuthErrors.h', + 'Source/FIRAuthSwiftNameSupport.h', + 'Source/AuthProviders/EmailPassword/FIREmailAuthProvider.h', + 'Source/AuthProviders/Facebook/FIRFacebookAuthProvider.h', + 'Source/AuthProviders/GitHub/FIRGitHubAuthProvider.h', + 'Source/AuthProviders/Google/FIRGoogleAuthProvider.h', + 'Source/AuthProviders/OAuth/FIROAuthProvider.h', + 'Source/AuthProviders/Phone/FIRPhoneAuthCredential.h', + 'Source/AuthProviders/Phone/FIRPhoneAuthProvider.h', + 'Source/AuthProviders/Twitter/FIRTwitterAuthProvider.h', + 'Source/FIRUser.h', + 'Source/FIRUserInfo.h' + s.preserve_paths = + 'README.md', + 'CHANGELOG.md' + s.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => + '$(inherited) ' + 'FIRAuth_VERSION=' + s.version.to_s + + ' FIRAuth_MINOR_VERSION=' + s.version.to_s.split(".")[0] + "." + s.version.to_s.split(".")[1] + } + s.framework = 'Security' +# s.dependency 'FirebaseDev/Core' + s.dependency 'GTMSessionFetcher/Core', '~> 1.1' + s.dependency 'GoogleToolboxForMac/NSDictionary+URLArguments', '~> 2.1' +end diff --git a/Firebase/Auth/README.md b/Firebase/Auth/README.md new file mode 100644 index 0000000..e766949 --- /dev/null +++ b/Firebase/Auth/README.md @@ -0,0 +1,8 @@ +# Firebase Auth for iOS + +Firebase Auth enables apps to easily support multiple authentication options +for their end users. + +Please visit [our developer site](https://developers.google.com/) for +integration instructions, documentation, support information, and terms of +service. diff --git a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.h b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.h new file mode 100644 index 0000000..4fb5ea0 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthSwiftNameSupport.h" + +@class FIRAuthCredential; + +NS_ASSUME_NONNULL_BEGIN + +/** + @brief A string constant identifying the email & password identity provider. + */ +extern NSString *const FIREmailAuthProviderID FIR_SWIFT_NAME(EmailAuthProviderID); + +/** + @brief please use @c FIREmailAuthProviderID instead. + */ +extern NSString *const FIREmailPasswordAuthProviderID __attribute__((deprecated)); + +/** @class FIREmailAuthProvider + @brief A concrete implementation of @c FIRAuthProvider for Email & Password Sign In. + */ +FIR_SWIFT_NAME(EmailAuthProvider) +@interface FIREmailAuthProvider : NSObject + +/** @typedef FIREmailPasswordAuthProvider + @brief Please use @c FIREmailAuthProvider instead. + */ +typedef FIREmailAuthProvider FIREmailPasswordAuthProvider __attribute__((deprecated)); + + +/** @fn credentialWithEmail:password: + @brief Creates an @c FIRAuthCredential for an email & password sign in. + + @param email The user's email address. + @param password The user's password. + @return A FIRAuthCredential containing the email & password credential. + */ ++ (FIRAuthCredential *)credentialWithEmail:(NSString *)email password:(NSString *)password; + +/** @fn init + @brief This class is not meant to be initialized. + */ +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.m b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.m new file mode 100644 index 0000000..d27611e --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailAuthProvider.m @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#import "FIREmailAuthProvider.h" + +#import "FIREmailPasswordAuthCredential.h" + +// FIREmailPasswordAuthProviderID is defined in FIRAuthProvider.m. + +@implementation FIREmailAuthProvider + +- (instancetype)init { + @throw [NSException exceptionWithName:@"Attempt to call unavailable initializer." + reason:@"This class is not meant to be initialized." + userInfo:nil]; +} + ++ (FIRAuthCredential *)credentialWithEmail:(NSString *)email password:(NSString *)password { + return [[FIREmailPasswordAuthCredential alloc] initWithEmail:email password:password]; +} + +@end diff --git a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h new file mode 100644 index 0000000..004716c --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "../../Private/FIRAuthCredential_Internal.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIREmailPasswordAuthCredential + @brief Internal implementation of FIRAuthCredential for Email/Password credentials. + */ +@interface FIREmailPasswordAuthCredential : FIRAuthCredential + +/** @property email + @brief The user's email address. + */ +@property(nonatomic, readonly) NSString *email; + +/** @property password + @brief The user's password. + */ +@property(nonatomic, readonly) NSString *password; + +/** @fn initWithEmail:password: + @brief Designated initializer. + @param email The user's email address. + @param password The user's password. + */ +- (nullable instancetype)initWithEmail:(NSString *)email password:(NSString *)password + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.m b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.m new file mode 100644 index 0000000..4361366 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.m @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#import "FIREmailPasswordAuthCredential.h" + +#import "FIREmailAuthProvider.h" +#import "FIRAuthExceptionUtils.h" +#import "FIRVerifyAssertionRequest.h" + +@interface FIREmailPasswordAuthCredential () + +- (nullable instancetype)initWithProvider:(NSString *)provider NS_UNAVAILABLE; + +@end + +@implementation FIREmailPasswordAuthCredential + +- (nullable instancetype)initWithProvider:(NSString *)provider { + [FIRAuthExceptionUtils raiseMethodNotImplementedExceptionWithReason: + @"Please call the designated initializer."]; + return nil; +} + +- (nullable instancetype)initWithEmail:(NSString *)email password:(NSString *)password { + self = [super initWithProvider:FIREmailAuthProviderID]; + if (self) { + _email = [email copy]; + _password = [password copy]; + } + return self; +} + +- (void)prepareVerifyAssertionRequest:(FIRVerifyAssertionRequest *)request { + [FIRAuthExceptionUtils raiseMethodNotImplementedExceptionWithReason: + @"Attempt to call prepareVerifyAssertionRequest: on a FIREmailPasswordAuthCredential."]; +} + +@end diff --git a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthProvider.h b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthProvider.h new file mode 100644 index 0000000..bf7db21 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthProvider.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +@class FIRAuthCredential; + +NS_ASSUME_NONNULL_BEGIN + +/** + @brief A string constant identifying the email & password identity provider. + */ +extern NSString *const FIREmailPasswordAuthProviderID; + +/** @class FIREmailPasswordAuthProvider + @brief A concrete implementation of @c FIRAuthProvider for Email & Password Sign In. + */ +@interface FIREmailPasswordAuthProvider : NSObject + +/** @fn credentialWithEmail:password: + @brief Creates an @c FIRAuthCredential for an email & password sign in. + + @param email The user's email address. + @param password The user's password. + @return A FIRAuthCredential containing the email & password credential. + */ ++ (FIRAuthCredential *)credentialWithEmail:(NSString *)email password:(NSString *)password; + +/** @fn init + @brief This class is not meant to be initialized. + */ +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthProvider.m b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthProvider.m new file mode 100644 index 0000000..84c3787 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/EmailPassword/FIREmailPasswordAuthProvider.m @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#import "FIREmailPasswordAuthProvider.h" + +#import "FIREmailPasswordAuthCredential.h" + +// FIREmailPasswordAuthProviderID is defined in FIRAuthProvider.m. + +@implementation FIREmailPasswordAuthProvider + +- (instancetype)init { + @throw [NSException exceptionWithName:@"Attempt to call unavailable initializer." + reason:@"This class is not meant to be initialized." + userInfo:nil]; +} + ++ (FIRAuthCredential *)credentialWithEmail:(NSString *)email password:(NSString *)password { + return [[FIREmailPasswordAuthCredential alloc] initWithEmail:email password:password]; +} + +@end diff --git a/Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthCredential.h b/Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthCredential.h new file mode 100644 index 0000000..1c03573 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthCredential.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "../../Private/FIRAuthCredential_Internal.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRFacebookAuthCredential + @brief Internal implementation of FIRAuthCredential for the Facebook IdP. + */ +@interface FIRFacebookAuthCredential : FIRAuthCredential + +/** @fn initWithAccessToken: + @brief Designated initializer. + @param accessToken The Access Token obtained from Facebook. + */ +- (nullable instancetype)initWithAccessToken:(NSString *)accessToken NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthCredential.m b/Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthCredential.m new file mode 100644 index 0000000..1c3576a --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthCredential.m @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#import "FIRFacebookAuthCredential.h" + +#import "FIRFacebookAuthProvider.h" +#import "FIRAuthExceptionUtils.h" +#import "FIRVerifyAssertionRequest.h" + +@interface FIRFacebookAuthCredential () + +- (nullable instancetype)initWithProvider:(NSString *)provider NS_UNAVAILABLE; + +@end + +@implementation FIRFacebookAuthCredential { + NSString *_accessToken; +} + +- (nullable instancetype)initWithProvider:(NSString *)provider { + [FIRAuthExceptionUtils raiseMethodNotImplementedExceptionWithReason: + @"Please call the designated initializer."]; + return nil; +} + +- (nullable instancetype)initWithAccessToken:(NSString *)accessToken { + self = [super initWithProvider:FIRFacebookAuthProviderID]; + if (self) { + _accessToken = [accessToken copy]; + } + return self; +} + +- (void)prepareVerifyAssertionRequest:(FIRVerifyAssertionRequest *)request { + request.providerAccessToken = _accessToken; +} + +@end diff --git a/Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthProvider.h b/Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthProvider.h new file mode 100644 index 0000000..2307b08 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthProvider.h @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthSwiftNameSupport.h" + +@class FIRAuthCredential; + +NS_ASSUME_NONNULL_BEGIN + +/** + @brief A string constant identifying the Facebook identity provider. + */ +extern NSString *const FIRFacebookAuthProviderID FIR_SWIFT_NAME(FacebookAuthProviderID); + +/** @class FIRFacebookAuthProvider + @brief Utility class for constructing Facebook credentials. + */ +FIR_SWIFT_NAME(FacebookAuthProvider) +@interface FIRFacebookAuthProvider : NSObject + +/** @fn credentialWithAccessToken: + @brief Creates an @c FIRAuthCredential for a Facebook sign in. + + @param accessToken The Access Token from Facebook. + @return A FIRAuthCredential containing the Facebook credentials. + */ ++ (FIRAuthCredential *)credentialWithAccessToken:(NSString *)accessToken; + +/** @fn init + @brief This class should not be initialized. + */ +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthProvider.m b/Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthProvider.m new file mode 100644 index 0000000..d2759ae --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Facebook/FIRFacebookAuthProvider.m @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#import "FIRFacebookAuthProvider.h" + +#import "FIRFacebookAuthCredential.h" +#import "FIRAuthExceptionUtils.h" + +// FIRFacebookAuthProviderID is defined in FIRAuthProvider.m. + +@implementation FIRFacebookAuthProvider + +- (instancetype)init { + [FIRAuthExceptionUtils raiseMethodNotImplementedExceptionWithReason: + @"This class is not meant to be initialized."]; + return nil; +} + ++ (FIRAuthCredential *)credentialWithAccessToken:(NSString *)accessToken { + return [[FIRFacebookAuthCredential alloc] initWithAccessToken:accessToken]; +} + +@end diff --git a/Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthCredential.h b/Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthCredential.h new file mode 100644 index 0000000..c43fb52 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthCredential.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "../../Private/FIRAuthCredential_Internal.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRGitHubAuthCredential + @brief Internal implementation of FIRAuthCredential for GitHub credentials. + */ +@interface FIRGitHubAuthCredential : FIRAuthCredential + +/** @property token + @brief The GitHub OAuth access token. + */ +@property(nonatomic, readonly) NSString *token; + +/** @fn initWithToken: + @brief Designated initializer. + @param token The GitHub OAuth access token. + */ +- (nullable instancetype)initWithToken:(NSString *)token NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthCredential.m b/Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthCredential.m new file mode 100644 index 0000000..a0185eb --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthCredential.m @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#import "FIRGitHubAuthCredential.h" + +#import "FIRGitHubAuthProvider.h" +#import "FIRAuthExceptionUtils.h" +#import "FIRVerifyAssertionRequest.h" + +@interface FIRGitHubAuthCredential () + +- (nullable instancetype)initWithProvider:(NSString *)provider NS_UNAVAILABLE; + +@end + +@implementation FIRGitHubAuthCredential + +- (nullable instancetype)initWithProvider:(NSString *)provider { + [FIRAuthExceptionUtils raiseMethodNotImplementedExceptionWithReason: + @"Please call the designated initializer."]; + return nil; +} + +- (nullable instancetype)initWithToken:(NSString *)token { + self = [super initWithProvider:FIRGitHubAuthProviderID]; + if (self) { + _token = [token copy]; + } + return self; +} + +- (void)prepareVerifyAssertionRequest:(FIRVerifyAssertionRequest *)request { + request.providerAccessToken = _token; +} + +@end diff --git a/Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthProvider.h b/Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthProvider.h new file mode 100644 index 0000000..ab5c0ef --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthProvider.h @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthSwiftNameSupport.h" + +@class FIRAuthCredential; + +NS_ASSUME_NONNULL_BEGIN + +/** + @brief A string constant identifying the GitHub identity provider. + */ +extern NSString *const FIRGitHubAuthProviderID FIR_SWIFT_NAME(GitHubAuthProviderID); + +/** @class FIRGitHubAuthProvider + @brief Utility class for constructing GitHub credentials. + */ +FIR_SWIFT_NAME(GitHubAuthProvider) +@interface FIRGitHubAuthProvider : NSObject + +/** @fn credentialWithToken: + @brief Creates an @c FIRAuthCredential for a GitHub sign in. + + @param token The GitHub OAuth access token. + @return A FIRAuthCredential containing the GitHub credential. + */ ++ (FIRAuthCredential *)credentialWithToken:(NSString *)token; + +/** @fn init + @brief This class is not meant to be initialized. + */ +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthProvider.m b/Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthProvider.m new file mode 100644 index 0000000..8e0ff76 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/GitHub/FIRGitHubAuthProvider.m @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#import "FIRGitHubAuthProvider.h" + +#import "FIRGitHubAuthCredential.h" +#import "FIRAuthExceptionUtils.h" + +// FIRGitHubAuthProviderID is defined in FIRAuthProvider.m. + +@implementation FIRGitHubAuthProvider + +- (instancetype)init { + [FIRAuthExceptionUtils raiseMethodNotImplementedExceptionWithReason: + @"This class is not meant to be initialized."]; + return nil; +} + ++ (FIRAuthCredential *)credentialWithToken:(NSString *)token { + return [[FIRGitHubAuthCredential alloc] initWithToken:token]; +} + +@end diff --git a/Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthCredential.h b/Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthCredential.h new file mode 100644 index 0000000..ae98fbc --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthCredential.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "../../Private/FIRAuthCredential_Internal.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRGoogleAuthCredential + @brief Internal implementation of FIRAuthCredential for the Google IdP. + */ +@interface FIRGoogleAuthCredential : FIRAuthCredential + +/** @fn initWithIDToken:accessToken: + @brief Designated initializer. + @param IDToken The ID Token obtained from Google. + @param accessToken The Access Token obtained from Google. + */ +- (nullable instancetype)initWithIDToken:(NSString *)IDToken accessToken:(NSString *)accessToken + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthCredential.m b/Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthCredential.m new file mode 100644 index 0000000..d66b2e2 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthCredential.m @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#import "FIRGoogleAuthCredential.h" + +#import "FIRGoogleAuthProvider.h" +#import "FIRAuthExceptionUtils.h" +#import "FIRVerifyAssertionRequest.h" + +@interface FIRGoogleAuthCredential () + +- (nullable instancetype)initWithProvider:(NSString *)provider NS_UNAVAILABLE; + +@end + +@implementation FIRGoogleAuthCredential { + NSString *_IDToken; + NSString *_accessToken; +} + +- (nullable instancetype)initWithProvider:(NSString *)provider { + [FIRAuthExceptionUtils raiseMethodNotImplementedExceptionWithReason: + @"Please call the designated initializer."]; + return nil; +} + +- (nullable instancetype)initWithIDToken:(NSString *)IDToken accessToken:(NSString *)accessToken { + self = [super initWithProvider:FIRGoogleAuthProviderID]; + if (self) { + _IDToken = [IDToken copy]; + _accessToken = [accessToken copy]; + } + return self; +} + +- (void)prepareVerifyAssertionRequest:(FIRVerifyAssertionRequest *)request { + request.providerIDToken = _IDToken; + request.providerAccessToken = _accessToken; +} + +@end diff --git a/Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthProvider.h b/Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthProvider.h new file mode 100644 index 0000000..92f0db2 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthProvider.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthSwiftNameSupport.h" + +@class FIRAuthCredential; + +NS_ASSUME_NONNULL_BEGIN + +/** + @brief A string constant identifying the Google identity provider. + */ +extern NSString *const FIRGoogleAuthProviderID FIR_SWIFT_NAME(GoogleAuthProviderID); + +/** @class FIRGoogleAuthProvider + @brief Utility class for constructing Google Sign In credentials. + */ +FIR_SWIFT_NAME(GoogleAuthProvider) +@interface FIRGoogleAuthProvider : NSObject + +/** @fn credentialWithIDToken:accessToken: + @brief Creates an @c FIRAuthCredential for a Google sign in. + + @param IDToken The ID Token from Google. + @param accessToken The Access Token from Google. + @return A FIRAuthCredential containing the Google credentials. + */ ++ (FIRAuthCredential *)credentialWithIDToken:(NSString *)IDToken + accessToken:(NSString *)accessToken; + +/** @fn init + @brief This class should not be initialized. + */ +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthProvider.m b/Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthProvider.m new file mode 100644 index 0000000..a2f4c79 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Google/FIRGoogleAuthProvider.m @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#import "FIRGoogleAuthProvider.h" + +#import "FIRGoogleAuthCredential.h" +#import "FIRAuthExceptionUtils.h" + +// FIRGoogleAuthProviderID is defined in FIRAuthProvider.m. + +@implementation FIRGoogleAuthProvider + +- (instancetype)init { + [FIRAuthExceptionUtils raiseMethodNotImplementedExceptionWithReason: + @"This class is not meant to be initialized."]; + return nil; +} + ++ (FIRAuthCredential *)credentialWithIDToken:(NSString *)IDToken + accessToken:(NSString *)accessToken { + return [[FIRGoogleAuthCredential alloc] initWithIDToken:IDToken accessToken:accessToken]; +} + +@end diff --git a/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthCredential.h b/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthCredential.h new file mode 100644 index 0000000..744df33 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthCredential.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "../../Private/FIRAuthCredential_Internal.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIROAuthCredential + @brief Internal implementation of FIRAuthCredential for generic credentials. + */ +@interface FIROAuthCredential : FIRAuthCredential + +/** @property providerID + @brief The provider ID associated with this credential. + */ +@property(nonatomic, readonly) NSString *providerID; + +/** @property IDToken + @brief The ID Token associated with this credential. + */ +@property(nonatomic, readonly) NSString *IDToken; + +/** @property accessToken + @brief The access token associated with this credential. + */ +@property(nonatomic, readonly) NSString *accessToken; + +/** @fn initWithProviderId:IDToken:accessToken: + @brief Designated initializer. + @param providerID The provider ID associated with the credential being created. + @param IDToken The ID Token associated with the credential being created. + @param accessToken The access token associated with the credential being created. + */ +- (nullable instancetype)initWithProvierID:(NSString *)providerID + IDToken:(nullable NSString*)IDToken + accessToken:(nullable NSString *)accessToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthCredential.m b/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthCredential.m new file mode 100644 index 0000000..28712a6 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthCredential.m @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#import "FIROAuthCredential.h" +#import "FIRAuthExceptionUtils.h" +#import "FIRVerifyAssertionRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIROAuthCredential () + +- (nullable instancetype)initWithProvider:(NSString *)provider NS_UNAVAILABLE; + +@end + +@implementation FIROAuthCredential + +- (nullable instancetype)initWithProvierID:(NSString *)providerID + IDToken:(nullable NSString *)IDToken + accessToken:(nullable NSString *)accessToken { + self = [super initWithProvider:providerID]; + if (self) { + _providerID = providerID; + _IDToken = IDToken; + _accessToken = accessToken; + } + return self; +} + +- (void)prepareVerifyAssertionRequest:(FIRVerifyAssertionRequest *)request { + request.providerIDToken = _IDToken; + request.providerAccessToken = _accessToken; +} + +NS_ASSUME_NONNULL_END + +@end diff --git a/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.h b/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.h new file mode 100644 index 0000000..e059b22 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthSwiftNameSupport.h" + +@class FIRAuthCredential; + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIROAuthProvider + @brief A concrete implementation of @c FIRAuthProvider for generic OAuth Providers. + */ +FIR_SWIFT_NAME(OAuthProvider) +@interface FIROAuthProvider : NSObject + +/** @fn credentialWithProviderID:IDToken:accessToken: + @brief Creates an @c FIRAuthCredential for that OAuth 2 provider identified by providerID, ID + token and access token. + + @param providerID The provider ID associated with the Auth credential being created. + @param IDToken The IDToken associated with the Auth credential being created. + @param accessToken The accessstoken associated with the Auth credential be created, if + available. + @return A FIRAuthCredential for the specified provider ID, ID token and access token. + */ ++ (FIRAuthCredential *)credentialWithProviderID:(NSString *)providerID + IDToken:(NSString *)IDToken + accessToken:(nullable NSString *)accessToken; + + +/** @fn credentialWithProviderID:accessToken: + @brief Creates an @c FIRAuthCredential for that OAuth 2 provider identified by providerID using + an ID token. + + @param providerID The provider ID associated with the Auth credential being created. + @param accessToken The accessstoken associated with the Auth credential be created + @return A FIRAuthCredential. + */ ++ (FIRAuthCredential *)credentialWithProviderID:(NSString *)providerID + accessToken:(NSString *)accessToken; + +/** @fn init + @brief This class is not meant to be initialized. + */ +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.m b/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.m new file mode 100644 index 0000000..e810680 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/OAuth/FIROAuthProvider.m @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#import "FIROAuthProvider.h" + +#import "FIROAuthCredential.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation FIROAuthProvider + ++ (FIRAuthCredential *)credentialWithProviderID:(NSString *)providerID + IDToken:(NSString *)IDToken + accessToken:(nullable NSString *)accessToken { + return [[FIROAuthCredential alloc] initWithProvierID:providerID + IDToken:IDToken + accessToken:accessToken]; +} + ++ (FIROAuthCredential *)credentialWithProviderID:(NSString *)providerID + accessToken:(NSString *)accessToken { + return [[FIROAuthCredential alloc] initWithProvierID:providerID + IDToken:nil + accessToken:accessToken]; +} + +NS_ASSUME_NONNULL_END + +@end diff --git a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthCredential.h b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthCredential.h new file mode 100644 index 0000000..d951564 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthCredential.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthCredential.h" +#import "FIRAuthSwiftNameSupport.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRPhoneAuthCredential + @brief Implementation of FIRAuthCredential for Phone Auth credentials. + */ +FIR_SWIFT_NAME(PhoneAuthCredential) +@interface FIRPhoneAuthCredential : FIRAuthCredential + +/** @fn init + @brief This class is not supposed to be instantiated directly. + */ +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthCredential.m b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthCredential.m new file mode 100644 index 0000000..b9bf577 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthCredential.m @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#import "FIRPhoneAuthCredential.h" + +#import "FIRPhoneAuthProvider.h" +#import "FIRPhoneAuthCredential_Internal.h" +#import "FIRAuthCredential_Internal.h" +#import "FIRAuthExceptionUtils.h" +#import "FIRVerifyAssertionRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRPhoneAuthCredential () + +- (nullable instancetype)initWithProvider:(NSString *)provider NS_UNAVAILABLE; + +@end + +@implementation FIRPhoneAuthCredential + +- (instancetype)initWithTemporaryProof:(NSString *)temporaryProof + phoneNumber:(NSString *)phoneNumber + providerID:(NSString *)providerID { + self = [super initWithProvider:providerID]; + if (self) { + _temporaryProof = [temporaryProof copy]; + _phoneNumber = [phoneNumber copy]; + } + return self; +} + +- (nullable instancetype)initWithProvider:(NSString *)provider { + [FIRAuthExceptionUtils raiseMethodNotImplementedExceptionWithReason: + @"Please call the designated initializer."]; + return nil; +} + +- (instancetype)initWithProviderID:(NSString *)providerID + verificationID:(NSString *)verificationID + verificationCode:(NSString *)verificationCode { + self = [super initWithProvider:providerID]; + if (self) { + _verificationID = [verificationID copy]; + _verificationCode = [verificationCode copy]; + } + return self; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthCredential_Internal.h b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthCredential_Internal.h new file mode 100644 index 0000000..f260b89 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthCredential_Internal.h @@ -0,0 +1,70 @@ +/* + * 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. + */ + + +#import <Foundation/Foundation.h> + +#import "FIRPhoneAuthCredential.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @extension FIRPhoneAuthCredential + @brief Internal implementation of FIRAuthCredential for Phone Auth credentials. + */ +@interface FIRPhoneAuthCredential () + +/** @var verificationID + @brief The verification ID obtained from invoking @c verifyPhoneNumber:completion: + */ +@property(nonatomic, readonly, nonnull) NSString *verificationID; + +/** @var verificationCode + @brief The verification code provided by the user. + */ +@property(nonatomic, readonly, nonnull) NSString *verificationCode; + +/** @var temporaryProof + @brief The a temporary proof code perftaining to this credential, returned from the backend. + */ +@property(nonatomic, readonly, nonnull) NSString *temporaryProof; + +/** @var phoneNumber + @brief The a phone number pertaining to this credential, returned from the backend. + */ +@property(nonatomic, readonly, nonnull) NSString *phoneNumber; + +/** @var initWithTemporaryProof:phoneNumber: + @brief Designated Initializer. + @param providerID The provider ID associated with the phone auth credential being created. + */ +- (instancetype)initWithTemporaryProof:(NSString *)temporaryProof + phoneNumber:(NSString *)phoneNumber + providerID:(NSString *)providerID NS_DESIGNATED_INITIALIZER; + +/** @var initWithProviderID:verificationID:verificationCode: + @brief Designated Initializer. + @param providerID The provider ID associated with the phone auth credential being created. + @param verificationID The verification ID associated witht Phone Auth credential being created. + @param verificationCode The verification code associated witht Phone Auth credential being + created. + */ +- (instancetype)initWithProviderID:(NSString *)providerID + verificationID:(NSString *)verificationID + verificationCode:(NSString *)verificationCode NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.h b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.h new file mode 100644 index 0000000..bc12b43 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.h @@ -0,0 +1,90 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuth.h" +#import "FIRAuthSwiftNameSupport.h" + +@class FIRPhoneAuthCredential; + +NS_ASSUME_NONNULL_BEGIN + +/** @var FIRPhoneAuthProviderID + @brief A string constant identifying the phone identity provider. + */ +extern NSString *const FIRPhoneAuthProviderID FIR_SWIFT_NAME(PhoneAuthProviderID); + +/** @typedef FIRVerificationResultCallback + @brief The type of block invoked when a request to send a verification code has finished. + + @param verificationID On success, the verification ID provided, nil otherwise. + @param error On error, the error that occured, nil otherwise. + */ +typedef void (^FIRVerificationResultCallback)(NSString *_Nullable verificationID, + NSError *_Nullable error) + FIR_SWIFT_NAME(VerificationResultCallback); + +/** @class FIRPhoneNumberProvider + @brief A concrete implementation of @c FIRAuthProvider for Phone Auth Providers. + */ +FIR_SWIFT_NAME(PhoneAuthProvider) +@interface FIRPhoneAuthProvider : NSObject + +/** @fn provider + @brief Returns an instance of @c FIRPhoneAuthProvider for the default @c FIRAuth object. + */ ++ (instancetype)provider FIR_SWIFT_NAME(provider()); + +/** @fn providerWithAuth: + @brief Returns an instance of @c FIRPhoneAuthProvider for the provided @c FIRAuth object. + + @param auth The auth object to associate with the @c PhoneauthProvider instance. + */ ++ (instancetype)providerWithAuth:(FIRAuth *)auth FIR_SWIFT_NAME(provider(auth:)); + +/** @fn verifyPhoneNumber:completion: + @brief Starts the phone number authentication flow by sending a verifcation code to the + specified phone number. + + @param phoneNumber The phone number to be verified. + @param completion The callback to be invoked when the verification flow is finished. + */ +- (void)verifyPhoneNumber:(NSString *)phoneNumber + completion:(nullable FIRVerificationResultCallback)completion; + +/** @fn credentialWithVerificationID:verificationCode: + @brief Creates an @c FIRAuthCredential for the phone number provider identified by the + verification ID and verification code. + + @param verificationID The verification ID obtained from invoking @c + verifyPhoneNumber:completion: + @param verificationCode The verification code obtained from the user. + @return The corresponding @c FIRAuthCredential for the verification ID and verification code + provided. + */ +- (FIRPhoneAuthCredential *)credentialWithVerificationID:(NSString *)verificationID + verificationCode:(NSString *)verificationCode; + +/** @fn init + @brief Please use the @c provider or @providerWithAuth: methods to obtain an instance of @c + FIRPhoneAuthProvider. + */ +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m new file mode 100644 index 0000000..423b2b6 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m @@ -0,0 +1,213 @@ +/* + * 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. + */ + +#import "FIRPhoneAuthProvider.h" + +#import "FIRLogger.h" +#import "FIRPhoneAuthCredential_Internal.h" +#import "NSString+FIRAuth.h" +#import "../../Private/FIRAuthAPNSToken.h" +#import "../../Private/FIRAuthAPNSTokenManager.h" +#import "../../Private/FIRAuthAppCredential.h" +#import "../../Private/FIRAuthAppCredentialManager.h" +#import "../../Private/FIRAuthGlobalWorkQueue.h" +#import "../../Private/FIRAuth_Internal.h" +#import "../../Private/FIRAuthNotificationManager.h" +#import "../../Private/FIRAuthErrorUtils.h" +#import "FIRAuthBackend.h" +#import "FIRSendVerificationCodeRequest.h" +#import "FIRSendVerificationCodeResponse.h" +#import "FIRVerifyClientRequest.h" +#import "FIRVerifyClientResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @typedef FIRVerifyClientCallback + @brief The callback invoked at the end of a client verification flow. + @param appCredential credential that proves the identity of the app during a phone + authentication flow. + @param error The error that occured while verifying the app, if any. + */ +typedef void (^FIRVerifyClientCallback)(FIRAuthAppCredential *_Nullable appCredential, + NSError *_Nullable error); + +@implementation FIRPhoneAuthProvider { + + /** @var _auth + @brief The auth instance used to for verifying the phone number. + */ + FIRAuth *_auth; +} + +/** @fn initWithAuth: + @brief returns an instance of @c FIRPhoneAuthProvider assocaited with the provided auth + instance. + @return An Instance of @c FIRPhoneAuthProvider. + */ +- (nullable instancetype)initWithAuth:(FIRAuth *)auth { + self = [super init]; + if (self) { + _auth = auth; + } + return self; +} + +- (void)verifyPhoneNumber:(NSString *)phoneNumber + completion:(nullable FIRVerificationResultCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + FIRVerificationResultCallback callBackOnMainThread = ^(NSString *_Nullable verificationID, + NSError *_Nullable error) { + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(verificationID, error); + }); + } + }; + + if (!phoneNumber.length) { + callBackOnMainThread(nil, + [FIRAuthErrorUtils missingPhoneNumberErrorWithMessage:nil]); + return; + } + [_auth.notificationManager checkNotificationForwardingWithCallback: + ^(BOOL isNotificationBeingForwarded) { + if (!isNotificationBeingForwarded) { + callBackOnMainThread(nil, [FIRAuthErrorUtils notificationNotForwardedError]); + return; + } + [self verifyClientAndSendVerificationCodeToPhoneNumber:phoneNumber + retryOnInvalidAppCredential:YES + callback:callBackOnMainThread]; + }]; + }); +} + +- (FIRPhoneAuthCredential *)credentialWithVerificationID:(NSString *)verificationID + verificationCode:(NSString *)verificationCode { + return [[FIRPhoneAuthCredential alloc] initWithProviderID:FIRPhoneAuthProviderID + verificationID:verificationID + verificationCode:verificationCode]; +} + ++ (instancetype)provider { + return [[self alloc]initWithAuth:[FIRAuth auth]]; +} + ++ (instancetype)providerWithAuth:(FIRAuth *)auth { + return [[self alloc]initWithAuth:auth]; +} + +#pragma mark - Internal Methods + +/** @fn verifyClientAndSendVerificationCodeToPhoneNumber:retryOnInvalidAppCredential:callback: + @brief Starts the flow to verify the client via silent push notification. + @param retryOnInvalidAppCredential Whether of not the flow should be retried if an + FIRAuthErrorCodeInvalidAppCredential error is returned from the backend. + @param phoneNumber The phone number to be verified. + @param callback The callback to be invoked on the global work queue when the flow is + finished. + */ +- (void)verifyClientAndSendVerificationCodeToPhoneNumber:(NSString *)phoneNumber + retryOnInvalidAppCredential:(BOOL)retryOnInvalidAppCredential + callback:(FIRVerificationResultCallback)callback { + [self verifyClientWithCompletion:^(FIRAuthAppCredential *_Nullable appCredential, + NSError *_Nullable error) { + if (error) { + callback(nil, error); + return; + } + FIRSendVerificationCodeRequest *request = + [[FIRSendVerificationCodeRequest alloc] initWithPhoneNumber:phoneNumber + appCredential:appCredential + APIKey:_auth.APIKey]; + [FIRAuthBackend sendVerificationCode:request + callback:^(FIRSendVerificationCodeResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + if (error.code == FIRAuthErrorCodeInvalidAppCredential) { + if (retryOnInvalidAppCredential) { + [_auth.appCredentialManager clearCredential]; + [self verifyClientAndSendVerificationCodeToPhoneNumber:phoneNumber + retryOnInvalidAppCredential:NO + callback:callback]; + return; + } + callback(nil, [FIRAuthErrorUtils unexpectedResponseWithDeserializedResponse:nil + underlyingError:error]); + return; + } + callback(nil, error); + return; + } + // Associate the phone number with the verification ID. + response.verificationID.fir_authPhoneNumber = phoneNumber; + callback(response.verificationID, nil); + }]; + }]; +} + +/** @fn verifyClientWithCompletion:completion: + @brief Continues the flow to verify the client via silent push notification. + @param completion The callback to be invoked when the client verification flow is finished. + */ +- (void)verifyClientWithCompletion:(FIRVerifyClientCallback)completion { + if (_auth.appCredentialManager.credential) { + completion(_auth.appCredentialManager.credential, nil); + return; + } + [_auth.tokenManager getTokenWithCallback:^(FIRAuthAPNSToken * _Nullable token) { + if (!token) { + completion(nil, [FIRAuthErrorUtils missingAppTokenError]); + return; + } + + // Convert token data to hex string. + NSUInteger capacity = token.data.length * 2; + NSMutableString *tokenString = [NSMutableString stringWithCapacity:capacity]; + const unsigned char *tokenData = token.data.bytes; + for (int idx = 0; idx < token.data.length; ++idx) { + [tokenString appendFormat:@"%02X", (int)tokenData[idx]]; + } + + FIRVerifyClientRequest *request = + [[FIRVerifyClientRequest alloc] initWithAppToken:tokenString + isSandbox:token.type == FIRAuthAPNSTokenTypeSandbox + APIKey:_auth.APIKey]; + [FIRAuthBackend verifyClient:request callback:^(FIRVerifyClientResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + completion(nil, error); + return; + } + NSTimeInterval timeout = [response.suggestedTimeOutDate timeIntervalSinceNow]; + [_auth.appCredentialManager + didStartVerificationWithReceipt:response.receipt + timeout:timeout + callback:^(FIRAuthAppCredential *credential) { + if (!credential.secret) { + FIRLogError(kFIRLoggerAuth, @"I-AUT000014", + @"Failed to receive remote notification to verify app identity within " + @"%.0f second(s)", timeout); + } + completion(credential, nil); + }]; + }]; + }]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/Phone/NSString+FIRAuth.h b/Firebase/Auth/Source/AuthProviders/Phone/NSString+FIRAuth.h new file mode 100644 index 0000000..ba123fa --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Phone/NSString+FIRAuth.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** @category NSString(FIRAuth) + @brief A FIRAuth category for extending the functionality of NSString for specific Firebase Auth + use cases. + */ +@interface NSString (FIRAuth) + +/** @property fir_authPhoneNumber + @brief A phone number associated with the verification ID (NSString instance). + @remarks Allows an instance on NSString to be associated with a phone number in order to link + phone number with the verificationID returned from verifyPhoneNumber:completion: + */ +@property(nonatomic, strong) NSString *fir_authPhoneNumber; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/Phone/NSString+FIRAuth.m b/Firebase/Auth/Source/AuthProviders/Phone/NSString+FIRAuth.m new file mode 100644 index 0000000..87f3b1c --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Phone/NSString+FIRAuth.m @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#import "NSString+FIRAuth.h" + +#import <objc/runtime.h> + +NS_ASSUME_NONNULL_BEGIN + +@implementation NSString (FIRAuth) + +- (void)setFir_authPhoneNumber:(NSString *)phoneNumber { + objc_setAssociatedObject(self, @selector(fir_authPhoneNumber), phoneNumber, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSString *)fir_authPhoneNumber { + return objc_getAssociatedObject(self, @selector(fir_authPhoneNumber)); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthCredential.h b/Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthCredential.h new file mode 100644 index 0000000..5fab4e2 --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthCredential.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "../../Private/FIRAuthCredential_Internal.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRTwitterAuthCredential + @brief Internal implementation of FIRAuthCredential for Twitter credentials. + */ +@interface FIRTwitterAuthCredential : FIRAuthCredential + +/** @property token + @brief The Twitter OAuth token. + */ +@property(nonatomic, readonly) NSString *token; + +/** @property secret + @brief The Twitter OAuth secret. + */ +@property(nonatomic, readonly) NSString *secret; + +/** @fn initWithToken:secret: + @brief Designated initializer. + @param token The Twitter OAuth token. + @param secret The Twitter OAuth secret. + */ +- (nullable instancetype)initWithToken:(NSString *)token secret:(NSString *)secret + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthCredential.m b/Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthCredential.m new file mode 100644 index 0000000..6772d6f --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthCredential.m @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#import "FIRTwitterAuthCredential.h" + +#import "FIRTwitterAuthProvider.h" +#import "FIRAuthExceptionUtils.h" +#import "FIRVerifyAssertionRequest.h" + +@interface FIRTwitterAuthCredential () + +- (nullable instancetype)initWithProvider:(NSString *)provider NS_UNAVAILABLE; + +@end + +@implementation FIRTwitterAuthCredential + +- (nullable instancetype)initWithProvider:(NSString *)provider { + [FIRAuthExceptionUtils raiseMethodNotImplementedExceptionWithReason: + @"Please call the designated initializer."]; + return nil; +} + +- (nullable instancetype)initWithToken:(NSString *)token secret:(NSString *)secret { + self = [super initWithProvider:FIRTwitterAuthProviderID]; + if (self) { + _token = [token copy]; + _secret = [secret copy]; + } + return self; +} + +- (void)prepareVerifyAssertionRequest:(FIRVerifyAssertionRequest *)request { + request.providerAccessToken = _token; + request.providerOAuthTokenSecret = _secret; +} + +@end diff --git a/Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthProvider.h b/Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthProvider.h new file mode 100644 index 0000000..d8f647d --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthProvider.h @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthSwiftNameSupport.h" + +@class FIRAuthCredential; + +NS_ASSUME_NONNULL_BEGIN + +/** + @brief A string constant identifying the Twitter identity provider. + */ +extern NSString *const FIRTwitterAuthProviderID FIR_SWIFT_NAME(TwitterAuthProviderID); + +/** @class FIRTwitterAuthProvider + @brief Utility class for constructing Twitter credentials. + */ +FIR_SWIFT_NAME(TwitterAuthProvider) +@interface FIRTwitterAuthProvider : NSObject + +/** @fn credentialWithToken:secret: + @brief Creates an @c FIRAuthCredential for a Twitter sign in. + + @param token The Twitter OAuth token. + @param secret The Twitter OAuth secret. + @return A FIRAuthCredential containing the Twitter credential. + */ ++ (FIRAuthCredential *)credentialWithToken:(NSString *)token secret:(NSString *)secret; + +/** @fn init + @brief This class is not meant to be initialized. + */ +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthProvider.m b/Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthProvider.m new file mode 100644 index 0000000..5d738ce --- /dev/null +++ b/Firebase/Auth/Source/AuthProviders/Twitter/FIRTwitterAuthProvider.m @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#import "FIRTwitterAuthProvider.h" + +#import "FIRTwitterAuthCredential.h" +#import "FIRAuthExceptionUtils.h" + +// FIRTwitterAuthProviderID is defined in FIRAuthProvider.m. + +@implementation FIRTwitterAuthProvider + +- (instancetype)init { + [FIRAuthExceptionUtils raiseMethodNotImplementedExceptionWithReason: + @"This class is not meant to be initialized."]; + return nil; +} + ++ (FIRAuthCredential *)credentialWithToken:(NSString *)token secret:(NSString *)secret { + return [[FIRTwitterAuthCredential alloc] initWithToken:token secret:secret]; +} + +@end diff --git a/Firebase/Auth/Source/FIRActionCodeSettings.m b/Firebase/Auth/Source/FIRActionCodeSettings.m new file mode 100644 index 0000000..26d7538 --- /dev/null +++ b/Firebase/Auth/Source/FIRActionCodeSettings.m @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#import "Private/FIRActionCodeSettings.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation FIRActionCodeSettings + +- (void)setIOSBundleID:(NSString *)iOSBundleID + appStoreID:(nullable NSString *)appStoreID { + _iOSBundleID = [iOSBundleID copy]; + _iOSAppStoreID = [appStoreID copy]; +} + +- (void)setAndroidPackageName:(NSString *)androidPackageName + installIfNotAvailable:(BOOL)installIfNotAvailable + minimumVersion:(nullable NSString *)minimumVersion { + _androidPackageName = [androidPackageName copy]; + _androidInstallIfNotAvailable = installIfNotAvailable; + _androidMinimumVersion = [minimumVersion copy]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAdditionalUserInfo.h b/Firebase/Auth/Source/FIRAdditionalUserInfo.h new file mode 100644 index 0000000..70e9e57 --- /dev/null +++ b/Firebase/Auth/Source/FIRAdditionalUserInfo.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthSwiftNameSupport.h" + +@class FIRVerifyAssertionResponse; + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRAdditionalUserInfo + @brief Represents additional user data returned from an identity provider. + */ +FIR_SWIFT_NAME(AdditionalUserInfo) +@interface FIRAdditionalUserInfo : NSObject + +/** @fn init + @brief This class should not be initialized manually. @c FIRAdditionalUserInfo can be retrieved + from @c FIRAuthDataResult . + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @property providerID + @brief The provider identifier. + */ +@property(nonatomic, readonly) NSString *providerID; + +/** @property profile + @brief profile Dictionary containing the additional IdP specific information. + */ +@property(nonatomic, readonly, nullable) NSDictionary<NSString *, NSObject *> *profile; + +/** @property username + @brief username The name of the user. + */ +@property(nonatomic, readonly, nullable) NSString *username; + +/** @property newUser + @brief Indicates whether or not the current user was signed in for the first time. + */ +@property(nonatomic, readonly, getter=isNewUser) BOOL newUser; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAdditionalUserInfo.m b/Firebase/Auth/Source/FIRAdditionalUserInfo.m new file mode 100644 index 0000000..e00347d --- /dev/null +++ b/Firebase/Auth/Source/FIRAdditionalUserInfo.m @@ -0,0 +1,98 @@ +/* + * 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. + */ + +#import "Private/FIRAdditionalUserInfo_Internal.h" + +#import "FIRVerifyAssertionResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation FIRAdditionalUserInfo + +/** @var kProviderIDCodingKey + @brief The key used to encode the providerID property for NSSecureCoding. + */ +static NSString *const kProviderIDCodingKey = @"providerID"; + +/** @var kProfileCodingKey + @brief The key used to encode the profile property for NSSecureCoding. + */ +static NSString *const kProfileCodingKey = @"profile"; + +/** @var kUsernameCodingKey + @brief The key used to encode the username property for NSSecureCoding. + */ +static NSString *const kUsernameCodingKey = @"username"; + +/** @var kNewUserKey + @brief The key used to encode the newUser property for NSSecureCoding. + */ +static NSString *const kNewUserKey = @"newUser"; + ++ (nullable instancetype)userInfoWithVerifyAssertionResponse: + (FIRVerifyAssertionResponse *)verifyAssertionResponse { + return [[self alloc] initWithProviderID:verifyAssertionResponse.providerID + profile:verifyAssertionResponse.profile + username:verifyAssertionResponse.username + isNewUser:verifyAssertionResponse.isNewUser]; +} + +- (nullable instancetype)initWithProviderID:(NSString *)providerID + profile:(nullable NSDictionary<NSString *, NSObject *> *)profile + username:(nullable NSString *)username + isNewUser:(BOOL)isNewUser { + self = [super init]; + if (self) { + _providerID = [providerID copy]; + if (profile) { + _profile = [[NSDictionary alloc] initWithDictionary:profile copyItems:YES]; + } + _username = [username copy]; + _newUser = isNewUser; + } + return self; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { + NSString *providerID = + [aDecoder decodeObjectOfClass:[NSString class] forKey:kProviderIDCodingKey]; + NSDictionary<NSString *, NSObject *> *profile = + [aDecoder decodeObjectOfClass:[NSDictionary class] forKey:kProfileCodingKey]; + NSString *username = [aDecoder decodeObjectOfClass:[NSString class] forKey:kUsernameCodingKey]; + NSNumber *isNewUser = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:kNewUserKey]; + + return [self initWithProviderID:providerID + profile:profile + username:username + isNewUser:isNewUser.boolValue]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_providerID forKey:kProviderIDCodingKey]; + [aCoder encodeObject:_profile forKey:kProfileCodingKey]; + [aCoder encodeObject:_username forKey:kUsernameCodingKey]; + [aCoder encodeObject:[NSNumber numberWithBool:_newUser] forKey:kNewUserKey]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuth.h b/Firebase/Auth/Source/FIRAuth.h new file mode 100644 index 0000000..b913380 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuth.h @@ -0,0 +1,612 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthAPNSTokenType.h" +#import "FIRAuthErrors.h" +#import "FIRAuthSwiftNameSupport.h" + +@class FIRApp; +@class FIRAuth; +@class FIRAuthCredential; +@class FIRAuthDataResult; +@class FIRUser; +@protocol FIRAuthStateListener; + +NS_ASSUME_NONNULL_BEGIN + +/** @typedef FIRAuthStateDidChangeListenerHandle + @brief The type of handle returned by @c FIRAuth.addAuthStateDidChangeListener:. + */ +typedef id<NSObject> FIRAuthStateDidChangeListenerHandle + FIR_SWIFT_NAME(AuthStateDidChangeListenerHandle); + +/** @typedef FIRAuthStateDidChangeListenerBlock + @brief The type of block which can be registered as a listener for auth state did change events. + + @param auth The FIRAuth object on which state changes occurred. + @param user Optionally; the current signed in user, if any. + */ +typedef void(^FIRAuthStateDidChangeListenerBlock)(FIRAuth *auth, FIRUser *_Nullable user) + FIR_SWIFT_NAME(AuthStateDidChangeListenerBlock); + +/** @typedef FIRIDTokenDidChangeListenerHandle + @brief The type of handle returned by @c FIRAuth.addIDTokenDidChangeListener:. + */ +typedef id<NSObject> FIRIDTokenDidChangeListenerHandle + FIR_SWIFT_NAME(IDTokenDidChangeListenerHandle); + +/** @typedef FIRIDTokenDidChangeListenerBlock + @brief The type of block which can be registered as a listener for ID token did change events. + + @param auth The FIRAuth object on which ID token changes occurred. + @param user Optionally; the current signed in user, if any. + */ +typedef void(^FIRIDTokenDidChangeListenerBlock)(FIRAuth *auth, FIRUser *_Nullable user) + FIR_SWIFT_NAME(IDTokenDidChangeListenerBlock); + +/** @typedef FIRAuthDataResultCallback + @brief The type of block invoked when sign-in related events complete. + + @param authResult Optionally; Result of sign-in request containing @c FIRUser and + @c FIRAdditionalUserInfo. + @param error Optionally; the error which occurred - or nil if the request was successful. + */ +typedef void (^FIRAuthDataResultCallback)(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) + FIR_SWIFT_NAME(AuthDataResultCallback); + +#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +/** + @brief The name of the @c NSNotificationCenter notification which is posted when the auth state + changes (for example, a new token has been produced, a user signs in or signs out). The + object parameter of the notification is the sender @c FIRAuth instance. + */ +extern const NSNotificationName FIRAuthStateDidChangeNotification + FIR_SWIFT_NAME(AuthStateDidChange); +#else +/** + @brief The name of the @c NSNotificationCenter notification which is posted when the auth state + changes (for example, a new token has been produced, a user signs in or signs out). The + object parameter of the notification is the sender @c FIRAuth instance. + */ +extern NSString *const FIRAuthStateDidChangeNotification + FIR_SWIFT_NAME(AuthStateDidChangeNotification); +#endif // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + +/** @typedef FIRAuthResultCallback + @brief The type of block invoked when sign-in related events complete. + + @param user Optionally; the signed in user, if any. + @param error Optionally; if an error occurs, this is the NSError object that describes the + problem. Set to nil otherwise. + */ +typedef void (^FIRAuthResultCallback)(FIRUser *_Nullable user, NSError *_Nullable error) + FIR_SWIFT_NAME(AuthResultCallback); + +/** @typedef FIRProviderQueryCallback + @brief The type of block invoked when a list of identity providers for a given email address is + requested. + + @param providers Optionally; a list of provider identifiers, if any. + @see FIRGoogleAuthProviderID etc. + @param error Optionally; if an error occurs, this is the NSError object that describes the + problem. Set to nil otherwise. + */ +typedef void (^FIRProviderQueryCallback)(NSArray<NSString *> *_Nullable providers, + NSError *_Nullable error) + FIR_SWIFT_NAME(ProviderQueryCallback); + +/** @typedef FIRSendPasswordResetCallback + @brief The type of block invoked when sending a password reset email. + + @param error Optionally; if an error occurs, this is the NSError object that describes the + problem. Set to nil otherwise. + */ +typedef void (^FIRSendPasswordResetCallback)(NSError *_Nullable error) + FIR_SWIFT_NAME(SendPasswordResetCallback); + +/** @typedef FIRConfirmPasswordResetCallback + @brief The type of block invoked when performing a password reset. + + @param error Optionally; if an error occurs, this is the NSError object that describes the + problem. Set to nil otherwise. + */ +typedef void (^FIRConfirmPasswordResetCallback)(NSError *_Nullable error) + FIR_SWIFT_NAME(ConfirmPasswordResetCallback); + +/** @typedef FIRVerifyPasswordResetCodeCallback + @brief The type of block invoked when verifying that an out of band code should be used to + perform password reset. + + @param email Optionally; the email address of the user for which the out of band code applies. + @param error Optionally; if an error occurs, this is the NSError object that describes the + problem. Set to nil otherwise. + */ +typedef void (^FIRVerifyPasswordResetCodeCallback)(NSString *_Nullable email, + NSError *_Nullable error) + FIR_SWIFT_NAME(VerifyPasswordResetCodeCallback); + +/** @typedef FIRApplyActionCodeCallback + @brief The type of block invoked when applying an action code. + + @param error Optionally; if an error occurs, this is the NSError object that describes the + problem. Set to nil otherwise. + */ +typedef void (^FIRApplyActionCodeCallback)(NSError *_Nullable error) + FIR_SWIFT_NAME(ApplyActionCodeCallback); + +/** + @brief Keys used to retrieve operation data from a @c FIRActionCodeInfo object by the @c + dataForKey method. + */ +typedef NS_ENUM(NSInteger, FIRActionDataKey) { + /** + * The email address to which the code was sent. + * For FIRActionCodeOperationRecoverEmail, the new email address for the account. + */ + FIRActionCodeEmailKey = 0, + + /** For FIRActionCodeOperationRecoverEmail, the current email address for the account. */ + FIRActionCodeFromEmailKey = 1 +} FIR_SWIFT_NAME(ActionDataKey); + +/** @class FIRActionCodeInfo + @brief Manages information regarding action codes. + */ +FIR_SWIFT_NAME(ActionCodeInfo) +@interface FIRActionCodeInfo : NSObject + +/** + @brief Operations which can be performed with action codes. + */ +typedef NS_ENUM(NSInteger, FIRActionCodeOperation) { + /** Action code for unknown operation. */ + FIRActionCodeOperationUnknown = 0, + + /** Action code for password reset operation. */ + FIRActionCodeOperationPasswordReset = 1, + + /** Action code for verify email operation. */ + FIRActionCodeOperationVerifyEmail = 2 +} FIR_SWIFT_NAME(ActionCodeOperation); + +/** + @brief The operation being performed. + */ +@property(nonatomic, readonly) FIRActionCodeOperation operation; + +/** @fn dataForKey: + @brief The operation being performed. + + @param key The FIRActionDataKey value used to retrieve the operation data. + + @return The operation data pertaining to the provided action code key. + */ +- (NSString *)dataForKey:(FIRActionDataKey)key; + +/** @fn init + @brief please use initWithOperation: instead. + */ +- (instancetype)init NS_UNAVAILABLE; + +@end + +/** @typedef FIRCheckActionCodeCallBack + @brief The type of block invoked when performing a check action code operation. + + @param info Metadata corresponding to the action code. + @param error Optionally; if an error occurs, this is the NSError object that describes the + problem. Set to nil otherwise. + */ +typedef void (^FIRCheckActionCodeCallBack)(FIRActionCodeInfo *_Nullable info, + NSError *_Nullable error) + FIR_SWIFT_NAME(CheckActionCodeCallback); + +/** @class FIRAuth + @brief Manages authentication for Firebase apps. + @remarks This class is thread-safe. + */ +FIR_SWIFT_NAME(Auth) +@interface FIRAuth : NSObject + +/** @fn auth + @brief Gets the auth object for the default Firebase app. + @remarks The default Firebase app must have already been configured or an exception will be + raised. + */ ++ (FIRAuth *)auth FIR_SWIFT_NAME(auth()); + +/** @fn authWithApp: + @brief Gets the auth object for a @c FIRApp. + + @param app The FIRApp for which to retrieve the associated FIRAuth instance. + @return The FIRAuth instance associated with the given FIRApp. + */ ++ (FIRAuth *)authWithApp:(FIRApp *)app FIR_SWIFT_NAME(auth(app:)); + +/** @property app + @brief Gets the @c FIRApp object that this auth object is connected to. + */ +@property(nonatomic, weak, readonly, nullable) FIRApp *app; + +/** @property currentUser + @brief Synchronously gets the cached current user, or null if there is none. + */ +@property(nonatomic, strong, readonly, nullable) FIRUser *currentUser; + +/** @property APNSToken + @brief The APNs token used for phone number authentication. The type of the token (production + or sandbox) will be attempted to be automatcially detected. + @remarks If swizzling is disabled, the APNs Token must be set for phone number auth to work, + by either setting this property or by calling @c setAPNSToken:type: + */ +@property(nonatomic, strong, nullable) NSData *APNSToken; + +/** @fn init + @brief Please access auth instances using @c FIRAuth.auth and @c FIRAuth.authForApp:. + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @fn fetchProvidersForEmail:completion: + @brief Fetches the list of IdPs that can be used for signing in with the provided email address. + Useful for an "identifier-first" sign-in flow. + + @param email The email address for which to obtain a list of identity providers. + @param completion Optionally; a block which is invoked when the list of providers for the + specified email address is ready or an error was encountered. Invoked asynchronously on the + main thread in the future. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeInvalidEmail - Indicates the email address is malformed.</li> + </ul> + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods. + */ +- (void)fetchProvidersForEmail:(NSString *)email + completion:(nullable FIRProviderQueryCallback)completion; + +/** @fn signInWithEmail:password:completion: + @brief Signs in using an email address and password. + + @param email The user's email address. + @param password The user's password. + @param completion Optionally; a block which is invoked when the sign in flow finishes, or is + canceled. Invoked asynchronously on the main thread in the future. + + @remarks Possible error codes: + + <ul> + <li>@c FIRAuthErrorCodeOperationNotAllowed - Indicates that email and password + accounts are not enabled. Enable them in the Auth section of the + Firebase console. + </li> + <li>@c FIRAuthErrorCodeUserDisabled - Indicates the user's account is disabled. + </li> + <li>@c FIRAuthErrorCodeWrongPassword - Indicates the user attempted + sign in with an incorrect password. + </li> + <li>@c FIRAuthErrorCodeInvalidEmail - Indicates the email address is malformed. + </li> + </ul> + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods. + */ +- (void)signInWithEmail:(NSString *)email + password:(NSString *)password + completion:(nullable FIRAuthResultCallback)completion; + +/** @fn signInWithCredential:completion: + @brief Convenience method for @c signInAndRetrieveDataWithCredential:completion: This method + doesn't return additional identity provider data. + */ +- (void)signInWithCredential:(FIRAuthCredential *)credential + completion:(nullable FIRAuthResultCallback)completion; + +/** @fn signInAndRetrieveDataWithCredential:completion: + @brief Asynchronously signs in to Firebase with the given 3rd-party credentials (e.g. a Facebook + login Access Token, a Google ID Token/Access Token pair, etc.) and returns additional + identity provider data. + + @param credential The credential supplied by the IdP. + @param completion Optionally; a block which is invoked when the sign in flow finishes, or is + canceled. Invoked asynchronously on the main thread in the future. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeInvalidCredential - Indicates the supplied credential is invalid. + This could happen if it has expired or it is malformed. + </li> + <li>@c FIRAuthErrorCodeOperationNotAllowed - Indicates that accounts + with the identity provider represented by the credential are not enabled. + Enable them in the Auth section of the Firebase console. + </li> + <li>@c FIRAuthErrorCodeAccountExistsWithDifferentCredential - Indicates the email asserted + by the credential (e.g. the email in a Facebook access token) is already in use by an + existing account, that cannot be authenticated with this sign-in method. Call + fetchProvidersForEmail for this user’s email and then prompt them to sign in with any of + the sign-in providers returned. This error will only be thrown if the "One account per + email address" setting is enabled in the Firebase console, under Auth settings. + </li> + <li>@c FIRAuthErrorCodeUserDisabled - Indicates the user's account is disabled. + </li> + <li>@c FIRAuthErrorCodeWrongPassword - Indicates the user attempted sign in with an + incorrect password, if credential is of the type EmailPasswordAuthCredential. + </li> + <li>@c FIRAuthErrorCodeInvalidEmail - Indicates the email address is malformed. + </li> + </ul> + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods. + */ +- (void)signInAndRetrieveDataWithCredential:(FIRAuthCredential *)credential + completion:(nullable FIRAuthDataResultCallback)completion; + +/** @fn signInAnonymouslyWithCompletion: + @brief Asynchronously creates and becomes an anonymous user. + @param completion Optionally; a block which is invoked when the sign in finishes, or is + canceled. Invoked asynchronously on the main thread in the future. + + @remarks If there is already an anonymous user signed in, that user will be returned instead. + If there is any other existing user signed in, that user will be signed out. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeOperationNotAllowed - Indicates that anonymous accounts are + not enabled. Enable them in the Auth section of the Firebase console. + </li> + </ul> + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods. + */ +- (void)signInAnonymouslyWithCompletion:(nullable FIRAuthResultCallback)completion; + +/** @fn signInWithCustomToken:completion: + @brief Asynchronously signs in to Firebase with the given Auth token. + + @param token A self-signed custom auth token. + @param completion Optionally; a block which is invoked when the sign in finishes, or is + canceled. Invoked asynchronously on the main thread in the future. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeInvalidCustomToken - Indicates a validation error with + the custom token. + </li> + <li>@c FIRAuthErrorCodeCustomTokenMismatch - Indicates the service account and the API key + belong to different projects. + </li> + </ul> + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods. + */ +- (void)signInWithCustomToken:(NSString *)token + completion:(nullable FIRAuthResultCallback)completion; + +/** @fn createUserWithEmail:password:completion: + @brief Creates and, on success, signs in a user with the given email address and password. + + @param email The user's email address. + @param password The user's desired password. + @param completion Optionally; a block which is invoked when the sign up flow finishes, or is + canceled. Invoked asynchronously on the main thread in the future. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeInvalidEmail - Indicates the email address is malformed. + </li> + <li>@c FIRAuthErrorCodeEmailAlreadyInUse - Indicates the email used to attempt sign up + already exists. Call fetchProvidersForEmail to check which sign-in mechanisms the user + used, and prompt the user to sign in with one of those. + </li> + <li>@c FIRAuthErrorCodeOperationNotAllowed - Indicates that email and password accounts + are not enabled. Enable them in the Auth section of the Firebase console. + </li> + <li>@c FIRAuthErrorCodeWeakPassword - Indicates an attempt to set a password that is + considered too weak. The NSLocalizedFailureReasonErrorKey field in the NSError.userInfo + dictionary object will contain more detailed explanation that can be shown to the user. + </li> + </ul> + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods. + */ +- (void)createUserWithEmail:(NSString *)email + password:(NSString *)password + completion:(nullable FIRAuthResultCallback)completion; + +/** @fn confirmPasswordResetWithCode:newPassword:completion: + @brief Resets the password given a code sent to the user outside of the app and a new password + for the user. + + @param newPassword The new password. + @param completion Optionally; a block which is invoked when the request finishes. Invoked + asynchronously on the main thread in the future. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeWeakPassword - Indicates an attempt to set a password that is + considered too weak. + </li> + <li>@c FIRAuthErrorCodeOperationNotAllowed - Indicates the administrator disabled sign + in with the specified identity provider. + </li> + <li>@c FIRAuthErrorCodeExpiredActionCode - Indicates the OOB code is expired. + </li> + <li>@c FIRAuthErrorCodeInvalidActionCode - Indicates the OOB code is invalid. + </li> + </ul> + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods. + */ +- (void)confirmPasswordResetWithCode:(NSString *)code + newPassword:(NSString *)newPassword + completion:(FIRConfirmPasswordResetCallback)completion; + +/** @fn checkActionCode:completion: + @brief Checks the validity of an out of band code. + + @param code The out of band code to check validity. + @param completion Optionally; a block which is invoked when the request finishes. Invoked + asynchronously on the main thread in the future. + */ +- (void)checkActionCode:(NSString *)code completion:(FIRCheckActionCodeCallBack)completion; + +/** @fn verifyPasswordResetCode:completion: + @brief Checks the validity of a verify password reset code. + + @param code The password reset code to be verified. + @param completion Optionally; a block which is invoked when the request finishes. Invoked + asynchronously on the main thread in the future. + */ +- (void)verifyPasswordResetCode:(NSString *)code + completion:(FIRVerifyPasswordResetCodeCallback)completion; + +/** @fn applyActionCode:completion: + @brief Applies out of band code. + + @param code The out of band code to be applied. + @param completion Optionally; a block which is invoked when the request finishes. Invoked + asynchronously on the main thread in the future. + + @remarks This method will not work for out of band codes which require an additional parameter, + such as password reset code. + */ +- (void)applyActionCode:(NSString *)code + completion:(FIRApplyActionCodeCallback)completion; + +/** @fn sendPasswordResetWithEmail:completion: + @brief Initiates a password reset for the given email address. + + @param email The email address of the user. + @param completion Optionally; a block which is invoked when the request finishes. Invoked + asynchronously on the main thread in the future. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeInvalidRecipientEmail - Indicates an invalid recipient email was + sent in the request. + </li> + <li>@c FIRAuthErrorCodeInvalidSender - Indicates an invalid sender email is set in + the console for this action. + </li> + <li>@c FIRAuthErrorCodeInvalidMessagePayload - Indicates an invalid email template for + sending update email. + </li> + </ul> + */ +- (void)sendPasswordResetWithEmail:(NSString *)email + completion:(nullable FIRSendPasswordResetCallback)completion; + +/** @fn signOut: + @brief Signs out the current user. + + @param error Optionally; if an error occurs, upon return contains an NSError object that + describes the problem; is nil otherwise. + @return @YES when the sign out request was successful. @NO otherwise. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeKeychainError - Indicates an error occurred when accessing the + keychain. The @c NSLocalizedFailureReasonErrorKey field in the @c NSError.userInfo + dictionary will contain more information about the error encountered. + </li> + </ul> + + */ +- (BOOL)signOut:(NSError *_Nullable *_Nullable)error; + +/** @fn addAuthStateDidChangeListener: + @brief Registers a block as an "auth state did change" listener. To be invoked when: + + + The block is registered as a listener, + + A user with a different UID from the current user has signed in, or + + The current user has signed out. + + @param listener The block to be invoked. The block is always invoked asynchronously on the main + thread, even for it's initial invocation after having been added as a listener. + + @remarks The block is invoked immediately after adding it according to it's standard invocation + semantics, asynchronously on the main thread. Users should pay special attention to + making sure the block does not inadvertently retain objects which should not be retained by + the long-lived block. The block itself will be retained by @c FIRAuth until it is + unregistered or until the @c FIRAuth instance is otherwise deallocated. + + @return A handle useful for manually unregistering the block as a listener. + */ +- (FIRAuthStateDidChangeListenerHandle)addAuthStateDidChangeListener: + (FIRAuthStateDidChangeListenerBlock)listener; + +/** @fn removeAuthStateDidChangeListener: + @brief Unregisters a block as an "auth state did change" listener. + + @param listenerHandle The handle for the listener. + */ +- (void)removeAuthStateDidChangeListener:(FIRAuthStateDidChangeListenerHandle)listenerHandle; + +/** @fn addIDTokenDidChangeListener: + @brief Registers a block as an "ID token did change" listener. To be invoked when: + + + The block is registered as a listener, + + A user with a different UID from the current user has signed in, + + The ID token of the current user has been refreshed, or + + The current user has signed out. + + @param listener The block to be invoked. The block is always invoked asynchronously on the main + thread, even for it's initial invocation after having been added as a listener. + + @remarks The block is invoked immediately after adding it according to it's standard invocation + semantics, asynchronously on the main thread. Users should pay special attention to + making sure the block does not inadvertently retain objects which should not be retained by + the long-lived block. The block itself will be retained by @c FIRAuth until it is + unregistered or until the @c FIRAuth instance is otherwise deallocated. + + @return A handle useful for manually unregistering the block as a listener. + */ +- (FIRIDTokenDidChangeListenerHandle)addIDTokenDidChangeListener: + (FIRIDTokenDidChangeListenerBlock)listener; + +/** @fn removeIDTokenDidChangeListener: + @brief Unregisters a block as an "ID token did change" listener. + + @param listenerHandle The handle for the listener. + */ +- (void)removeIDTokenDidChangeListener:(FIRIDTokenDidChangeListenerHandle)listenerHandle; + +/** @fn setAPNSToken:type: + @brief Sets the APNs token along with its type. + @remarks If swizzling is disabled, the APNs Token must be set for phone number auth to work, + by either setting calling this method or by setting the @c APNSToken property. + */ +- (void)setAPNSToken:(NSData *)token type:(FIRAuthAPNSTokenType)type; + +/** @fn canHandleNotification: + @brief Whether the specific remote notification is handled by @c FIRAuth . + @param userInfo A dictionary that contains information related to the + notification in question. + @return Whether or the notification is handled. @c YES means the notification is for @c FIRAuth + so the caller should ignore the notification from further processing, and @c NO means the + the notification is for the app (or another libaray) so the caller should continue handling + this notification as usual. + @remarks If swizzling is disabled, related remote notifications must be forwarded to this method + for phone number auth to work. + */ +- (BOOL)canHandleNotification:(NSDictionary *)userInfo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuth.m b/Firebase/Auth/Source/FIRAuth.m new file mode 100644 index 0000000..d1beae6 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuth.m @@ -0,0 +1,1252 @@ +/* + * 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. + */ + +#import "Private/FIRAuth_Internal.h" + +#import "FIRAppAssociationRegistration.h" +#import "FIRAppInternal.h" +#import "FIROptions.h" +#import "FIRLogger.h" +#import "AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h" +#import "AuthProviders/Phone/FIRPhoneAuthCredential_Internal.h" +#import "Private/FIRAdditionalUserInfo_Internal.h" +#import "Private/FIRAuthAPNSToken.h" +#import "Private/FIRAuthAPNSTokenManager.h" +#import "Private/FIRAuthAppCredentialManager.h" +#import "Private/FIRAuthAppDelegateProxy.h" +#import "Private/FIRAuthCredential_Internal.h" +#import "Private/FIRAuthDataResult_Internal.h" +#import "Private/FIRAuthDispatcher.h" +#import "Private/FIRAuthErrorUtils.h" +#import "FIRAuthExceptionUtils.h" +#import "Private/FIRAuthGlobalWorkQueue.h" +#import "Private/FIRAuthKeychain.h" +#import "Private/FIRAuthNotificationManager.h" +#import "Private/FIRUser_Internal.h" +#import "FirebaseAuth.h" +#import "FIRAuthBackend.h" +#import "FIRCreateAuthURIRequest.h" +#import "FIRCreateAuthURIResponse.h" +#import "FIRGetOOBConfirmationCodeRequest.h" +#import "FIRGetOOBConfirmationCodeResponse.h" +#import "FIRResetPasswordRequest.h" +#import "FIRResetPasswordResponse.h" +#import "FIRSendVerificationCodeRequest.h" +#import "FIRSendVerificationCodeResponse.h" +#import "FIRSetAccountInfoRequest.h" +#import "FIRSetAccountInfoResponse.h" +#import "FIRSignUpNewUserRequest.h" +#import "FIRSignUpNewUserResponse.h" +#import "FIRVerifyAssertionRequest.h" +#import "FIRVerifyAssertionResponse.h" +#import "FIRVerifyCustomTokenRequest.h" +#import "FIRVerifyCustomTokenResponse.h" +#import "FIRVerifyPasswordRequest.h" +#import "FIRVerifyPasswordResponse.h" +#import "FIRVerifyPhoneNumberRequest.h" +#import "FIRVerifyPhoneNumberResponse.h" + +#pragma mark - Constants + +NSString *const FIRAuthStateDidChangeInternalNotification = + @"FIRAuthStateDidChangeInternalNotification"; +NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey = + @"FIRAuthStateDidChangeInternalNotificationTokenKey"; + +#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +const NSNotificationName FIRAuthStateDidChangeNotification = @"FIRAuthStateDidChangeNotification"; +#else +NSString *const FIRAuthStateDidChangeNotification = @"FIRAuthStateDidChangeNotification"; +#endif // defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + +/** @var kMaxWaitTimeForBackoff + @brief The maximum wait time before attempting to retry auto refreshing tokens after a failed + attempt. + @remarks This is the upper limit (in seconds) of the exponential backoff used for retrying + token refresh. + */ +static NSTimeInterval kMaxWaitTimeForBackoff = 16 * 60; + +/** @var kTokenRefreshHeadStart + @brief The amount of time before the token expires that proactive refresh should be attempted. + */ +NSTimeInterval kTokenRefreshHeadStart = 5 * 60; + +/** @var kUserKey + @brief Key of user stored in the keychain. Prefixed with a Firebase app name. + */ +static NSString *const kUserKey = @"%@_firebase_user"; + +/** @var kMissingEmailInvalidParameterExceptionReason + @brief The key of missing email key @c invalidParameterException. + */ +static NSString *const kEmailInvalidParameterReason = @"The email used to initiate user password " + "cannot be nil"; + +static NSString *const kPasswordResetRequestType = @"PASSWORD_RESET"; + +static NSString *const kVerifyEmailRequestType = @"VERIFY_EMAIL"; + +/** @var kMissingPasswordReason + @brief The reason why the @c FIRAuthErrorCodeWeakPassword error is thrown. + @remarks This error message will be localized in the future. + */ +static NSString *const kMissingPasswordReason = @"Missing Password"; + +/** @var gKeychainServiceNameForAppName + @brief A map from Firebase app name to keychain service names. + @remarks This map is needed for looking up the keychain service name after the FIRApp instance + is deleted, to remove the associated keychain item. Accessing should occur within a + @syncronized([FIRAuth class]) context. + */ +static NSMutableDictionary *gKeychainServiceNameForAppName; + +#pragma mark - FIRActionCodeInfo + +@implementation FIRActionCodeInfo { + /** @var _email + @brief The email address to which the code was sent. The new email address in the case of + FIRActionCodeOperationRecoverEmail. + */ + NSString *_email; + + /** @var _fromEmail + @brief The current email address in the case of FIRActionCodeOperationRecoverEmail. + */ + NSString *_fromEmail; +} + +- (NSString *)dataForKey:(FIRActionDataKey)key{ + switch (key) { + case FIRActionCodeEmailKey: + return _email; + case FIRActionCodeFromEmailKey: + return _fromEmail; + } +} + +- (instancetype)initWithOperation:(FIRActionCodeOperation)operation + email:(NSString *)email + newEmail:(nullable NSString *)newEmail { + self = [super init]; + if (self) { + _operation = operation; + if (newEmail) { + _email = [newEmail copy]; + _fromEmail = [email copy]; + } else { + _email = [email copy]; + } + } + return self; +} + +/** @fn actionCodeOperationForRequestType: + @brief Returns the corresponding operation type per provided request type string. + @param requestType Request type returned in in the server response. + @return The corresponding FIRActionCodeOperation for the supplied request type. + */ ++ (FIRActionCodeOperation)actionCodeOperationForRequestType:(NSString *)requestType { + if ([requestType isEqualToString:kPasswordResetRequestType]) { + return FIRActionCodeOperationPasswordReset; + } + if ([requestType isEqualToString:kVerifyEmailRequestType]) { + return FIRActionCodeOperationVerifyEmail; + } + return FIRActionCodeOperationUnknown; +} + +@end + +#pragma mark - FIRAuth + +@interface FIRAuth () <FIRAuthAppDelegateHandler> + +/** @property firebaseAppId + @brief The Firebase app ID. + */ +@property(nonatomic, copy, readonly) NSString *firebaseAppId; + +/** @fn initWithApp: + @brief Creates a @c FIRAuth instance associated with the provided @c FIRApp instance. + @param app The application to associate the auth instance with. + */ +- (instancetype)initWithApp:(FIRApp *)app; + +@end + +@implementation FIRAuth { + /** @var _firebaseAppName + @brief The Firebase app name. + */ + NSString *_firebaseAppName; + + /** @var _listenerHandles + @brief Handles returned from @c NSNotificationCenter for blocks which are "auth state did + change" notification listeners. + @remarks Mutations should occur within a @syncronized(self) context. + */ + NSMutableArray<FIRAuthStateDidChangeListenerHandle> *_listenerHandles; + + /** @var _keychain + @brief The keychain service. + */ + FIRAuthKeychain *_keychain; + + /** @var _autoRefreshTokens + @brief This flag denotes whether or not tokens should be automatically refreshed. + @remarks Will only be set to @YES if the another Firebase service is included (additionally to + Firebase Auth). + */ + BOOL _autoRefreshTokens; + + /** @var _autoRefreshScheduled + @brief Whether or not token auto-refresh is currently scheduled. + */ + BOOL _autoRefreshScheduled; + + /** @var _isAppInBackground + @brief A flag that is set to YES if the app is put in the background and no when the app is + returned to the foreground. + */ + BOOL _isAppInBackground; + + /** @var _applicationDidBecomeActiveObserver + @brief An opaque object to act as the observer for UIApplicationDidBecomeActiveNotification. + */ + id<NSObject> _applicationDidBecomeActiveObserver; + + /** @var _applicationDidBecomeActiveObserver + @brief An opaque object to act as the observer for + UIApplicationDidEnterBackgroundNotification. + */ + id<NSObject> _applicationDidEnterBackgroundObserver; +} + ++ (void)load { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + gKeychainServiceNameForAppName = [[NSMutableDictionary alloc] init]; + + NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter]; + + // Ensures the @c FIRAuth instance for a given app gets loaded as soon as the app is ready. + [defaultCenter addObserverForName:kFIRAppReadyToConfigureSDKNotification + object:[FIRApp class] + queue:nil + usingBlock:^(NSNotification *notification) { + [FIRAuth authWithApp:[FIRApp appNamed:notification.userInfo[kFIRAppNameKey]]]; + }]; + // Ensures the saved user is cleared when the app is deleted. + [defaultCenter addObserverForName:kFIRAppDeleteNotification + object:[FIRApp class] + queue:nil + usingBlock:^(NSNotification *notification) { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + // This doesn't stop any request already issued, see b/27704535 . + NSString *appName = notification.userInfo[kFIRAppNameKey]; + NSString *keychainServiceName = [FIRAuth keychainServiceNameForAppName:appName]; + if (keychainServiceName) { + [self deleteKeychainServiceNameForAppName:appName]; + FIRAuthKeychain *keychain = [[FIRAuthKeychain alloc] initWithService:keychainServiceName]; + NSString *userKey = [NSString stringWithFormat:kUserKey, appName]; + [keychain removeDataForKey:userKey error:NULL]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] + postNotificationName:FIRAuthStateDidChangeNotification + object:nil]; + }); + }); + }]; + }); +} + ++ (FIRAuth *)auth { + FIRApp *defaultApp = [FIRApp defaultApp]; + if (!defaultApp) { + [NSException raise:NSInternalInconsistencyException + format:@"The default FIRApp instance must be configured before the default FIRAuth" + @"instance can be initialized. One way to ensure that is to call " + @"`[FIRApp configure];` is called in " + @"`application:didFinishLaunchingWithOptions:`."]; + } + return [self authWithApp:defaultApp]; +} + ++ (FIRAuth *)authWithApp:(FIRApp *)app { + return [FIRAppAssociationRegistration registeredObjectWithHost:app + key:NSStringFromClass(self) + creationBlock:^FIRAuth *_Nullable() { + return [[FIRAuth alloc] initWithApp:app]; + }]; +} + +- (instancetype)initWithApp:(FIRApp *)app { + [FIRAuth setKeychainServiceNameForApp:app]; + self = [self initWithAPIKey:app.options.APIKey appName:app.name]; + if (self) { + _app = app; + __weak FIRAuth *weakSelf = self; + app.getTokenImplementation = ^(BOOL forceRefresh, FIRTokenCallback callback) { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + FIRAuth *strongSelf = weakSelf; + if (strongSelf && !strongSelf->_autoRefreshTokens) { + FIRLogInfo(kFIRLoggerAuth, @"I-AUT000002", @"Token auto-refresh enabled."); + strongSelf->_autoRefreshTokens = YES; + strongSelf->_applicationDidBecomeActiveObserver = [[NSNotificationCenter defaultCenter] + addObserverForName:UIApplicationDidBecomeActiveNotification + object:nil + queue:nil + usingBlock:^(NSNotification *notification) { + FIRAuth *strongSelf = weakSelf; + if (strongSelf) { + strongSelf->_isAppInBackground = NO; + if (!strongSelf->_autoRefreshScheduled) { + [weakSelf scheduleAutoTokenRefresh]; + } + } + }]; + strongSelf->_applicationDidEnterBackgroundObserver = [[NSNotificationCenter defaultCenter] + addObserverForName:UIApplicationDidEnterBackgroundNotification + object:nil + queue:nil + usingBlock:^(NSNotification *notification) { + FIRAuth *strongSelf = weakSelf; + if (strongSelf) { + strongSelf->_isAppInBackground = YES; + } + }]; + } + if (!strongSelf.currentUser) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(nil, nil); + }); + return; + } + [strongSelf.currentUser internalGetTokenForcingRefresh:forceRefresh + callback:^(NSString *_Nullable token, + NSError *_Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(token, error); + }); + }]; + }); + }; + app.getUIDImplementation = ^NSString *_Nullable() { + __block NSString *uid; + dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ + uid = [weakSelf getUID]; + }); + return uid; + }; + } + return self; +} + +- (instancetype)initWithAPIKey:(NSString *)APIKey appName:(NSString *)appName { + self = [super init]; + if (self) { + _listenerHandles = [NSMutableArray array]; + _APIKey = [APIKey copy]; + _firebaseAppName = [appName copy]; + NSString *keychainServiceName = [FIRAuth keychainServiceNameForAppName:appName]; + if (keychainServiceName) { + _keychain = [[FIRAuthKeychain alloc] initWithService:keychainServiceName]; + } + // Load current user from keychain. + FIRUser *user; + NSError *error; + if ([self getUser:&user error:&error]) { + [self updateCurrentUser:user byForce:NO savingToDisk:NO error:&error]; + } else { + FIRLogError(kFIRLoggerAuth, @"I-AUT000001", + @"Error loading saved user when starting up: %@", error); + } + // Initialize for phone number auth. + _tokenManager = + [[FIRAuthAPNSTokenManager alloc] initWithApplication:[UIApplication sharedApplication]]; + _appCredentialManager = [[FIRAuthAppCredentialManager alloc] initWithKeychain:_keychain]; + _notificationManager = + [[FIRAuthNotificationManager alloc] initWithApplication:[UIApplication sharedApplication] + appCredentialManager:_appCredentialManager]; + [[FIRAuthAppDelegateProxy sharedInstance] addHandler:self]; + } + return self; +} + +- (void)dealloc { + @synchronized (self) { + NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter]; + while (_listenerHandles.count != 0) { + FIRAuthStateDidChangeListenerHandle handleToRemove = _listenerHandles.lastObject; + [defaultCenter removeObserver:handleToRemove]; + [_listenerHandles removeLastObject]; + } + [defaultCenter removeObserver:_applicationDidBecomeActiveObserver + name:UIApplicationDidBecomeActiveNotification + object:nil]; + [defaultCenter removeObserver:_applicationDidEnterBackgroundObserver + name:UIApplicationDidEnterBackgroundNotification + object:nil]; + } +} + +#pragma mark - Public API + +- (void)fetchProvidersForEmail:(NSString *)email + completion:(FIRProviderQueryCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + FIRCreateAuthURIRequest *request = + [[FIRCreateAuthURIRequest alloc] initWithIdentifier:email + continueURI:@"http://www.google.com/" + APIKey:_APIKey]; + [FIRAuthBackend createAuthURI:request callback:^(FIRCreateAuthURIResponse *_Nullable response, + NSError *_Nullable error) { + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(response.allProviders, error); + }); + } + }]; + }); +} + +- (void)signInWithEmail:(NSString *)email + password:(NSString *)password + completion:(FIRAuthResultCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + [self signInWithEmail:email + password:password + callback:[self signInFlowAuthResultCallbackByDecoratingCallback:completion]]; + }); +} + +/** @fn signInWithEmail:password:callback: + @brief Signs in using an email address and password. + @param email The user's email address. + @param password The user's password. + @param callback A block which is invoked when the sign in finishes (or is cancelled.) Invoked + asynchronously on the global auth work queue in the future. + @remarks This is the internal counterpart of this method, which uses a callback that does not + update the current user. + */ +- (void)signInWithEmail:(NSString *)email + password:(NSString *)password + callback:(FIRAuthResultCallback)callback { + FIRVerifyPasswordRequest *request = + [[FIRVerifyPasswordRequest alloc] initWithEmail:email password:password APIKey:_APIKey]; + + if (![request.password length]) { + callback(nil, [FIRAuthErrorUtils wrongPasswordErrorWithMessage:nil]); + return; + } + [FIRAuthBackend verifyPassword:request + callback:^(FIRVerifyPasswordResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + callback(nil, error); + return; + } + [self completeSignInWithAccessToken:response.IDToken + accessTokenExpirationDate:response.approximateExpirationDate + refreshToken:response.refreshToken + anonymous:NO + callback:callback]; + }]; +} + +- (void)signInWithCredential:(FIRAuthCredential *)credential + completion:(FIRAuthResultCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + FIRAuthResultCallback callback = + [self signInFlowAuthResultCallbackByDecoratingCallback:completion]; + [self internalSignInWithCredential:credential callback:callback]; + }); +} + +- (void)signInAndRetrieveDataWithCredential:(FIRAuthCredential *)credential + completion:(nullable FIRAuthDataResultCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + FIRAuthDataResultCallback callback = + [self signInFlowAuthDataResultCallbackByDecoratingCallback:completion]; + [self internalSignInAndRetrieveDataWithCredential:credential + isReauthentication:NO + callback:callback]; + }); +} + +- (void)internalSignInWithCredential:(FIRAuthCredential *)credential + callback:(FIRAuthResultCallback)callback { + [self internalSignInAndRetrieveDataWithCredential:credential + isReauthentication:NO + callback:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + callback(authResult.user, error); + }]; +} + +- (void)internalSignInAndRetrieveDataWithCredential:(FIRAuthCredential *)credential + isReauthentication:(BOOL)isReauthentication + callback:(nullable FIRAuthDataResultCallback)callback { + if ([credential isKindOfClass:[FIREmailPasswordAuthCredential class]]) { + // Special case for email/password credentials: + FIREmailPasswordAuthCredential *emailPasswordCredential = + (FIREmailPasswordAuthCredential *)credential; + [self signInWithEmail:emailPasswordCredential.email + password:emailPasswordCredential.password + callback:^(FIRUser *_Nullable user, NSError *_Nullable error) { + if (callback) { + FIRAuthDataResult *result = user ? + [[FIRAuthDataResult alloc] initWithUser:user additionalUserInfo:nil] : nil; + callback(result, error); + } + }]; + return; + } + + if ([credential isKindOfClass:[FIRPhoneAuthCredential class]]) { + // Special case for phone auth credential + FIRPhoneAuthCredential *phoneCredential = (FIRPhoneAuthCredential *)credential; + [self signInWithPhoneCredential:phoneCredential callback:^(FIRUser *_Nullable user, + NSError *_Nullable error) { + if (callback) { + FIRAuthDataResult *result = user ? + [[FIRAuthDataResult alloc] initWithUser:user additionalUserInfo:nil] : nil; + callback(result, error); + } + }]; + return; + } + + FIRVerifyAssertionRequest *request = + [[FIRVerifyAssertionRequest alloc] initWithAPIKey:_APIKey providerID:credential.provider]; + request.autoCreate = !isReauthentication; + [credential prepareVerifyAssertionRequest:request]; + [FIRAuthBackend verifyAssertion:request + callback:^(FIRVerifyAssertionResponse *response, NSError *error) { + if (error) { + if (callback) { + callback(nil, error); + } + return; + } + + if (response.needConfirmation) { + if (callback) { + NSString *email = response.email; + callback(nil, [FIRAuthErrorUtils accountExistsWithDifferentCredentialErrorWithEmail:email]); + } + return; + } + + if (!response.providerID.length) { + if (callback) { + callback(nil, [FIRAuthErrorUtils unexpectedResponseWithDeserializedResponse:response]); + } + return; + } + [self completeSignInWithAccessToken:response.IDToken + accessTokenExpirationDate:response.approximateExpirationDate + refreshToken:response.refreshToken + anonymous:NO + callback:^(FIRUser *_Nullable user, NSError *_Nullable error) { + if (callback) { + FIRAdditionalUserInfo *additionalUserInfo = + [FIRAdditionalUserInfo userInfoWithVerifyAssertionResponse:response]; + FIRAuthDataResult *result = user ? + [[FIRAuthDataResult alloc] initWithUser:user + additionalUserInfo:additionalUserInfo] : nil; + callback(result, error); + } + }]; + }]; +} + +- (void)signInWithCredential:(FIRAuthCredential *)credential + callback:(FIRAuthResultCallback)callback { + [self signInAndRetrieveDataWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + callback(authResult.user, error); + }]; +} + +- (void)signInAnonymouslyWithCompletion:(FIRAuthResultCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + FIRAuthResultCallback decoratedCallback = + [self signInFlowAuthResultCallbackByDecoratingCallback:completion]; + if (_currentUser.anonymous) { + decoratedCallback(_currentUser, nil); + return; + } + FIRSignUpNewUserRequest *request = [[FIRSignUpNewUserRequest alloc] initWithAPIKey:_APIKey]; + [FIRAuthBackend signUpNewUser:request + callback:^(FIRSignUpNewUserResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + decoratedCallback(nil, error); + return; + } + [self completeSignInWithAccessToken:response.IDToken + accessTokenExpirationDate:response.approximateExpirationDate + refreshToken:response.refreshToken + anonymous:YES + callback:decoratedCallback]; + }]; + }); +} + +- (void)signInWithCustomToken:(NSString *)token + completion:(nullable FIRAuthResultCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + FIRAuthResultCallback decoratedCallback = + [self signInFlowAuthResultCallbackByDecoratingCallback:completion]; + FIRVerifyCustomTokenRequest *request = + [[FIRVerifyCustomTokenRequest alloc] initWithToken:token APIKey:_APIKey]; + [FIRAuthBackend verifyCustomToken:request + callback:^(FIRVerifyCustomTokenResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + decoratedCallback(nil, error); + return; + } + [self completeSignInWithAccessToken:response.IDToken + accessTokenExpirationDate:response.approximateExpirationDate + refreshToken:response.refreshToken + anonymous:NO + callback:decoratedCallback]; + }]; + }); +} + +- (void)createUserWithEmail:(NSString *)email + password:(NSString *)password + completion:(nullable FIRAuthResultCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + FIRAuthResultCallback decoratedCallback = + [self signInFlowAuthResultCallbackByDecoratingCallback:completion]; + FIRSignUpNewUserRequest *request = [[FIRSignUpNewUserRequest alloc] initWithAPIKey:_APIKey + email:email + password:password + displayName:nil]; + if (![request.password length]) { + decoratedCallback(nil, [FIRAuthErrorUtils + weakPasswordErrorWithServerResponseReason:kMissingPasswordReason]); + return; + } + [FIRAuthBackend signUpNewUser:request + callback:^(FIRSignUpNewUserResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + decoratedCallback(nil, error); + return; + } + [self completeSignInWithAccessToken:response.IDToken + accessTokenExpirationDate:response.approximateExpirationDate + refreshToken:response.refreshToken + anonymous:NO + callback:decoratedCallback]; + }]; + }); +} + +- (void)confirmPasswordResetWithCode:(NSString *)code + newPassword:(NSString *)newPassword + completion:(FIRConfirmPasswordResetCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + FIRResetPasswordRequest *request = + [[FIRResetPasswordRequest alloc] initWithAPIKey:_APIKey + oobCode:code + newPassword:newPassword]; + [FIRAuthBackend resetPassword:request callback:^(FIRResetPasswordResponse *_Nullable response, + NSError *_Nullable error) { + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + if (error) { + completion(error); + return; + } + completion(nil); + }); + } + }]; + }); +} + +- (void)checkActionCode:(NSString *)code completion:(FIRCheckActionCodeCallBack)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^ { + FIRResetPasswordRequest *request = + [[FIRResetPasswordRequest alloc] initWithAPIKey:_APIKey + oobCode:code + newPassword:nil]; + [FIRAuthBackend resetPassword:request callback:^(FIRResetPasswordResponse *_Nullable response, + NSError *_Nullable error) { + if (completion) { + if (error) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(nil, error); + }); + return; + } + FIRActionCodeOperation operation = + [FIRActionCodeInfo actionCodeOperationForRequestType:response.requestType]; + FIRActionCodeInfo *actionCodeInfo = + [[FIRActionCodeInfo alloc] initWithOperation:operation + email:response.email + newEmail:response.verifiedEmail]; + dispatch_async(dispatch_get_main_queue(), ^{ + completion(actionCodeInfo, nil); + }); + } + }]; + }); +} + +- (void)verifyPasswordResetCode:(NSString *)code + completion:(FIRVerifyPasswordResetCodeCallback)completion { + [self checkActionCode:code completion:^(FIRActionCodeInfo *_Nullable info, + NSError *_Nullable error) { + if (completion) { + if (error) { + completion(nil, error); + return; + } + completion([info dataForKey:FIRActionCodeEmailKey], nil); + } + }]; +} + +- (void)applyActionCode:(NSString *)code completion:(FIRApplyActionCodeCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^ { + FIRSetAccountInfoRequest *request = [[FIRSetAccountInfoRequest alloc]initWithAPIKey:_APIKey]; + request.OOBCode = code; + [FIRAuthBackend setAccountInfo:request callback:^(FIRSetAccountInfoResponse *_Nullable response, + NSError *_Nullable error) { + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(error); + }); + } + }]; + }); +} + +- (void)sendPasswordResetWithEmail:(NSString *)email + completion:(nullable FIRSendPasswordResetCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + if (!email) { + [FIRAuthExceptionUtils raiseInvalidParameterExceptionWithReason:kEmailInvalidParameterReason]; + } + FIRGetOOBConfirmationCodeRequest *request = + [FIRGetOOBConfirmationCodeRequest passwordResetRequestWithEmail:email APIKey:_APIKey]; + [FIRAuthBackend getOOBConfirmationCode:request + callback:^(FIRGetOOBConfirmationCodeResponse *_Nullable response, + NSError *_Nullable error) { + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(error); + }); + } + }]; + }); +} + +- (BOOL)signOut:(NSError *_Nullable *_Nullable)error { + __block BOOL result = YES; + dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ + if (!_currentUser) { + return; + } + result = [self updateCurrentUser:nil byForce:NO savingToDisk:YES error:error]; + }); + return result; +} + +- (BOOL)signOutByForceWithUserID:(NSString *)userID error:(NSError *_Nullable *_Nullable)error { + if (_currentUser.uid != userID) { + return YES; + } + return [self updateCurrentUser:nil byForce:YES savingToDisk:YES error:error]; +} + +- (FIRAuthStateDidChangeListenerHandle)addAuthStateDidChangeListener: + (FIRAuthStateDidChangeListenerBlock)listener { + __block BOOL firstInvocation = YES; + __block NSString *previousUserID; + return [self addIDTokenDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) { + BOOL shouldCallListener = firstInvocation || + !(previousUserID == user.uid || [previousUserID isEqualToString:user.uid]); + firstInvocation = NO; + previousUserID = [user.uid copy]; + if (shouldCallListener) { + listener(auth, user); + } + }]; +} + +- (void)removeAuthStateDidChangeListener:(FIRAuthStateDidChangeListenerHandle)listenerHandle { + [self removeIDTokenDidChangeListener:listenerHandle]; +} + +- (FIRIDTokenDidChangeListenerHandle)addIDTokenDidChangeListener: + (FIRIDTokenDidChangeListenerBlock)listener { + if (!listener) { + [NSException raise:NSInvalidArgumentException format:@"listener must not be nil."]; + return nil; + } + FIRAuthStateDidChangeListenerHandle handle; + NSNotificationCenter *notifications = [NSNotificationCenter defaultCenter]; + handle = [notifications addObserverForName:FIRAuthStateDidChangeNotification + object:self + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification *_Nonnull notification) { + FIRAuth *auth = notification.object; + listener(auth, auth.currentUser); + }]; + @synchronized (self) { + [_listenerHandles addObject:handle]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + listener(self, self.currentUser); + }); + return handle; +} + +- (void)removeIDTokenDidChangeListener:(FIRIDTokenDidChangeListenerHandle)listenerHandle { + [[NSNotificationCenter defaultCenter] removeObserver:listenerHandle]; + @synchronized (self) { + [_listenerHandles removeObject:listenerHandle]; + } +} + +- (NSData *)APNStoken { + __block NSData *result = nil; + dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ + result = _tokenManager.token.data; + }); + return result; +} + +- (void)setAPNSToken:(NSData *)APNSToken { + [self setAPNSToken:APNSToken type:FIRAuthAPNSTokenTypeUnknown]; +} + +- (void)setAPNSToken:(NSData *)token type:(FIRAuthAPNSTokenType)type { + dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ + _tokenManager.token = [[FIRAuthAPNSToken alloc] initWithData:token type:type]; + }); +} + +- (BOOL)canHandleNotification:(NSDictionary *)userInfo { + __block BOOL result = NO; + dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ + result = [_notificationManager canHandleNotification:userInfo]; + }); + return result; +} + +#pragma mark - Internal Methods + +/** @fn signInWithPhoneCredential:callback: + @brief Signs in using a phone credential. + @param credential The Phone Auth credential used to sign in. + @param callback A block which is invoked when the sign in finishes (or is cancelled.) Invoked + asynchronously on the global auth work queue in the future. + */ +- (void)signInWithPhoneCredential:(FIRPhoneAuthCredential *)credential + callback:(FIRAuthResultCallback)callback { + if (credential.temporaryProof.length && credential.phoneNumber.length) { + FIRVerifyPhoneNumberRequest *request = + [[FIRVerifyPhoneNumberRequest alloc] initWithTemporaryProof:credential.temporaryProof + phoneNumber:credential.phoneNumber + APIKey:_APIKey]; + [self phoneNumberSignInWithRequest:request callback:callback]; + return; + } + + if (!credential.verificationID.length) { + callback(nil, [FIRAuthErrorUtils missingVerificationIDErrorWithMessage:nil]); + return; + } + if (!credential.verificationCode.length) { + callback(nil, [FIRAuthErrorUtils missingVerificationCodeErrorWithMessage:nil]); + return; + } + FIRVerifyPhoneNumberRequest *request = + [[FIRVerifyPhoneNumberRequest alloc]initWithVerificationID:credential.verificationID + verificationCode:credential.verificationCode + APIKey:_APIKey]; + [self phoneNumberSignInWithRequest:request callback:callback]; +} + + +/** @fn phoneNumberSignInWithVerificationID:pasverificationCodesword:callback: + @brief Signs in using a FIRVerifyPhoneNumberRequest object. + @param request THe FIRVerifyPhoneNumberRequest request object. + @param callback A block which is invoked when the sign in finishes (or is cancelled.) Invoked + asynchronously on the global auth work queue in the future. + */ +- (void)phoneNumberSignInWithRequest:(FIRVerifyPhoneNumberRequest *)request + callback:(FIRAuthResultCallback)callback { + [FIRAuthBackend verifyPhoneNumber:request + callback:^(FIRVerifyPhoneNumberResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + callback(nil, error); + return; + } + [self completeSignInWithAccessToken:response.IDToken + accessTokenExpirationDate:response.approximateExpirationDate + refreshToken:response.refreshToken + anonymous:NO + callback:callback]; + }]; +} + +- (void)notifyListenersOfAuthStateChangeWithUser:(FIRUser *)user token:(NSString *)token { + if (user && _autoRefreshTokens) { + // Shedule new refresh task after successful attempt. + [self scheduleAutoTokenRefresh]; + } + if (user == _currentUser) { + NSMutableDictionary *internalNotificationParameters = [NSMutableDictionary dictionary]; + if (token.length) { + internalNotificationParameters[FIRAuthStateDidChangeInternalNotificationTokenKey] = token; + } + NSNotificationCenter *notifications = [NSNotificationCenter defaultCenter]; + dispatch_async(dispatch_get_main_queue(), ^{ + [notifications postNotificationName:FIRAuthStateDidChangeInternalNotification + object:self + userInfo:internalNotificationParameters]; + [notifications postNotificationName:FIRAuthStateDidChangeNotification + object:self]; + }); + } +} + +- (BOOL)updateKeychainWithUser:(FIRUser *)user error:(NSError *_Nullable *_Nullable)error { + if (user == _currentUser) { + return [self saveUser:user error:error]; + } + // No-op if the user is no longer signed in. This is not considered an error as we don't check + // whether the user is still current on other callbacks of user operations either. + return YES; +} + +/** @fn setKeychainServiceNameForApp + @brief Sets the keychain service name global data for the particular app. + @param app The Firebase app to set keychain service name for. + */ ++ (void)setKeychainServiceNameForApp:(FIRApp *)app { + @synchronized (self) { + gKeychainServiceNameForAppName[app.name] = + [@"firebase_auth_" stringByAppendingString:app.options.googleAppID]; + } +} + +/** @fn keychainServiceNameForAppName: + @brief Gets the keychain service name global data for the particular app by name. + @param appName The name of the Firebase app to get keychain service name for. + */ ++ (NSString *)keychainServiceNameForAppName:(NSString *)appName { + @synchronized (self) { + return gKeychainServiceNameForAppName[appName]; + } +} + +/** @fn deleteKeychainServiceNameForAppName: + @brief Deletes the keychain service name global data for the particular app by name. + @param appName The name of the Firebase app to delete keychain service name for. + */ ++ (void)deleteKeychainServiceNameForAppName:(NSString *)appName { + @synchronized (self) { + [gKeychainServiceNameForAppName removeObjectForKey:appName]; + } +} + +/** @fn scheduleAutoTokenRefreshWithDelay: + @brief Schedules a task to automatically refresh tokens on the current user. The token refresh + is scheduled 5 minutes before the scheduled expiration time. + @remarks If the token expires in less than 5 minutes, schedule the token refresh immediately. + */ +- (void)scheduleAutoTokenRefresh { + NSTimeInterval tokenExpirationInterval = + [_currentUser.accessTokenExpirationDate timeIntervalSinceNow] - kTokenRefreshHeadStart; + [self scheduleAutoTokenRefreshWithDelay:MAX(tokenExpirationInterval, 0) retry:NO]; +} + +/** @fn scheduleAutoTokenRefreshWithDelay: + @brief Schedules a task to automatically refresh tokens on the current user. + @param delay The delay in seconds after which the token refresh task should be scheduled to be + executed. + @param retry Flag to determine whether the invocation is a retry attempt or not. + */ +- (void)scheduleAutoTokenRefreshWithDelay:(NSTimeInterval)delay retry:(BOOL)retry { + NSString *accessToken = _currentUser.rawAccessToken; + if (!accessToken) { + return; + } + if (retry) { + FIRLogNotice(kFIRLoggerAuth, @"I-AUT000003", + @"Token auto-refresh re-scheduled in %02d:%02d " + @"because of error on previous refresh attempt.", + (int)ceil(delay) / 60, (int)ceil(delay) % 60); + } else { + FIRLogInfo(kFIRLoggerAuth, @"I-AUT000004", + @"Token auto-refresh scheduled in %02d:%02d for the new token.", + (int)ceil(delay) / 60, (int)ceil(delay) % 60); + } + _autoRefreshScheduled = YES; + __weak FIRAuth *weakSelf = self; + [[FIRAuthDispatcher sharedInstance] dispatchAfterDelay:delay + queue:FIRAuthGlobalWorkQueue() + task:^(void) { + FIRAuth *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + if (![strongSelf->_currentUser.rawAccessToken isEqualToString:accessToken]) { + // Another auto refresh must have been scheduled, so keep _autoRefreshScheduled unchanged. + return; + } + strongSelf->_autoRefreshScheduled = NO; + if (strongSelf->_isAppInBackground) { + return; + } + NSString *uid = strongSelf->_currentUser.uid; + [strongSelf->_currentUser internalGetTokenForcingRefresh:YES + callback:^(NSString *_Nullable token, + NSError *_Nullable error) { + if (![strongSelf->_currentUser.uid isEqualToString:uid]) { + return; + } + // If the error is an invalid token, sign the user out. + if (error.code == FIRAuthErrorCodeInvalidUserToken) { + FIRLogWarning(kFIRLoggerAuth, @"I-AUT000005", + @"Invalid refresh token detected, user is automatically signed out."); + [strongSelf signOutByForceWithUserID:uid error:nil]; + return; + } + if (error) { + // Kicks off exponential back off logic to retry failed attempt. Starts with one minute + // delay (60 seconds) if this is the first failed attempt. + NSTimeInterval rescheduleDelay; + if (retry) { + rescheduleDelay = MIN(delay * 2, kMaxWaitTimeForBackoff); + } else { + rescheduleDelay = 60; + } + [strongSelf scheduleAutoTokenRefreshWithDelay:rescheduleDelay retry:YES]; + } + }]; + }]; +} + +#pragma mark - + +/** @fn completeSignInWithTokenService:callback: + @brief Completes a sign-in flow once we have access and refresh tokens for the user. + @param accessToken The STS access token. + @param accessTokenExpirationDate The approximate expiration date of the access token. + @param refreshToken The STS refresh token. + @param anonymous Whether or not the user is anonymous. + @param callback Called when the user has been signed in or when an error occurred. Invoked + asynchronously on the global auth work queue in the future. + */ +- (void)completeSignInWithAccessToken:(NSString *)accessToken + accessTokenExpirationDate:(NSDate *)accessTokenExpirationDate + refreshToken:(NSString *)refreshToken + anonymous:(BOOL)anonymous + callback:(FIRAuthResultCallback)callback { + [FIRUser retrieveUserWithAPIKey:_APIKey + accessToken:accessToken + accessTokenExpirationDate:accessTokenExpirationDate + refreshToken:refreshToken + anonymous:anonymous + callback:callback]; +} + +/** @fn signInFlowAuthResultCallbackByDecoratingCallback: + @brief Creates a FIRAuthResultCallback block which wraps another FIRAuthResultCallback; trying + to update the current user before forwarding it's invocations along to a subject block + @param callback Called when the user has been updated or when an error has occurred. Invoked + asynchronously on the main thread in the future. + @return Returns a block that updates the current user. + @remarks Typically invoked as part of the complete sign-in flow. For any other uses please + consider alternative ways of updating the current user. +*/ +- (FIRAuthResultCallback)signInFlowAuthResultCallbackByDecoratingCallback: + (nullable FIRAuthResultCallback)callback { + return ^(FIRUser *_Nullable user, NSError *_Nullable error) { + if (error) { + if (callback) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(nil, error); + }); + } + return; + } + if (![self updateCurrentUser:user byForce:NO savingToDisk:YES error:&error]) { + if (callback) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(nil, error); + }); + } + return; + } + if (callback) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(user, nil); + }); + } + }; +} + +/** @fn signInFlowAuthDataResultCallbackByDecoratingCallback: + @brief Creates a FIRAuthDataResultCallback block which wraps another FIRAuthDataResultCallback; + trying to update the current user before forwarding it's invocations along to a subject + block. + @param callback Called when the user has been updated or when an error has occurred. Invoked + asynchronously on the main thread in the future. + @return Returns a block that updates the current user. + @remarks Typically invoked as part of the complete sign-in flow. For any other uses please + consider alternative ways of updating the current user. +*/ +- (FIRAuthDataResultCallback)signInFlowAuthDataResultCallbackByDecoratingCallback: + (nullable FIRAuthDataResultCallback)callback { + return ^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) { + if (error) { + if (callback) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(nil, error); + }); + } + return; + } + if (![self updateCurrentUser:authResult.user byForce:NO savingToDisk:YES error:&error]) { + if (callback) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(nil, error); + }); + } + return; + } + if (callback) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(authResult, nil); + }); + } + }; +} + +#pragma mark - User-Related Methods + +/** @fn updateCurrentUser:savingToDisk: + @brief Update the current user; initializing the user's internal properties correctly, and + optionally saving the user to disk. + @remarks This method is called during: sign in and sign out events, as well as during class + initialization time. The only time the saveToDisk parameter should be set to NO is during + class initialization time because the user was just read from disk. + @param user The user to use as the current user (including nil, which is passed at sign out + time.) + @param saveToDisk Indicates the method should persist the user data to disk. + */ +- (BOOL)updateCurrentUser:(FIRUser *)user + byForce:(BOOL)force + savingToDisk:(BOOL)saveToDisk + error:(NSError *_Nullable *_Nullable)error { + if (user == _currentUser) { + return YES; + } + BOOL success = YES; + if (saveToDisk) { + success = [self saveUser:user error:error]; + } + if (success || force) { + FIRUser *previousUser = _currentUser; + previousUser.auth = nil; + _currentUser = user; + _currentUser.auth = self; + [self notifyListenersOfAuthStateChangeWithUser:user token:user.rawAccessToken]; + } + return success; +} + +/** @fn saveUser:error: + @brief Persists user. + @param user The user to save. + @param error Return value for any error which occurs. + @return @YES on success, @NO otherwise. + */ +- (BOOL)saveUser:(FIRUser *)user + error:(NSError *_Nullable *_Nullable)error { + BOOL success; + NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName]; + + if (!user) { + success = [_keychain removeDataForKey:userKey error:error]; + } else { + // Encode the user object. + NSMutableData *archiveData = [NSMutableData data]; + NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:archiveData]; + [archiver encodeObject:user forKey:userKey]; + [archiver finishEncoding]; + + // Save the user object's encoded value. + success = [_keychain setData:archiveData forKey:userKey error:error]; + } + return success; +} + +/** @fn getUser:error: + @brief Retrieves the saved user associated, if one exists, from the keychain. + @param outUser An out parameter which is populated with the saved user, if one exists. + @param error Return value for any error which occurs. + @return YES if the operation was a success (irrespective of whether or not a saved user existed + for the given @c firebaseAppId,) NO if an error occurred. + */ +- (BOOL)getUser:(FIRUser *_Nullable *)outUser + error:(NSError *_Nullable *_Nullable)error { + NSString *userKey = [NSString stringWithFormat:kUserKey, _firebaseAppName]; + + NSError *keychainError; + NSData *encodedUserData = [_keychain dataForKey:userKey error:&keychainError]; + if (keychainError) { + if (error) { + *error = keychainError; + } + return NO; + } + if (!encodedUserData) { + *outUser = nil; + return YES; + } + NSKeyedUnarchiver *unarchiver = + [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedUserData]; + *outUser = [unarchiver decodeObjectOfClass:[FIRUser class] forKey:userKey]; + return YES; +} + +/** @fn getUID + @brief Gets the identifier of the current user, if any. + @return The identifier of the current user, or nil if there is no current user. + */ +- (nullable NSString *)getUID { + return _currentUser.uid; +} + +@end diff --git a/Firebase/Auth/Source/FIRAuthAPNSToken.m b/Firebase/Auth/Source/FIRAuthAPNSToken.m new file mode 100644 index 0000000..fc5ee2d --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthAPNSToken.m @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#import "Private/FIRAuthAPNSToken.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation FIRAuthAPNSToken + +- (instancetype)initWithData:(NSData *)data type:(FIRAuthAPNSTokenType)type { + self = [super init]; + if (self) { + _data = [data copy]; + _type = type; + } + return self; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthAPNSTokenManager.m b/Firebase/Auth/Source/FIRAuthAPNSTokenManager.m new file mode 100644 index 0000000..9609d86 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthAPNSTokenManager.m @@ -0,0 +1,255 @@ +/* + * 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. + */ + +#import "Private/FIRAuthAPNSTokenManager.h" + +#import "FIRLogger.h" +#import "Private/FIRAuthAPNSToken.h" +#import "FIRAuthGlobalWorkQueue.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @var kRegistrationTimeout + @brief Timeout for registration for remote notification. + @remarks Once we start to handle `application:didFailToRegisterForRemoteNotificationsWithError:` + we probably don't have to use timeout at all. + */ +static const NSTimeInterval kRegistrationTimeout = 5; + +/** @var kLegacyRegistrationTimeout + @brief Timeout for registration for remote notification on iOS 7. + */ +static const NSTimeInterval kLegacyRegistrationTimeout = 30; + +@implementation FIRAuthAPNSTokenManager { + /** @var _application + @brief The @c UIApplication to request the token from. + */ + UIApplication *_application; + + /** @var _pendingCallbacks + @brief The list of all pending callbacks for the APNs token. + */ + NSMutableArray<FIRAuthAPNSTokenCallback> *_pendingCallbacks; +} + +- (instancetype)initWithApplication:(UIApplication *)application { + self = [super init]; + if (self) { + _application = application; + _timeout = [_application respondsToSelector:@selector(registerForRemoteNotifications)] ? + kRegistrationTimeout : kLegacyRegistrationTimeout; + } + return self; +} + +- (void)getTokenWithCallback:(FIRAuthAPNSTokenCallback)callback { + if (_token) { + callback(_token); + return; + } + if (_pendingCallbacks) { + [_pendingCallbacks addObject:callback]; + return; + } + _pendingCallbacks = + [[NSMutableArray<FIRAuthAPNSTokenCallback> alloc] initWithObjects:callback, nil]; + dispatch_async(dispatch_get_main_queue(), ^{ + if ([_application respondsToSelector:@selector(registerForRemoteNotifications)]) { + [_application registerForRemoteNotifications]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [_application registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert]; +#pragma clang diagnostic pop + } + }); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_timeout * NSEC_PER_SEC)), + FIRAuthGlobalWorkQueue(), ^{ + [self callBack]; + }); +} + +- (void)setToken:(nullable FIRAuthAPNSToken *)token { + if (!token) { + _token = nil; + return; + } + if (token.type == FIRAuthAPNSTokenTypeUnknown) { + static FIRAuthAPNSTokenType detectedTokenType = FIRAuthAPNSTokenTypeUnknown; + if (detectedTokenType == FIRAuthAPNSTokenTypeUnknown) { + detectedTokenType = + [[self class] isProductionApp] ? FIRAuthAPNSTokenTypeProd : FIRAuthAPNSTokenTypeSandbox; + } + token = [[FIRAuthAPNSToken alloc] initWithData:token.data type:detectedTokenType]; + } + _token = token; + [self callBack]; +} + +#pragma mark - Internal methods + +/** @fn callBack + @brief Calls back all pending callbacks with the current APNs token, if one is available. + */ +- (void)callBack { + if (!_pendingCallbacks) { + return; + } + NSArray<FIRAuthAPNSTokenCallback> *allCallbacks = _pendingCallbacks; + _pendingCallbacks = nil; + for (FIRAuthAPNSTokenCallback callback in allCallbacks) { + callback(_token); + } +}; + +/** @fn isProductionApp + @brief Whether or not the app has production (versus sandbox) provisioning profile. + @remarks This method is adapted from @c FIRInstanceID . + */ ++ (BOOL)isProductionApp { + const BOOL defaultAppTypeProd = YES; + + NSError *error = nil; + + Class envClass = NSClassFromString(@"FIRAppEnvironmentUtil"); + SEL isSimulatorSelector = NSSelectorFromString(@"isSimulator"); + if ([envClass respondsToSelector:isSimulatorSelector]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + if ([envClass performSelector:isSimulatorSelector]) { +#pragma clang diagnostic pop + FIRLogWarning(kFIRLoggerAuth, @"I-AUT000006", + @"Assuming prod APNs token type on simulator."); + return defaultAppTypeProd; + } + } + + NSString *path = [[[NSBundle mainBundle] bundlePath] + stringByAppendingPathComponent:@"embedded.mobileprovision"]; + + // Apps distributed via AppStore or TestFlight use the Production APNS certificates. + SEL isFromAppStoreSelector = NSSelectorFromString(@"isFromAppStore"); + if ([envClass respondsToSelector:isFromAppStoreSelector]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + if ([envClass performSelector:isFromAppStoreSelector]) { +#pragma clang diagnostic pop + return defaultAppTypeProd; + } + } + + SEL isAppStoreReceiptSandboxSelector = NSSelectorFromString(@"isAppStoreReceiptSandbox"); + if ([envClass respondsToSelector:isAppStoreReceiptSandboxSelector]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + if ([envClass performSelector:isAppStoreReceiptSandboxSelector] && !path.length) { +#pragma clang diagnostic pop + // Distributed via TestFlight + return defaultAppTypeProd; + } + } + + NSMutableData *profileData = [NSMutableData dataWithContentsOfFile:path options:0 error:&error]; + + if (!profileData.length || error) { + FIRLogWarning(kFIRLoggerAuth, @"I-AUT000007", + @"Error while reading embedded mobileprovision %@", error); + return defaultAppTypeProd; + } + + // The "embedded.mobileprovision" sometimes contains characters with value 0, which signals the + // end of a c-string and halts the ASCII parser, or with value > 127, which violates strict 7-bit + // ASCII. Replace any 0s or invalid characters in the input. + uint8_t *profileBytes = (uint8_t *)profileData.bytes; + for (int i = 0; i < profileData.length; i++) { + uint8_t currentByte = profileBytes[i]; + if (!currentByte || currentByte > 127) { + profileBytes[i] = '.'; + } + } + + NSString *embeddedProfile = [[NSString alloc] initWithBytesNoCopy:profileBytes + length:profileData.length + encoding:NSASCIIStringEncoding + freeWhenDone:NO]; + + if (error || !embeddedProfile.length) { + FIRLogWarning(kFIRLoggerAuth, @"I-AUT000008", + @"Error while reading embedded mobileprovision %@", error); + return defaultAppTypeProd; + } + + NSScanner *scanner = [NSScanner scannerWithString:embeddedProfile]; + NSString *plistContents; + if ([scanner scanUpToString:@"<plist" intoString:nil]) { + if ([scanner scanUpToString:@"</plist>" intoString:&plistContents]) { + plistContents = [plistContents stringByAppendingString:@"</plist>"]; + } + } + + if (!plistContents.length) { + return defaultAppTypeProd; + } + + NSData *data = [plistContents dataUsingEncoding:NSUTF8StringEncoding]; + if (!data.length) { + FIRLogWarning(kFIRLoggerAuth, @"I-AUT000009", + @"Couldn't read plist fetched from embedded mobileprovision"); + return defaultAppTypeProd; + } + + NSError *plistMapError; + id plistData = [NSPropertyListSerialization propertyListWithData:data + options:NSPropertyListImmutable + format:nil + error:&plistMapError]; + if (plistMapError || ![plistData isKindOfClass:[NSDictionary class]]) { + FIRLogWarning(kFIRLoggerAuth, @"I-AUT000010", + @"Error while converting assumed plist to dict %@", + plistMapError.localizedDescription); + return defaultAppTypeProd; + } + NSDictionary *plistMap = (NSDictionary *)plistData; + + if ([plistMap valueForKeyPath:@"ProvisionedDevices"]) { + FIRLogInfo(kFIRLoggerAuth, @"I-AUT000011", + @"Provisioning profile has specifically provisioned devices, " + @"most likely a Dev profile."); + } + + NSString *apsEnvironment = [plistMap valueForKeyPath:@"Entitlements.aps-environment"]; + FIRLogDebug(kFIRLoggerAuth, @"I-AUT000012", + @"APNS Environment in profile: %@", apsEnvironment); + + // No aps-environment in the profile. + if (!apsEnvironment.length) { + FIRLogWarning(kFIRLoggerAuth, @"I-AUT000013", + @"No aps-environment set. If testing on a device APNS is not " + @"correctly configured. Please recheck your provisioning profiles."); + return defaultAppTypeProd; + } + + if ([apsEnvironment isEqualToString:@"development"]) { + return NO; + } + + return defaultAppTypeProd; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthAPNSTokenType.h b/Firebase/Auth/Source/FIRAuthAPNSTokenType.h new file mode 100644 index 0000000..87df574 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthAPNSTokenType.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthSwiftNameSupport.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * @brief The APNs token type for the app. + */ +typedef NS_ENUM(NSInteger, FIRAuthAPNSTokenType) { + + /** Unknown token type. + The actual token type will be detected from the provisioning profile in the app's bundle. + */ + FIRAuthAPNSTokenTypeUnknown, + + /** Sandbox token type. + */ + FIRAuthAPNSTokenTypeSandbox, + + /** Production token type. + */ + FIRAuthAPNSTokenTypeProd, +} FIR_SWIFT_NAME(AuthAPNSTokenType); + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthAppCredential.m b/Firebase/Auth/Source/FIRAuthAppCredential.m new file mode 100644 index 0000000..ad12741 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthAppCredential.m @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#import "Private/FIRAuthAppCredential.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @var kReceiptKey + @brief The key used to encode the receipt property for NSSecureCoding. + */ +static NSString *const kReceiptKey = @"receipt"; + +/** @var kSecretKey + @brief The key used to encode the secret property for NSSecureCoding. + */ +static NSString *const kSecretKey = @"secret"; + +@implementation FIRAuthAppCredential + +- (instancetype)initWithReceipt:(NSString *)receipt secret:(nullable NSString *)secret { + self = [super init]; + if (self) { + _receipt = [receipt copy]; + _secret = [secret copy]; + } + return self; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { + NSString *receipt = [aDecoder decodeObjectOfClass:[NSString class] forKey:kReceiptKey]; + if (!receipt) { + return nil; + } + NSString *secret = [aDecoder decodeObjectOfClass:[NSString class] forKey:kSecretKey]; + return [self initWithReceipt:receipt secret:secret]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_receipt forKey:kReceiptKey]; + [aCoder encodeObject:_secret forKey:kSecretKey]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthAppCredentialManager.m b/Firebase/Auth/Source/FIRAuthAppCredentialManager.m new file mode 100644 index 0000000..2a0d1c7 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthAppCredentialManager.m @@ -0,0 +1,164 @@ +/* + * 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. + */ + +#import "Private/FIRAuthAppCredentialManager.h" + +#import "Private/FIRAuthAppCredential.h" +#import "FIRAuthGlobalWorkQueue.h" +#import "FIRAuthKeychain.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @var kKeychainDataKey + @brief The keychain key for the data. + */ +static NSString *const kKeychainDataKey = @"app_credentials"; + +/** @var kFullCredentialKey + @brief The data key for the full app credential. + */ +static NSString *const kFullCredentialKey = @"full_credential"; + +/** @var kPendingReceiptsKey + @brief The data key for the array of pending receipts. + */ +static NSString *const kPendingReceiptsKey = @"pending_receipts"; + +/** @var kMaximumNumberOfPendingReceipts + @brief The maximum number of partial credentials kept by this class. + */ +static const NSUInteger kMaximumNumberOfPendingReceipts = 32; + +@implementation FIRAuthAppCredentialManager { + /** @var _keychain + @brief The keychain for app credentials to load from and to save to. + */ + FIRAuthKeychain *_keychain; + + /** @var _pendingReceipts + @brief A list of pending receipts sorted in the order they were recorded. + */ + NSMutableArray<NSString *> *_pendingReceipts; + + /** @var _callbacksByReceipt + @brief A map from pending receipts to callbacks. + */ + NSMutableDictionary<NSString *, FIRAuthAppCredentialCallback> *_callbacksByReceipt; +} + +- (instancetype)initWithKeychain:(FIRAuthKeychain *)keychain { + self = [super init]; + if (self) { + _keychain = keychain; + // Load the credentials from keychain if possible. + NSError *error; + NSData *encodedData = [_keychain dataForKey:kKeychainDataKey error:&error]; + if (!error && encodedData) { + NSKeyedUnarchiver *unarchiver = + [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedData]; + FIRAuthAppCredential *credential = + [unarchiver decodeObjectOfClass:[FIRAuthAppCredential class] + forKey:kFullCredentialKey]; + if ([credential isKindOfClass:[FIRAuthAppCredential class]]) { + _credential = credential; + } + NSSet<Class> *allowedClasses = + [NSSet<Class> setWithObjects:[NSArray class], [NSString class], nil]; + NSArray<NSString *> *pendingReceipts = + [unarchiver decodeObjectOfClasses:allowedClasses forKey:kPendingReceiptsKey]; + if ([pendingReceipts isKindOfClass:[NSArray class]]) { + _pendingReceipts = [pendingReceipts mutableCopy]; + } + } + if (!_pendingReceipts) { + _pendingReceipts = [[NSMutableArray<NSString *> alloc] init]; + } + _callbacksByReceipt = + [[NSMutableDictionary<NSString *, FIRAuthAppCredentialCallback> alloc] init]; + } + return self; +} + +- (NSUInteger)maximumNumberOfPendingReceipts { + return kMaximumNumberOfPendingReceipts; +} + +- (void)didStartVerificationWithReceipt:(NSString *)receipt + timeout:(NSTimeInterval)timeout + callback:(FIRAuthAppCredentialCallback)callback { + [_pendingReceipts removeObject:receipt]; + if (_pendingReceipts.count >= kMaximumNumberOfPendingReceipts) { + [_pendingReceipts removeObjectAtIndex:0]; + } + [_pendingReceipts addObject:receipt]; + _callbacksByReceipt[receipt] = callback; + [self saveData]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)), + FIRAuthGlobalWorkQueue(), ^{ + [self callBackWithReceipt:receipt]; + }); +} + +- (BOOL)canFinishVerificationWithReceipt:(NSString *)receipt secret:(NSString *)secret { + if (![_pendingReceipts containsObject:receipt]) { + return NO; + } + [_pendingReceipts removeObject:receipt]; + _credential = [[FIRAuthAppCredential alloc] initWithReceipt:receipt secret:secret]; + [self saveData]; + [self callBackWithReceipt:receipt]; + return YES; +} + +- (void)clearCredential { + _credential = nil; + [self saveData]; +} + +#pragma mark - Internal methods + +/** @fn saveData + @brief Save the data in memory to the keychain ignoring any errors. + */ +- (void)saveData { + NSMutableData *archiveData = [NSMutableData data]; + NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:archiveData]; + [archiver encodeObject:_credential forKey:kFullCredentialKey]; + [archiver encodeObject:_pendingReceipts forKey:kPendingReceiptsKey]; + [archiver finishEncoding]; + [_keychain setData:archiveData forKey:kKeychainDataKey error:NULL]; +} + +/** @fn callBackWithReceipt: + @brief Calls the saved callback for the specifc receipt. + @param receipt The receipt associated with the callback. + */ +- (void)callBackWithReceipt:(NSString *)receipt { + FIRAuthAppCredentialCallback callback = _callbacksByReceipt[receipt]; + if (!callback) { + return; + } + [_callbacksByReceipt removeObjectForKey:receipt]; + if (_credential) { + callback(_credential); + } else { + callback([[FIRAuthAppCredential alloc] initWithReceipt:receipt secret:nil]); + } +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthAppDelegateProxy.m b/Firebase/Auth/Source/FIRAuthAppDelegateProxy.m new file mode 100644 index 0000000..ca88a4c --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthAppDelegateProxy.m @@ -0,0 +1,245 @@ +/* + * 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. + */ + +#import "Private/FIRAuthAppDelegateProxy.h" + +#import <objc/runtime.h> + +NS_ASSUME_NONNULL_BEGIN + +/** @var kProxyEnabledBundleKey + @brief The key in application's bundle plist for whether or not proxy should be enabled. + @remarks This key is a shared constant with Analytics and FCM. + */ +static NSString *const kProxyEnabledBundleKey = @"FirebaseAppDelegateProxyEnabled"; + +/** @fn noop + @brief A function that does nothing. + @remarks This is used as the placeholder for unimplemented UApplicationDelegate methods, + because once we added a method there is no way to remove it from the class. + */ +#if !OBJC_OLD_DISPATCH_PROTOTYPES +static void noop(void) { +} +#else +static id noop(id object, SEL cmd, ...) { + return nil; +} +#endif + +@implementation FIRAuthAppDelegateProxy { + /** @var _appDelegate + @brief The application delegate whose method is being swizzled. + */ + id<UIApplicationDelegate> _appDelegate; + + /** @var _orginalImplementationsBySelector + @brief A map from selectors to original implementations that have been swizzled. + */ + NSMutableDictionary<NSValue *, NSValue *> *_originalImplementationsBySelector; + + /** @var _handlers + @brief The array of weak pointers of `id<FIRAuthAppDelegateHandler>`. + */ + NSPointerArray *_handlers; +} + +- (nullable instancetype)initWithApplication:(nullable UIApplication *)application { + self = [super init]; + if (self) { + id proxyEnabled = [[NSBundle mainBundle] objectForInfoDictionaryKey:kProxyEnabledBundleKey]; + if ([proxyEnabled isKindOfClass:[NSNumber class]] && !((NSNumber *)proxyEnabled).boolValue) { + return nil; + } + _appDelegate = application.delegate; + if (![_appDelegate conformsToProtocol:@protocol(UIApplicationDelegate)]) { + return nil; + } + _originalImplementationsBySelector = [[NSMutableDictionary<NSValue *, NSValue *> alloc] init]; + _handlers = [[NSPointerArray alloc] initWithOptions:NSPointerFunctionsWeakMemory]; + + // Swizzle the methods. + __weak FIRAuthAppDelegateProxy *weakSelf = self; + SEL registerDeviceTokenSelector = + @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:); + [self replaceSelector:registerDeviceTokenSelector + withBlock:^(id object, UIApplication* application, NSData *deviceToken) { + [weakSelf object:object + selector:registerDeviceTokenSelector + application:application + didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; + }]; + SEL receiveNotificationSelector = @selector(application:didReceiveRemoteNotification:); + SEL receiveNotificationWithHandlerSelector = + @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); + if ([_appDelegate respondsToSelector:receiveNotificationWithHandlerSelector] || + ![_appDelegate respondsToSelector:receiveNotificationSelector]) { + // Replace the modern selector which is available on iOS 7 and above. + [self replaceSelector:receiveNotificationWithHandlerSelector + withBlock:^(id object, UIApplication *application, NSDictionary *notification, + void (^completionHandler)(UIBackgroundFetchResult)) { + [weakSelf object:object + selector:receiveNotificationWithHandlerSelector + application:application + didReceiveRemoteNotification:notification + fetchCompletionHandler:completionHandler]; + }]; + } else { + // Replace the deprecated selector because this is the only one that the client app uses. + [self replaceSelector:receiveNotificationSelector + withBlock:^(id object, UIApplication *application, NSDictionary *notification) { + [weakSelf object:object + selector:receiveNotificationSelector + application:application + didReceiveRemoteNotification:notification]; + }]; + } + } + return self; +} + +- (void)dealloc { + for (NSValue *selector in _originalImplementationsBySelector) { + IMP implementation = _originalImplementationsBySelector[selector].pointerValue; + Method method = class_getInstanceMethod([_appDelegate class], selector.pointerValue); + imp_removeBlock(method_setImplementation(method, implementation)); + } +} + +- (void)addHandler:(__weak id<FIRAuthAppDelegateHandler>)handler { + @synchronized (_handlers) { + [_handlers addPointer:(__bridge void *)handler]; + } +} + ++ (nullable instancetype)sharedInstance { + static dispatch_once_t onceToken; + static FIRAuthAppDelegateProxy *_Nullable sharedInstance; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] initWithApplication:[UIApplication sharedApplication]]; + }); + return sharedInstance; +} + +#pragma mark - UIApplicationDelegate proxy methods. + +- (void)object:(id)object + selector:(SEL)selector + application:(UIApplication *)application + didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + if (object == _appDelegate) { + for (id<FIRAuthAppDelegateHandler> handler in [self handlers]) { + [handler setAPNSToken:deviceToken]; + } + } + IMP originalImplementation = [self originalImplementationForSelector:selector]; + if (originalImplementation) { + typedef void (*Implmentation)(id, SEL, UIApplication*, NSData *); + ((Implmentation)originalImplementation)(object, selector, application, deviceToken); + } +} + +- (void)object:(id)object + selector:(SEL)selector + application:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)notification + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { + if (object == _appDelegate) { + for (id<FIRAuthAppDelegateHandler> handler in [self handlers]) { + if ([handler canHandleNotification:notification]) { + completionHandler(UIBackgroundFetchResultNoData); + return; + }; + } + } + IMP originalImplementation = [self originalImplementationForSelector:selector]; + typedef void (*Implmentation)(id, SEL, UIApplication*, NSDictionary *, + void (^)(UIBackgroundFetchResult)); + ((Implmentation)originalImplementation)(object, selector, application, notification, + completionHandler); +} + +- (void)object:(id)object + selector:(SEL)selector + application:(UIApplication *)application + didReceiveRemoteNotification:(NSDictionary *)notification { + if (object == _appDelegate) { + for (id<FIRAuthAppDelegateHandler> handler in [self handlers]) { + if ([handler canHandleNotification:notification]) { + return; + }; + } + } + IMP originalImplementation = [self originalImplementationForSelector:selector]; + typedef void (*Implmentation)(id, SEL, UIApplication*, NSDictionary *); + ((Implmentation)originalImplementation)(object, selector, application, notification); +} + +#pragma mark - Internal Methods + +/** @fn handlers + @brief Gets the list of handlers from `_handlers` safely. + */ +- (NSArray<id<FIRAuthAppDelegateHandler>> *)handlers { + @synchronized (_handlers) { + NSMutableArray<id<FIRAuthAppDelegateHandler>> *liveHandlers = + [[NSMutableArray<id<FIRAuthAppDelegateHandler>> alloc] initWithCapacity:_handlers.count]; + for (__weak id<FIRAuthAppDelegateHandler> handler in _handlers) { + if (handler) { + [liveHandlers addObject:handler]; + } + } + if (liveHandlers.count < _handlers.count) { + [_handlers compact]; + } + return liveHandlers; + } +} + +/** @fn replaceSelector:withBlock: + @brief replaces the implementation for a method of `_appDelegate` specified by a selector. + @param selector The selector for the method. + @param block The block as the new implementation of the method. + */ +- (void)replaceSelector:(SEL)selector withBlock:(id)block { + Method originalMethod = class_getInstanceMethod([_appDelegate class], selector); + IMP newImplementation = imp_implementationWithBlock(block); + IMP originalImplementation; + if (originalMethod) { + originalImplementation = method_setImplementation(originalMethod, newImplementation) ?: &noop; + } else { + // The original method was not implemented in the class, add it with the new implementation. + struct objc_method_description methodDescription = + protocol_getMethodDescription(@protocol(UIApplicationDelegate), selector, NO, YES); + class_addMethod([_appDelegate class], selector, newImplementation, methodDescription.types); + originalImplementation = &noop; + } + _originalImplementationsBySelector[[NSValue valueWithPointer:selector]] = + [NSValue valueWithPointer:originalImplementation]; +} + +/** @fn originalImplementationForSelector: + @brief Gets the original implementation for the given selector. + @param selector The selector for the method that has been replaced. + @return The original implementation if there was one. + */ +- (IMP)originalImplementationForSelector:(SEL)selector { + return _originalImplementationsBySelector[[NSValue valueWithPointer:selector]].pointerValue; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthCredential.h b/Firebase/Auth/Source/FIRAuthCredential.h new file mode 100644 index 0000000..ce28854 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthCredential.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthSwiftNameSupport.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRAuthCredential + @brief Represents a credential. + */ +FIR_SWIFT_NAME(AuthCredential) +@interface FIRAuthCredential : NSObject + +/** @property provider + @brief Gets the name of the identity provider for the credential. + */ +@property(nonatomic, copy, readonly) NSString *provider; + +/** @fn init + @brief This is an abstract base class. Concrete instances should be created via factory + methods available in the various authentication provider libraries (like the Facebook + provider or the Google provider libraries.) + */ +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthCredential.m b/Firebase/Auth/Source/FIRAuthCredential.m new file mode 100644 index 0000000..d476d6d --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthCredential.m @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#import "Private/FIRAuthCredential_Internal.h" + +@implementation FIRAuthCredential + +- (instancetype)init { + @throw [NSException exceptionWithName:@"Attempt to call unavailable initializer." + reason:@"This class is an abstract base class. It's init method " + "should not be called directly." + userInfo:nil]; +} + +- (nullable instancetype)initWithProvider:(NSString *)provider { + self = [super init]; + if (self) { + _provider = [provider copy]; + } + return self; +} + +- (void)prepareVerifyAssertionRequest:(FIRVerifyAssertionRequest *)request { + @throw [NSException exceptionWithName:@"Attempt to call virtual method." + reason:@"This method must be overridden by a subclass." + userInfo:nil]; +} + +@end diff --git a/Firebase/Auth/Source/FIRAuthDataResult.h b/Firebase/Auth/Source/FIRAuthDataResult.h new file mode 100644 index 0000000..e72adf2 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthDataResult.h @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthSwiftNameSupport.h" + +@class FIRAdditionalUserInfo; +@class FIRUser; + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRAuthDataResult + @brief Helper object that contains the result of a successful sign-in, link and reauthenticate. + It contains a reference to a @c FIRUser and @c FIRAdditionalUserInfo. + */ +FIR_SWIFT_NAME(AuthDataResult) +@interface FIRAuthDataResult : NSObject + +/** @fn init + @brief This class should not be initialized manually. @c FIRAuthDataResult instance is + returned as part of @c FIRAuthDataResultCallback . + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @property user + @brief The signed in user. + */ +@property(nonatomic, readonly) FIRUser *user; + +/** @property additionalUserInfo + @brief If available contains the additional IdP specific information about signed in user. + */ +@property(nonatomic, readonly, nullable) FIRAdditionalUserInfo *additionalUserInfo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthDataResult.m b/Firebase/Auth/Source/FIRAuthDataResult.m new file mode 100644 index 0000000..fd2daa7 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthDataResult.m @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#import "Private/FIRAuthDataResult_Internal.h" + +#import "FIRAdditionalUserInfo.h" +#import "FIRUser.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation FIRAuthDataResult + +/** @var kAdditionalUserInfoCodingKey + @brief The key used to encode the additionalUserInfo property for NSSecureCoding. + */ +static NSString *const kAdditionalUserInfoCodingKey = @"additionalUserInfo"; + +/** @var kUserCodingKey + @brief The key used to encode the user property for NSSecureCoding. + */ +static NSString *const kUserCodingKey = @"user"; + +- (nullable instancetype)initWithUser:(FIRUser *)user + additionalUserInfo:(nullable FIRAdditionalUserInfo *)additionalUserInfo { + self = [super init]; + if (self) { + _additionalUserInfo = additionalUserInfo; + _user = user; + } + return self; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { + FIRUser *user = + [aDecoder decodeObjectOfClass:[FIRUser class] forKey:kUserCodingKey]; + FIRAdditionalUserInfo *additionalUserInfo = + [aDecoder decodeObjectOfClass:[FIRAdditionalUserInfo class] + forKey:kAdditionalUserInfoCodingKey]; + + return [self initWithUser:user additionalUserInfo:additionalUserInfo]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_user forKey:kUserCodingKey]; + [aCoder encodeObject:_additionalUserInfo forKey:kAdditionalUserInfoCodingKey]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthDispatcher.m b/Firebase/Auth/Source/FIRAuthDispatcher.m new file mode 100644 index 0000000..98eb50a --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthDispatcher.m @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#import "Private/FIRAuthDispatcher.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation FIRAuthDispatcher + +@synthesize dispatchAfterImplementation = _dispatchAfterImplementation; + ++ (instancetype)sharedInstance { + static dispatch_once_t onceToken; + static FIRAuthDispatcher *sharedInstance; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] init]; + }); + return sharedInstance; +} + +- (void)dispatchAfterDelay:(NSTimeInterval)delay + queue:(dispatch_queue_t)queue + task:(void (^)(void))task { + if (_dispatchAfterImplementation) { + _dispatchAfterImplementation(delay, queue, task); + return; + } + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC), queue, task); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthErrorUtils.m b/Firebase/Auth/Source/FIRAuthErrorUtils.m new file mode 100644 index 0000000..8179d02 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthErrorUtils.m @@ -0,0 +1,794 @@ +/* + * 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. + */ + +#import "Private/FIRAuthErrorUtils.h" + +#import "FIRAuthCredential.h" +#import "Private/FIRAuthInternalErrors.h" + +NS_ASSUME_NONNULL_BEGIN + +NSString *const FIRAuthErrorDomain = @"FIRAuthErrorDomain"; + +NSString *const FIRAuthInternalErrorDomain = @"FIRAuthInternalErrorDomain"; + +NSString *const FIRAuthErrorUserInfoDeserializedResponseKey = + @"FIRAuthErrorUserInfoDeserializedResponseKey"; + +NSString *const FIRAuthErrorUserInfoDataKey = @"FIRAuthErrorUserInfoDataKey"; + +NSString *const FIRAuthErrorUserInfoEmailKey = @"FIRAuthErrorUserInfoEmailKey"; + +NSString *const FIRAuthErrorNameKey = @"error_name"; + +NSString *const FIRAuthUpdatedCredentialKey = @"FIRAuthUpdatedCredentialKey"; + +/** @var kServerErrorDetailMarker + @brief This marker indicates that the server error message contains a detail error message which + should be used instead of the hardcoded client error message. + */ +static NSString *const kServerErrorDetailMarker = @" : "; + +#pragma mark - Standard Error Messages + +/** @var kFIRAuthErrorMessageInvalidCustomToken + @brief Message for @c FIRAuthErrorCodeInvalidCustomToken error code. + */ +static NSString *const kFIRAuthErrorMessageInvalidCustomToken = @"The custom token format is " + "incorrect. Please check the documentation."; + +/** @var kFIRAuthErrorMessageCustomTokenMismatch + @brief Message for @c FIRAuthErrorCodeCustomTokenMismatch error code. + */ +static NSString *const kFIRAuthErrorMessageCustomTokenMismatch = @"The custom token corresponds to " + "a different audience."; + +/** @var kFIRAuthErrorMessageInvalidEmail + @brief Message for @c FIRAuthErrorCodeInvalidEmail error code. + */ +static NSString *const kFIRAuthErrorMessageInvalidEmail = @"The email address is badly formatted."; + +/** @var kFIRAuthErrorMessageInvalidCredential + @brief Message for @c FIRAuthErrorCodeInvalidCredential error code. + */ +static NSString *const kFIRAuthErrorMessageInvalidCredential = @"The supplied auth credential is " + "malformed or has expired."; + +/** @var kFIRAuthErrorMessageUserDisabled + @brief Message for @c FIRAuthErrorCodeUserDisabled error code. + */ +static NSString *const kFIRAuthErrorMessageUserDisabled = @"The user account has been disabled by " + "an administrator."; + +/** @var kFIRAuthErrorMessageEmailAlreadyInUse + @brief Message for @c FIRAuthErrorCodeEmailAlreadyInUse error code. + */ +static NSString *const kFIRAuthErrorMessageEmailAlreadyInUse = @"The email address is already in " + "use by another account."; + +/** @var kFIRAuthErrorMessageWrongPassword + @brief Message for @c FIRAuthErrorCodeWrongPassword error code. + */ +static NSString *const kFIRAuthErrorMessageWrongPassword = @"The password is invalid or the user " + "does not have a password."; + +/** @var kFIRAuthErrorMessageTooManyRequests + @brief Message for @c FIRAuthErrorCodeTooManyRequests error code. + */ +static NSString *const kFIRAuthErrorMessageTooManyRequests = @"We have blocked all requests from " + "this device due to unusual activity. Try again later."; + +/** @var kFIRAuthErrorMessageAccountExistsWithDifferentCredential + @brief Message for @c FIRAuthErrorCodeAccountLinkNeeded error code. + */ +static NSString *const kFIRAuthErrorMessageAccountExistsWithDifferentCredential = @"An account " + "already exists with the same email address but different sign-in credentials. Sign in using a " + "provider associated with this email address."; + +/** @var kFIRAuthErrorMessageRequiresRecentLogin + @brief Message for @c FIRAuthErrorCodeRequiresRecentLogin error code. + */ +static NSString *const kFIRAuthErrorMessageRequiresRecentLogin= @"This operation is sensitive and " + "requires recent authentication. Log in again before retrying this request."; + +/** @var kFIRAuthErrorMessageProviderAlreadyLinked + @brief Message for @c FIRAuthErrorCodeProviderAlreadyExists error code. + */ +static NSString *const kFIRAuthErrorMessageProviderAlreadyLinked = + @"[ERROR_PROVIDER_ALREADY_LINKED] - User can only be linked to one identity for the given " + "provider."; + +/** @var kFIRAuthErrorMessageNoSuchProvider + @brief Message for @c FIRAuthErrorCodeNoSuchProvider error code. + */ +static NSString *const kFIRAuthErrorMessageNoSuchProvider = @"User was not linked to an account " + "with the given provider."; + +/** @var kFIRAuthErrorMessageInvalidUserToken + @brief Message for @c FIRAuthErrorCodeInvalidUserToken error code. + */ +static NSString *const kFIRAuthErrorMessageInvalidUserToken = @"The user's credential is no longer " + "valid. The user must sign in again."; + +/** @var kFIRAuthErrorMessageNetworkError + @brief Message for @c FIRAuthErrorCodeNetworkError error code. + */ +static NSString *const kFIRAuthErrorMessageNetworkError = @"Network error (such as timeout, " + "interrupted connection or unreachable host) has occurred."; + +/** @var kFIRAuthErrorMessageKeychainError + @brief Message for @c FIRAuthErrorCodeKeychainError error code. + */ +static NSString *const kFIRAuthErrorMessageKeychainError = @"An error occurred when accessing the " + "keychain. The @c NSLocalizedFailureReasonErrorKey field in the @c NSError.userInfo dictionary " + "will contain more information about the error encountered"; + +/** @var kFIRAuthErrorMessageUserTokenExpired + @brief Message for @c FIRAuthErrorCodeTokenExpired error code. + */ +static NSString *const kFIRAuthErrorMessageUserTokenExpired = @"The user's credential is no longer " + "valid. The user must sign in again."; + +/** @var kFIRAuthErrorMessageUserNotFound + @brief Message for @c FIRAuthErrorCodeUserNotFound error code. + */ +static NSString *const kFIRAuthErrorMessageUserNotFound = @"There is no user record corresponding " + "to this identifier. The user may have been deleted."; + +/** @var kFIRAuthErrorMessageInvalidAPIKey + @brief Message for @c FIRAuthErrorCodeInvalidAPIKey error code. + @remarks This error is not thrown by the server. + */ +static NSString *const kFIRAuthErrorMessageInvalidAPIKey = @"An invalid API Key was supplied in " + "the request."; + +/** @var kFIRAuthErrorMessageUserMismatch. + @brief Message for @c FIRAuthErrorCodeInvalidAPIKey error code. + */ +static NSString *const FIRAuthErrorMessageUserMismatch = @"The supplied credentials do not " + "correspond to the previously signed in user."; + +/** @var kFIRAuthErrorMessageCredentialAlreadyInUse + @brief Message for @c FIRAuthErrorCodeCredentialAlreadyInUse error code. + */ +static NSString *const kFIRAuthErrorMessageCredentialAlreadyInUse = @"This credential is already " + "associated with a different user account."; + +/** @var kFIRAuthErrorMessageOperationNotAllowed + @brief Message for @c FIRAuthErrorCodeOperationNotAllowed error code. + */ +static NSString *const kFIRAuthErrorMessageOperationNotAllowed = @"The given sign-in provider is " + "disabled for this Firebase project. Enable it in the Firebase console, under the sign-in " + "method tab of the Auth section."; + +/** @var kFIRAuthErrorMessageWeakPassword + @brief Message for @c FIRAuthErrorCodeWeakPassword error code. + */ +static NSString *const kFIRAuthErrorMessageWeakPassword = @"The password must be 6 characters long " + "or more."; + +/** @var kFIRAuthErrorMessageAppNotAuthorized + @brief Message for @c FIRAuthErrorCodeAppNotAuthorized error code. + */ +static NSString *const kFIRAuthErrorMessageAppNotAuthorized = @"This app is not authorized to use " + "Firebase Authentication with the provided API key. Review your key configuration in the " + "Google API console and ensure that it accepts requests from your app's bundle ID."; + +/** @var kFIRAuthErrorMessageExpiredActionCode + @brief Message for @c FIRAuthErrorCodeExpiredActionCode error code. + */ +static NSString *const kFIRAuthErrorMessageExpiredActionCode = @"The action code has expired."; + +/** @var kFIRAuthErrorMessageInvalidActionCode + @brief Message for @c FIRAuthErrorCodeInvalidActionCode error code. + */ +static NSString *const kFIRAuthErrorMessageInvalidActionCode = @"The action code is invalid. This " + "can happen if the code is malformed, expired, or has already been used."; + +/** @var kFIRAuthErrorMessageInvalidMessagePayload + @brief Message for @c FIRAuthErrorCodeInvalidMessagePayload error code. + */ +static NSString *const kFIRAuthErrorMessageInvalidMessagePayload = @"The action code is invalid. " + "This can happen if the code is malformed, expired, or has already been used."; + +/** @var kFIRAuthErrorMessageInvalidSender + @brief Message for @c FIRAuthErrorCodeInvalidSender error code. + */ +static NSString *const kFIRAuthErrorMessageInvalidSender = @"The email template corresponding to " + "this action contains invalid characters in its message. Please fix by going to the Auth email " + "templates section in the Firebase Console."; + +/** @var kFIRAuthErrorMessageInvalidRecipientEmail + @brief Message for @c FIRAuthErrorCodeInvalidRecipient error code. + */ +static NSString *const kFIRAuthErrorMessageInvalidRecipientEmail = @"The action code is invalid. " + "This can happen if the code is malformed, expired, or has already been used."; + +/** @var kFIRAuthErrorMessageMissingContinueURI + @brief Message for @c FIRAuthErrorCodeMissingContinueURI error code. + */ +static NSString *const kFIRAuthErrorMessageMissingContinueURI = + @"A continue URL must be provided in the request."; + +/** @var kFIRAuthErrorMessageMissingPhoneNumber + @brief Message for @c FIRAuthErrorCodeMissingPhoneNumber error code. + */ +static NSString *const kFIRAuthErrorMessageMissingPhoneNumber = + @"To send verification codes, provide a phone number for the recipient."; + +/** @var kFIRAuthErrorMessageInvalidPhoneNumber + @brief Message for @c FIRAuthErrorCodeMissingPhoneNumber error code. + */ +static NSString *const kFIRAuthErrorMessageInvalidPhoneNumber = + @"The format of the phone number provided is incorrect. Please enter the phone number in a " + "format that can be parsed into E.164 format. E.164 phone numbers are written in the format " + "[+][country code][subscriber number including area code]."; + +/** @var kFIRAuthErrorMessageMissingVerificationCode + @brief Message for @c FIRAuthErrorCodeMissingVerificationCode error code. + */ +static NSString *const kFIRAuthErrorMessageMissingVerificationCode = + @"The Phone Auth Credential was created with an empty SMS verification Code."; + +/** @var kFIRAuthErrorMessageInvalidVerificationCode + @brief Message for @c FIRAuthErrorCodeInvalidVerificationCode error code. + */ +static NSString *const kFIRAuthErrorMessageInvalidVerificationCode = + @"The SMS verification code used to create the phone auth credential is invalid. Please resend " + "the verification code sms and be sure use the verification code provided by the user."; + +/** @var kFIRAuthErrorMessageMissingVerificationID + @brief Message for @c FIRAuthErrorCodeInvalidVerificationID error code. + */ +static NSString *const kFIRAuthErrorMessageMissingVerificationID = + @"The Phone Auth Credential was created with an empty verification ID."; + +/** @var kFIRAuthErrorMessageInvalidVerificationID + @brief Message for @c FIRAuthErrorCodeInvalidVerificationID error code. + */ +static NSString *const kFIRAuthErrorMessageInvalidVerificationID = + @"The verification ID used to create the phone auth credential is invalid."; + +/** @var kFIRAuthErrorMessageSessionExpired + @brief Message for @c FIRAuthErrorCodeSessionExpired error code. + */ +static NSString *const kFIRAuthErrorMessageSessionExpired = @"The SMS code has expired. Please " + @"re-send the verification code to try again."; + +/** @var kFIRAuthErrorMessageMissingAppCredential + @brief Message for @c FIRAuthErrorCodeMissingAppCredential error code. + */ +static NSString *const kFIRAuthErrorMessageMissingAppCredential = @"The phone verification request " + "is missing an APNs Device token. Firebase Auth automatically detects APNs Device Tokens, " + "however, if method swizzling is disabled, the APNs token must be set via the APNSToken " + "property on FIRAuth or by calling setAPNSToken:type on FIRAuth."; + +/** @var kFIRAuthErrorMessageInvalidAppCredential + @brief Message for @c FIRAuthErrorCodeInvalidAppCredential error code. + */ +static NSString *const kFIRAuthErrorMessageInvalidAppCredential = @"The APNs device token provided " + "may be incorrect or does not match the private certificate uploaded to the Firebase Console."; + +/** @var kFIRAuthErrorMessageQuotaExceeded + @brief Message for @c FIRAuthErrorCodeQuotaExceeded error code. + */ +static NSString *const kFIRAuthErrorMessageQuotaExceeded = @"The SMS quota for this project has " + "been exceeded."; + +/** @var kFIRAuthErrorMessageMissingAppToken + @brief Message for @c FIRAuthErrorCodeMissingAppToken error code. + */ +static NSString *const kFIRAuthErrorMessageMissingAppToken = @"Remote notification and background " + "fetching need to be set up for the app. If app delegate swizzling is disabled, the APNs " + "device token received by UIApplicationDelegate needs to be forwarded to FIRAuth's APNSToken " + "property."; + +/** @var kFIRAuthErrorMessageMissingAppToken + @brief Message for @c FIRAuthErrorCodeMissingAppToken error code. + */ +static NSString *const kFIRAuthErrorMessageNotificationNotForwarded = @"If app delegate swizzling " + "is disabled, remote notifications received by UIApplicationDelegate need to be forwarded to " + "FIRAuth's canHandleNotificaton: method."; + +/** @var kFIRAuthErrorMessageAppNotVerified + @brief Message for @c FIRAuthErrorCodeMissingAppToken error code. + */ +static NSString *const kFIRAuthErrorMessageAppNotVerified = @"Firebase could not retrieve the " + "silent push notification and therefore could not verify your app. Ensure that you configured " + "your app correctly to recieve push notifications."; + +/** @var kFIRAuthErrorMessageInternalError + @brief Message for @c FIRAuthErrorCodeInternalError error code. + */ +static NSString *const kFIRAuthErrorMessageInternalError = @"An internal error has occurred, " + "print and inspect the error details for more information."; + +/** @var FIRAuthErrorDescription + @brief The error descrioption, based on the error code. + @remarks No default case so that we get a compiler warning if a new value was added to the enum. + */ +static NSString *FIRAuthErrorDescription(FIRAuthErrorCode code) { + switch (code) { + case FIRAuthErrorCodeInvalidCustomToken: + return kFIRAuthErrorMessageInvalidCustomToken; + case FIRAuthErrorCodeCustomTokenMismatch: + return kFIRAuthErrorMessageCustomTokenMismatch; + case FIRAuthErrorCodeInvalidEmail: + return kFIRAuthErrorMessageInvalidEmail; + case FIRAuthErrorCodeInvalidCredential: + return kFIRAuthErrorMessageInvalidCredential; + case FIRAuthErrorCodeUserDisabled: + return kFIRAuthErrorMessageUserDisabled; + case FIRAuthErrorCodeEmailAlreadyInUse: + return kFIRAuthErrorMessageEmailAlreadyInUse; + case FIRAuthErrorCodeWrongPassword: + return kFIRAuthErrorMessageWrongPassword; + case FIRAuthErrorCodeTooManyRequests: + return kFIRAuthErrorMessageTooManyRequests; + case FIRAuthErrorCodeAccountExistsWithDifferentCredential: + return kFIRAuthErrorMessageAccountExistsWithDifferentCredential; + case FIRAuthErrorCodeRequiresRecentLogin: + return kFIRAuthErrorMessageRequiresRecentLogin; + case FIRAuthErrorCodeProviderAlreadyLinked: + return kFIRAuthErrorMessageProviderAlreadyLinked; + case FIRAuthErrorCodeNoSuchProvider: + return kFIRAuthErrorMessageNoSuchProvider; + case FIRAuthErrorCodeInvalidUserToken: + return kFIRAuthErrorMessageInvalidUserToken; + case FIRAuthErrorCodeNetworkError: + return kFIRAuthErrorMessageNetworkError; + case FIRAuthErrorCodeKeychainError: + return kFIRAuthErrorMessageKeychainError; + case FIRAuthErrorCodeUserTokenExpired: + return kFIRAuthErrorMessageUserTokenExpired; + case FIRAuthErrorCodeUserNotFound: + return kFIRAuthErrorMessageUserNotFound; + case FIRAuthErrorCodeInvalidAPIKey: + return kFIRAuthErrorMessageInvalidAPIKey; + case FIRAuthErrorCodeCredentialAlreadyInUse: + return kFIRAuthErrorMessageCredentialAlreadyInUse; + case FIRAuthErrorCodeInternalError: + return kFIRAuthErrorMessageInternalError; + case FIRAuthErrorCodeUserMismatch: + return FIRAuthErrorMessageUserMismatch; + case FIRAuthErrorCodeOperationNotAllowed: + return kFIRAuthErrorMessageOperationNotAllowed; + case FIRAuthErrorCodeWeakPassword: + return kFIRAuthErrorMessageWeakPassword; + case FIRAuthErrorCodeAppNotAuthorized: + return kFIRAuthErrorMessageAppNotAuthorized; + case FIRAuthErrorCodeExpiredActionCode: + return kFIRAuthErrorMessageExpiredActionCode; + case FIRAuthErrorCodeInvalidActionCode: + return kFIRAuthErrorMessageInvalidActionCode; + case FIRAuthErrorCodeInvalidSender: + return kFIRAuthErrorMessageInvalidSender; + case FIRAuthErrorCodeInvalidMessagePayload: + return kFIRAuthErrorMessageInvalidMessagePayload; + case FIRAuthErrorCodeInvalidRecipientEmail: + return kFIRAuthErrorMessageInvalidRecipientEmail; + case FIRAuthErrorCodeMissingPhoneNumber: + return kFIRAuthErrorMessageMissingPhoneNumber; + case FIRAuthErrorCodeInvalidPhoneNumber: + return kFIRAuthErrorMessageInvalidPhoneNumber; + case FIRAuthErrorCodeMissingVerificationCode: + return kFIRAuthErrorMessageMissingVerificationCode; + case FIRAuthErrorCodeInvalidVerificationCode: + return kFIRAuthErrorMessageInvalidVerificationCode; + case FIRAuthErrorCodeMissingVerificationID: + return kFIRAuthErrorMessageMissingVerificationID; + case FIRAuthErrorCodeInvalidVerificationID: + return kFIRAuthErrorMessageInvalidVerificationID; + case FIRAuthErrorCodeSessionExpired: + return kFIRAuthErrorMessageSessionExpired; + case FIRAuthErrorCodeMissingAppCredential: + return kFIRAuthErrorMessageMissingAppCredential; + case FIRAuthErrorCodeInvalidAppCredential: + return kFIRAuthErrorMessageInvalidAppCredential; + case FIRAuthErrorCodeQuotaExceeded: + return kFIRAuthErrorMessageQuotaExceeded; + case FIRAuthErrorCodeMissingAppToken: + return kFIRAuthErrorMessageMissingAppToken; + case FIRAuthErrorCodeNotificationNotForwarded: + return kFIRAuthErrorMessageNotificationNotForwarded; + case FIRAuthErrorCodeAppNotVerified: + return kFIRAuthErrorMessageAppNotVerified; + } +} + +/** @var FIRAuthErrorCodeString + @brief The the error short string, based on the error code. + @remarks No default case so that we get a compiler warning if a new value was added to the enum. + */ +static NSString *const FIRAuthErrorCodeString(FIRAuthErrorCode code) { + switch (code) { + case FIRAuthErrorCodeInvalidCustomToken: + return @"ERROR_INVALID_CUSTOM_TOKEN"; + case FIRAuthErrorCodeCustomTokenMismatch: + return @"ERROR_CUSTOM_TOKEN_MISMATCH"; + case FIRAuthErrorCodeInvalidEmail: + return @"ERROR_INVALID_EMAIL"; + case FIRAuthErrorCodeInvalidCredential: + return @"ERROR_INVALID_CREDENTIAL"; + case FIRAuthErrorCodeUserDisabled: + return @"ERROR_USER_DISABLED"; + case FIRAuthErrorCodeEmailAlreadyInUse: + return @"ERROR_EMAIL_ALREADY_IN_USE"; + case FIRAuthErrorCodeWrongPassword: + return @"ERROR_WRONG_PASSWORD"; + case FIRAuthErrorCodeTooManyRequests: + return @"ERROR_TOO_MANY_REQUESTS"; + case FIRAuthErrorCodeAccountExistsWithDifferentCredential: + return @"ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL"; + case FIRAuthErrorCodeRequiresRecentLogin: + return @"ERROR_REQUIRES_RECENT_LOGIN"; + case FIRAuthErrorCodeProviderAlreadyLinked: + return @"ERROR_PROVIDER_ALREADY_LINKED"; + case FIRAuthErrorCodeNoSuchProvider: + return @"ERROR_NO_SUCH_PROVIDER"; + case FIRAuthErrorCodeInvalidUserToken: + return @"ERROR_INVALID_USER_TOKEN"; + case FIRAuthErrorCodeNetworkError: + return @"ERROR_NETWORK_REQUEST_FAILED"; + case FIRAuthErrorCodeKeychainError: + return @"ERROR_KEYCHAIN_ERROR"; + case FIRAuthErrorCodeUserTokenExpired: + return @"ERROR_USER_TOKEN_EXPIRED"; + case FIRAuthErrorCodeUserNotFound: + return @"ERROR_USER_NOT_FOUND"; + case FIRAuthErrorCodeInvalidAPIKey: + return @"ERROR_INVALID_API_KEY"; + case FIRAuthErrorCodeCredentialAlreadyInUse: + return @"ERROR_CREDENTIAL_ALREADY_IN_USE"; + case FIRAuthErrorCodeInternalError: + return @"ERROR_INTERNAL_ERROR"; + case FIRAuthErrorCodeUserMismatch: + return @"ERROR_USER_MISMATCH"; + case FIRAuthErrorCodeOperationNotAllowed: + return @"ERROR_OPERATION_NOT_ALLOWED"; + case FIRAuthErrorCodeWeakPassword: + return @"ERROR_WEAK_PASSWORD"; + case FIRAuthErrorCodeAppNotAuthorized: + return @"ERROR_APP_NOT_AUTHORIZED"; + case FIRAuthErrorCodeExpiredActionCode: + return @"ERROR_EXPIRED_ACTION_CODE"; + case FIRAuthErrorCodeInvalidActionCode: + return @"ERROR_INVALID_ACTION_CODE"; + case FIRAuthErrorCodeInvalidMessagePayload: + return @"ERROR_INVALID_MESSAGE_PAYLOAD"; + case FIRAuthErrorCodeInvalidSender: + return @"ERROR_INVALID_SENDER"; + case FIRAuthErrorCodeInvalidRecipientEmail: + return @"ERROR_INVALID_RECIPIENT_EMAIL"; + case FIRAuthErrorCodeMissingPhoneNumber: + return @"ERROR_MISSING_PHONE_NUMBER"; + case FIRAuthErrorCodeInvalidPhoneNumber: + return @"ERROR_INVALID_PHONE_NUMBER"; + case FIRAuthErrorCodeMissingVerificationCode: + return @"ERROR_MISSING_VERIFICATION_CODE"; + case FIRAuthErrorCodeInvalidVerificationCode: + return @"ERROR_INVALID_VERIFICATION_CODE"; + case FIRAuthErrorCodeMissingVerificationID: + return @"ERROR_MISSING_VERIFICATION_ID"; + case FIRAuthErrorCodeInvalidVerificationID: + return @"ERROR_INVALID_VERIFICATION_ID"; + case FIRAuthErrorCodeSessionExpired: + return @"ERROR_SESSION_EXPIRED"; + case FIRAuthErrorCodeMissingAppCredential: + return @"MISSING_APP_CREDENTIAL"; + case FIRAuthErrorCodeInvalidAppCredential: + return @"INVALID_APP_CREDENTIAL"; + case FIRAuthErrorCodeQuotaExceeded: + return @"ERROR_QUOTA_EXCEEDED"; + case FIRAuthErrorCodeMissingAppToken: + return @"ERROR_MISSING_APP_TOKEN"; + case FIRAuthErrorCodeNotificationNotForwarded: + return @"ERROR_NOTIFICATION_NOT_FORWARDED"; + case FIRAuthErrorCodeAppNotVerified: + return @"ERROR_APP_NOT_VERIFIED"; + } +} + +@implementation FIRAuthErrorUtils + ++ (NSError *)errorWithCode:(FIRAuthInternalErrorCode)code { + return [self errorWithCode:code message:nil]; +} + ++ (NSError *)errorWithCode:(FIRAuthInternalErrorCode)code + message:(nullable NSString *)message { + NSDictionary *userInfo = nil; + if (message.length) { + userInfo = @{ + NSLocalizedDescriptionKey : message + }; + } + return [self errorWithCode:code userInfo:userInfo]; +} + ++ (NSError *)errorWithCode:(FIRAuthInternalErrorCode)code + underlyingError:(nullable NSError *)underlyingError { + NSDictionary *errorUserInfo = nil; + if (underlyingError) { + errorUserInfo = @{ + NSUnderlyingErrorKey : underlyingError + }; + } + return [self errorWithCode:code userInfo:errorUserInfo]; +} + ++ (NSError *)errorWithCode:(FIRAuthInternalErrorCode)code userInfo:(NSDictionary *)userInfo { + BOOL isPublic = (code & FIRAuthPublicErrorCodeFlag) == FIRAuthPublicErrorCodeFlag; + if (isPublic) { + // This is a public error. Return it as a public error and add a description. + NSInteger errorCode = code & ~FIRAuthPublicErrorCodeFlag; + NSMutableDictionary *errorUserInfo = [NSMutableDictionary dictionaryWithDictionary:userInfo]; + if (!errorUserInfo[NSLocalizedDescriptionKey]) { + errorUserInfo[NSLocalizedDescriptionKey] = FIRAuthErrorDescription(errorCode); + } + errorUserInfo[FIRAuthErrorNameKey] = FIRAuthErrorCodeString(errorCode); + return [NSError errorWithDomain:FIRAuthErrorDomain code:errorCode userInfo:errorUserInfo]; + } else { + // This is an internal error. Wrap it in an internal error. + NSError *error = + [NSError errorWithDomain:FIRAuthInternalErrorDomain code:code userInfo:userInfo]; + return [self errorWithCode:FIRAuthInternalErrorCodeInternalError underlyingError:error]; + } +} + ++ (NSError *)RPCRequestEncodingErrorWithUnderlyingError:(NSError *)underlyingError { + return [self errorWithCode:FIRAuthInternalErrorCodeRPCRequestEncodingError + underlyingError:underlyingError]; +} + ++ (NSError *)JSONSerializationErrorForUnencodableType { + return [self errorWithCode:FIRAuthInternalErrorCodeJSONSerializationError]; +} + ++ (NSError *)JSONSerializationErrorWithUnderlyingError:(NSError *)underlyingError { + return [self errorWithCode:FIRAuthInternalErrorCodeJSONSerializationError + underlyingError:underlyingError]; +} + ++ (NSError *)networkErrorWithUnderlyingError:(NSError *)underlyingError { + return [self errorWithCode:FIRAuthInternalErrorCodeNetworkError + underlyingError:underlyingError]; +} + ++ (NSError *)unexpectedErrorResponseWithData:(NSData *)data + underlyingError:(NSError *)underlyingError { + return [self errorWithCode:FIRAuthInternalErrorCodeUnexpectedErrorResponse userInfo:@{ + FIRAuthErrorUserInfoDataKey : data, + NSUnderlyingErrorKey : underlyingError + }]; +} + ++ (NSError *)unexpectedErrorResponseWithDeserializedResponse:(id)deserializedResponse { + return [self errorWithCode:FIRAuthInternalErrorCodeUnexpectedErrorResponse userInfo:@{ + FIRAuthErrorUserInfoDeserializedResponseKey : deserializedResponse + }]; +} + ++ (NSError *)unexpectedResponseWithData:(NSData *)data + underlyingError:(NSError *)underlyingError { + return [self errorWithCode:FIRAuthInternalErrorCodeUnexpectedResponse userInfo:@{ + FIRAuthErrorUserInfoDataKey : data, + NSUnderlyingErrorKey : underlyingError + }]; +} + ++ (NSError *)unexpectedResponseWithDeserializedResponse:(id)deserializedResponse { + return [self errorWithCode:FIRAuthInternalErrorCodeUnexpectedResponse userInfo:@{ + FIRAuthErrorUserInfoDeserializedResponseKey : deserializedResponse + }]; +} + ++ (NSError *)unexpectedResponseWithDeserializedResponse:(nullable id)deserializedResponse + underlyingError:(NSError *)underlyingError { + NSMutableDictionary *userInfo = + [NSMutableDictionary dictionaryWithDictionary:@{ NSUnderlyingErrorKey : underlyingError }]; + if (deserializedResponse) { + userInfo[FIRAuthErrorUserInfoDeserializedResponseKey] = deserializedResponse; + } + return [self errorWithCode:FIRAuthInternalErrorCodeUnexpectedResponse userInfo:userInfo]; +} + ++ (NSError *)RPCResponseDecodingErrorWithDeserializedResponse:(id)deserializedResponse + underlyingError:(NSError *)underlyingError { + return [self errorWithCode:FIRAuthInternalErrorCodeRPCResponseDecodingError userInfo:@{ + FIRAuthErrorUserInfoDeserializedResponseKey : deserializedResponse, + NSUnderlyingErrorKey : underlyingError + }]; +} + ++ (NSError *)emailAlreadyInUseErrorWithEmail:(nullable NSString *)email { + NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init]; + if (email.length) { + userInfo[FIRAuthErrorUserInfoEmailKey] = email; + } + return [self errorWithCode:FIRAuthInternalErrorCodeEmailAlreadyInUse userInfo:userInfo]; +} + ++ (NSError *)userDisabledErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeUserDisabled message:message]; +} + ++ (NSError *)wrongPasswordErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeWrongPassword message:message]; +} + ++ (NSError *)tooManyRequestsErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeTooManyRequests message:message]; +} + ++ (NSError *)invalidCustomTokenErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeInvalidCustomToken message:message]; +} + ++ (NSError *)customTokenMistmatchErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeCustomTokenMismatch message:message]; +} + ++ (NSError *)invalidCredentialErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeInvalidCredential message:message]; +} + ++ (NSError *)requiresRecentLoginErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeRequiresRecentLogin message:message]; +} + ++ (NSError *)invalidUserTokenErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeInvalidUserToken message:message]; +} + ++ (NSError *)invalidEmailErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeInvalidEmail message:message]; +} + ++ (NSError *)accountExistsWithDifferentCredentialErrorWithEmail:(nullable NSString *)email { + return [self errorWithCode:FIRAuthInternalErrorCodeAccountExistsWithDifferentCredential + userInfo:@{ FIRAuthErrorUserInfoEmailKey : email }]; +} + ++ (NSError *)providerAlreadyLinkedError { + return [self errorWithCode:FIRAuthInternalErrorCodeProviderAlreadyLinked]; +} + ++ (NSError *)noSuchProviderError { + return [self errorWithCode:FIRAuthInternalErrorCodeNoSuchProvider]; +} + ++ (NSError *)userTokenExpiredErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeUserTokenExpired message:message]; +} + ++ (NSError *)userNotFoundErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeUserNotFound message:message]; +} + ++ (NSError *)invalidAPIKeyError { + return [self errorWithCode:FIRAuthInternalErrorCodeInvalidAPIKey]; +} + ++ (NSError *)userMismatchError { + return [self errorWithCode:FIRAuthInternalErrorCodeUserMismatch]; +} + ++ (NSError *)credentialAlreadyInUseErrorWithMessage:(nullable NSString *)message + credential:(nullable FIRPhoneAuthCredential *)credential { + if (credential) { + return [self errorWithCode:FIRAuthInternalErrorCodeCredentialAlreadyInUse + userInfo:@{ FIRAuthUpdatedCredentialKey : credential }]; + } + return [self errorWithCode:FIRAuthInternalErrorCodeCredentialAlreadyInUse message:message]; +} + ++ (NSError *)operationNotAllowedErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeOperationNotAllowed message:message]; +} + ++ (NSError *)weakPasswordErrorWithServerResponseReason:(NSString *)reason { + return [self errorWithCode:FIRAuthInternalErrorCodeWeakPassword userInfo:@{ + NSLocalizedFailureReasonErrorKey : reason + }]; +} + ++ (NSError *)appNotAuthorizedError { + return [self errorWithCode:FIRAuthInternalErrorCodeAppNotAuthorized]; +} + ++ (NSError *)expiredActionCodeErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeExpiredActionCode message:message]; +} + ++ (NSError *)invalidActionCodeErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeInvalidActionCode message:message]; +} + ++ (NSError *)invalidMessagePayloadErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeInvalidMessagePayload message:message]; +} + ++ (NSError *)invalidSenderErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeInvalidSender message:message]; +} + ++ (NSError *)invalidRecipientEmailErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeInvalidRecipientEmail message:message]; +} + ++ (NSError *)missingPhoneNumberErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeMissingPhoneNumber message:message]; +} + ++ (NSError *)invalidPhoneNumberErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeInvalidPhoneNumber message:message]; +} + ++ (NSError *)missingVerificationCodeErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeMissingVerificationCode message:message]; +} + ++ (NSError *)invalidVerificationCodeErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeInvalidVerificationCode message:message]; +} + ++ (NSError *)missingVerificationIDErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeMissingVerificationID message:message]; +} + ++ (NSError *)invalidVerificationIDErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeInvalidVerificationID message:message]; +} + ++ (NSError *)sessionExpiredErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeSessionExpired message:message]; +} + ++ (NSError *)missingAppCredentialWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeMissingAppCredential message:message]; +} + ++ (NSError *)invalidAppCredentialWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeInvalidAppCredential message:message]; +} + ++ (NSError *)quotaExceededErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeQuotaExceeded message:message]; +} + ++ (NSError *)missingAppTokenError { + return [self errorWithCode:FIRAuthInternalErrorCodeMissingAppToken]; +} + ++ (NSError *)notificationNotForwardedError { + return [self errorWithCode:FIRAuthInternalErrorCodeNotificationNotForwarded]; +} + ++ (NSError *)appNotVerifiedErrorWithMessage:(nullable NSString *)message { + return [self errorWithCode:FIRAuthInternalErrorCodeAppNotVerified message:message]; +} + ++ (NSError *)keychainErrorWithFunction:(NSString *)keychainFunction status:(OSStatus)status { + NSString *failureReason = [NSString stringWithFormat:@"%@ (%li)", keychainFunction, (long)status]; + return [self errorWithCode:FIRAuthInternalErrorCodeKeychainError userInfo:@{ + NSLocalizedFailureReasonErrorKey : failureReason, + }]; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthErrors.h b/Firebase/Auth/Source/FIRAuthErrors.h new file mode 100644 index 0000000..c1e7900 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthErrors.h @@ -0,0 +1,258 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthSwiftNameSupport.h" + +/** @class FIRAuthErrors + @remarks Error Codes common to all API Methods: + <ul> + <li>@c FIRAuthErrorCodeNetworkError</li> + <li>@c FIRAuthErrorCodeUserNotFound</li> + <li>@c FIRAuthErrorCodeUserTokenExpired</li> + <li>@c FIRAuthErrorCodeTooManyRequests</li> + <li>@c FIRAuthErrorCodeInvalidAPIKey</li> + <li>@c FIRAuthErrorCodeAppNotAuthorized</li> + <li>@c FIRAuthErrorCodeKeychainError</li> + <li>@c FIRAuthErrorCodeInternalError</li> + </ul> + @remarks Common error codes for @c FIRUser operations: + <ul> + <li>@c FIRAuthErrorCodeInvalidUserToken</li> + <li>@c FIRAuthErrorCodeUserDisabled</li> + </ul> + */ +FIR_SWIFT_NAME(AuthErrors) +@interface FIRAuthErrors + +/** + @brief The Firebase Auth error domain. + */ +extern NSString *const FIRAuthErrorDomain FIR_SWIFT_NAME(AuthErrorDomain); + +/** + @brief The key used to read the updated credential from the userinfo dictionary of the NSError + object returned in the case that the credential being linked in already in use. + */ +extern NSString *const FIRAuthUpdatedCredentialKey FIR_SWIFT_NAME(AuthUpdatedCredentialKey); + +/** + @brief The name of the key for the "error_name" string in the NSError userinfo dictionary. + */ +extern NSString *const FIRAuthErrorNameKey FIR_SWIFT_NAME(AuthErrorNameKey); + +/** @var FIRAuthErrorUserInfoEmailKey + @brief Errors with the code @c FIRAuthErrorCodeEmailAlreadyInUse may contains an + @c NSError.userInfo dictinary which contains this key. The value associated with this key is + an NSString of the email address that already exists. + */ +extern NSString *const FIRAuthErrorUserInfoEmailKey FIR_SWIFT_NAME(AuthErrorUserInfoEmailKey); + +/** + @brief Error codes used by Firebase Auth. + */ +typedef NS_ENUM(NSInteger, FIRAuthErrorCode) { + /** Indicates a validation error with the custom token. + */ + FIRAuthErrorCodeInvalidCustomToken = 17000, + + /** Indicates the service account and the API key belong to different projects. + */ + FIRAuthErrorCodeCustomTokenMismatch = 17002, + + /** Indicates the IDP token or requestUri is invalid. + */ + FIRAuthErrorCodeInvalidCredential = 17004, + + /** Indicates the user's account is disabled on the server. + */ + FIRAuthErrorCodeUserDisabled = 17005, + + /** Indicates the administrator disabled sign in with the specified identity provider. + */ + FIRAuthErrorCodeOperationNotAllowed = 17006, + + /** Indicates the email used to attempt a sign up is already in use. + */ + FIRAuthErrorCodeEmailAlreadyInUse = 17007, + + /** Indicates the email is invalid. + */ + FIRAuthErrorCodeInvalidEmail = 17008, + + /** Indicates the user attempted sign in with a wrong password. + */ + FIRAuthErrorCodeWrongPassword = 17009, + + /** Indicates that too many requests were made to a server method. + */ + FIRAuthErrorCodeTooManyRequests = 17010, + + /** Indicates the user account was not found. + */ + FIRAuthErrorCodeUserNotFound = 17011, + + /** Indicates account linking is required. + */ + FIRAuthErrorCodeAccountExistsWithDifferentCredential = 17012, + + /** Indicates the user has attemped to change email or password more than 5 minutes after + signing in. + */ + FIRAuthErrorCodeRequiresRecentLogin = 17014, + + /** Indicates an attempt to link a provider to which the account is already linked. + */ + FIRAuthErrorCodeProviderAlreadyLinked = 17015, + + /** Indicates an attempt to unlink a provider that is not linked. + */ + FIRAuthErrorCodeNoSuchProvider = 17016, + + /** Indicates user's saved auth credential is invalid, the user needs to sign in again. + */ + FIRAuthErrorCodeInvalidUserToken = 17017, + + /** Indicates a network error occurred (such as a timeout, interrupted connection, or + unreachable host). These types of errors are often recoverable with a retry. The @c + NSUnderlyingError field in the @c NSError.userInfo dictionary will contain the error + encountered. + */ + FIRAuthErrorCodeNetworkError = 17020, + + /** Indicates the saved token has expired, for example, the user may have changed account + password on another device. The user needs to sign in again on the device that made this + request. + */ + FIRAuthErrorCodeUserTokenExpired = 17021, + + /** Indicates an invalid API key was supplied in the request. + */ + FIRAuthErrorCodeInvalidAPIKey = 17023, + + /** Indicates that an attempt was made to reauthenticate with a user which is not the current + user. + */ + FIRAuthErrorCodeUserMismatch = 17024, + + /** Indicates an attempt to link with a credential that has already been linked with a + different Firebase account + */ + FIRAuthErrorCodeCredentialAlreadyInUse = 17025, + + /** Indicates an attempt to set a password that is considered too weak. + */ + FIRAuthErrorCodeWeakPassword = 17026, + + /** Indicates the App is not authorized to use Firebase Authentication with the + provided API Key. + */ + FIRAuthErrorCodeAppNotAuthorized = 17028, + + /** Indicates the OOB code is expired. + */ + FIRAuthErrorCodeExpiredActionCode = 17029, + + /** Indicates the OOB code is invalid. + */ + FIRAuthErrorCodeInvalidActionCode = 17030, + + /** Indicates that there are invalid parameters in the payload during a "send password reset + * email" attempt. + */ + FIRAuthErrorCodeInvalidMessagePayload = 17031, + + /** Indicates that the sender email is invalid during a "send password reset email" attempt. + */ + FIRAuthErrorCodeInvalidSender = 17032, + + /** Indicates that the recipient email is invalid. + */ + FIRAuthErrorCodeInvalidRecipientEmail = 17033, + + // The enum values between 17033 and 17041 are reserved and should NOT be used for new error + // codes. + + /** Indicates that a phone number was not provided in a call to @c + verifyPhoneNumber:completion:. + */ + FIRAuthErrorCodeMissingPhoneNumber = 17041, + + /** Indicates that an invalid phone number was provided in a call to @c + verifyPhoneNumber:completion:. + */ + FIRAuthErrorCodeInvalidPhoneNumber = 17042, + + /** Indicates that the phone auth credential was created with an empty verification code. + */ + FIRAuthErrorCodeMissingVerificationCode = 17043, + + /** Indicates that an invalid verification code was used in the verifyPhoneNumber request. + */ + FIRAuthErrorCodeInvalidVerificationCode = 17044, + + /** Indicates that the phone auth credential was created with an empty verification ID. + */ + FIRAuthErrorCodeMissingVerificationID = 17045, + + /** Indicates that an invalid verification ID was used in the verifyPhoneNumber request. + */ + FIRAuthErrorCodeInvalidVerificationID = 17046, + + /** Indicates that the APNS device token is missing in the verifyClient request. + */ + FIRAuthErrorCodeMissingAppCredential = 17047, + + /** Indicates that an invalid APNS device token was used in the verifyClient request. + */ + FIRAuthErrorCodeInvalidAppCredential = 17048, + + // The enum values between 17048 and 17051 are reserved and should NOT be used for new error + // codes. + + /** Indicates that the SMS code has expired. + */ + FIRAuthErrorCodeSessionExpired = 17051, + + /** Indicates that the quota of SMS messages for a given project has been exceeded. + */ + FIRAuthErrorCodeQuotaExceeded = 17052, + + /** Indicates that the APNs device token could not be obtained. The app may not have set up + remote notification correctly, or may fail to forward the APNs device token to FIRAuth + if app delegate swizzling is disabled. + */ + FIRAuthErrorCodeMissingAppToken = 17053, + + /** Indicates that the app fails to forward remote notification to FIRAuth. + */ + FIRAuthErrorCodeNotificationNotForwarded = 17054, + + /** Indicates that the app could not be verified by Firebase during phone number authentication. + */ + FIRAuthErrorCodeAppNotVerified = 17055, + + /** Indicates an error occurred while attempting to access the keychain. + */ + FIRAuthErrorCodeKeychainError = 17995, + + /** Indicates an internal error occurred. + */ + FIRAuthErrorCodeInternalError = 17999, +} FIR_SWIFT_NAME(AuthErrorCode); + +@end diff --git a/Firebase/Auth/Source/FIRAuthExceptionUtils.h b/Firebase/Auth/Source/FIRAuthExceptionUtils.h new file mode 100644 index 0000000..3ae9159 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthExceptionUtils.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRAuthExceptionUtils + @brief Utility class used to raise standardized Auth related exceptions. +*/ +@interface FIRAuthExceptionUtils : NSObject + +/** @fn raiseInvalidParameterExceptionWithReason: + @brief raises the "invalid parameter" exception + @param reason string will contain a description of the error. + */ ++ (void)raiseInvalidParameterExceptionWithReason:(nullable NSString *)reason; + +/** @fn raiseMethodNotImplementedExceptionWithReason: + @brief raises the "method not implemented" exception + @param reason string will contain a description of the error. + @see FIRMethodNotImplementedException + */ ++ (void)raiseMethodNotImplementedExceptionWithReason:(nullable NSString *)reason; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthExceptionUtils.m b/Firebase/Auth/Source/FIRAuthExceptionUtils.m new file mode 100644 index 0000000..0adcd34 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthExceptionUtils.m @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#import "FIRAuthExceptionUtils.h" + +/** @var FIRMethodNotImplementedException + @brief The name of the "Method Not Implemented" exception. + */ +static NSString *const FIRMethodNotImplementedException = @"FIRMethodNotImplementedException"; + +@implementation FIRAuthExceptionUtils + ++ (void)raiseInvalidParameterExceptionWithReason:(NSString *)reason { + [NSException raise:NSInvalidArgumentException format:@"%@", reason]; +} + ++ (void)raiseMethodNotImplementedExceptionWithReason:(nullable NSString *)reason { + NSException *exception = + [NSException exceptionWithName:FIRMethodNotImplementedException reason:reason userInfo:nil]; + [exception raise]; +} + +@end diff --git a/Firebase/Auth/Source/FIRAuthGlobalWorkQueue.m b/Firebase/Auth/Source/FIRAuthGlobalWorkQueue.m new file mode 100644 index 0000000..8780959 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthGlobalWorkQueue.m @@ -0,0 +1,26 @@ +/* + * 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. + */ + +#import "Private/FIRAuthGlobalWorkQueue.h" + +dispatch_queue_t FIRAuthGlobalWorkQueue() { + static dispatch_once_t once; + static dispatch_queue_t queue; + dispatch_once(&once, ^{ + queue = dispatch_queue_create("com.google.firebase.auth.globalWorkQueue", NULL); + }); + return queue; +} diff --git a/Firebase/Auth/Source/FIRAuthKeychain.m b/Firebase/Auth/Source/FIRAuthKeychain.m new file mode 100644 index 0000000..68cf2f2 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthKeychain.m @@ -0,0 +1,256 @@ +/* + * 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. + */ + +#import "Private/FIRAuthKeychain.h" + +#import <Security/Security.h> + +#import "Private/FIRAuthErrorUtils.h" +#import "Private/FIRAuthUserDefaultsStorage.h" + +#if FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE +#import <UIKit/UIKit.h> + +/** @var kOSVersionMatcherForUsingUserDefaults + @brief The regular expression to match all OS versions that @c FIRAuthUserDefaultsStorage is + used instead if available. + */ +static NSString *const kOSVersionMatcherForUsingUserDefaults = @"^10\\.[01](\\..*)?$"; + +#endif // FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE + +/** @var kAccountPrefix + @brief The prefix string for keychain item account attribute before the key. + @remarks A number "1" is encoded in the prefix in case we need to upgrade the scheme in future. + */ +static NSString *const kAccountPrefix = @"firebase_auth_1_"; + +@implementation FIRAuthKeychain { + /** @var _service + @brief The name of the keychain service. + */ + NSString *_service; + + /** @var _legacyItemDeletedForKey + @brief Indicates whether or not this class knows that the legacy item for a particular key has + been deleted. + @remarks This dictionary is to avoid unecessary keychain operations against legacy items. + */ + NSMutableDictionary *_legacyEntryDeletedForKey; +} + +- (id<FIRAuthStorage>)initWithService:(NSString *)service { + +#if FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE + + NSString *OSVersion = [UIDevice currentDevice].systemVersion; + NSRegularExpression *regex = + [NSRegularExpression regularExpressionWithPattern:kOSVersionMatcherForUsingUserDefaults + options:0 + error:NULL]; + if ([regex numberOfMatchesInString:OSVersion options:0 range:NSMakeRange(0, OSVersion.length)]) { + return (id<FIRAuthStorage>)[[FIRAuthUserDefaultsStorage alloc] initWithService:service]; + } + +#endif // FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE + + self = [super init]; + if (self) { + _service = [service copy]; + _legacyEntryDeletedForKey = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (NSData *)dataForKey:(NSString *)key error:(NSError **_Nullable)error { + if (!key.length) { + [NSException raise:NSInvalidArgumentException + format:@"%@", @"The key cannot be nil or empty."]; + return nil; + } + NSData *data = [self itemWithQuery:[self genericPasswordQueryWithKey:key] error:error]; + if (error && *error) { + return nil; + } + if (data) { + return data; + } + // Check for legacy form. + if (_legacyEntryDeletedForKey[key]) { + return nil; + } + data = [self itemWithQuery:[self legacyGenericPasswordQueryWithKey:key] error:error]; + if (error && *error) { + return nil; + } + if (!data) { + // Mark legacy data as non-existing so we don't have to query it again. + _legacyEntryDeletedForKey[key] = @YES; + return nil; + } + // Move the data to current form. + if (![self setData:data forKey:key error:error]) { + return nil; + } + [self deleteLegacyItemWithKey:key]; + return data; +} + +- (BOOL)setData:(NSData *)data forKey:(NSString *)key error:(NSError **_Nullable)error { + if (!key.length) { + [NSException raise:NSInvalidArgumentException + format:@"%@", @"The key cannot be nil or empty."]; + return NO; + } + NSDictionary *attributes = @{ + (__bridge id)kSecValueData : data, + (__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, + }; + return [self setItemWithQuery:[self genericPasswordQueryWithKey:key] + attributes:attributes + error:error]; +} + +- (BOOL)removeDataForKey:(NSString *)key error:(NSError **_Nullable)error { + if (!key.length) { + [NSException raise:NSInvalidArgumentException + format:@"%@", @"The key cannot be nil or empty."]; + return NO; + } + if (![self deleteItemWithQuery:[self genericPasswordQueryWithKey:key] error:error]) { + return NO; + } + // Legacy form item, if exists, also needs to be removed, otherwise it will be exposed when + // current form item is removed, leading to incorrect semantics. + [self deleteLegacyItemWithKey:key]; + return YES; +} + +#pragma mark - Private + +- (NSData *)itemWithQuery:(NSDictionary *)query error:(NSError **_Nullable)error { + NSMutableDictionary *returningQuery = [query mutableCopy]; + returningQuery[(__bridge id)kSecReturnData] = @YES; + returningQuery[(__bridge id)kSecReturnAttributes] = @YES; + // Using a match limit of 2 means that we can check whether there is more than one item. + // If we used a match limit of 1 we would never find out. + returningQuery[(__bridge id)kSecMatchLimit] = @2; + + CFArrayRef result = NULL; + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)returningQuery, + (CFTypeRef *)&result); + + if (status == noErr && result != NULL) { + NSArray *items = (__bridge_transfer NSArray *)result; + if (items.count != 1) { + if (error) { + *error = [FIRAuthErrorUtils keychainErrorWithFunction:@"SecItemCopyMatching" + status:status]; + } + return nil; + } + + if (error) { + *error = nil; + } + NSDictionary *item = items[0]; + return item[(__bridge id)kSecValueData]; + } + + if (status == errSecItemNotFound) { + if (error) { + *error = nil; + } + } else { + if (error) { + *error = [FIRAuthErrorUtils keychainErrorWithFunction:@"SecItemCopyMatching" status:status]; + } + } + return nil; +} + +- (BOOL)setItemWithQuery:(NSDictionary *)query + attributes:(NSDictionary *)attributes + error:(NSError **_Nullable)error { + NSMutableDictionary *combined = [attributes mutableCopy]; + [combined addEntriesFromDictionary:query]; + BOOL hasItem = NO; + OSStatus status = SecItemAdd((__bridge CFDictionaryRef)combined, NULL); + + if (status == errSecDuplicateItem) { + hasItem = YES; + status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)attributes); + } + + if (status == noErr) { + return YES; + } + if (error) { + NSString *function = hasItem ? @"SecItemUpdate" : @"SecItemAdd"; + *error = [FIRAuthErrorUtils keychainErrorWithFunction:function status:status]; + } + return NO; +} + +- (BOOL)deleteItemWithQuery:(NSDictionary *)query error:(NSError **_Nullable)error { + OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); + if (status == noErr || status == errSecItemNotFound) { + return YES; + } + if (error) { + *error = [FIRAuthErrorUtils keychainErrorWithFunction:@"SecItemDelete" status:status]; + } + return NO; +} + +/** @fn deleteLegacyItemsWithKey: + @brief Deletes legacy item from the keychain if it is not already known to be deleted. + @param key The key for the item. + */ +- (void)deleteLegacyItemWithKey:(NSString *)key { + if (_legacyEntryDeletedForKey[key]) { + return; + } + NSDictionary *query = [self legacyGenericPasswordQueryWithKey:key]; + SecItemDelete((__bridge CFDictionaryRef)query); + _legacyEntryDeletedForKey[key] = @YES; +} + +/** @fn genericPasswordQueryWithKey: + @brief Returns a keychain query of generic password to be used to manipulate key'ed value. + @param key The key for the value being manipulated, used as the account field in the query. + */ +- (NSDictionary *)genericPasswordQueryWithKey:(NSString *)key { + return @{ + (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, + (__bridge id)kSecAttrAccount : [kAccountPrefix stringByAppendingString:key], + (__bridge id)kSecAttrService : _service, + }; +} + +/** @fn legacyGenericPasswordQueryWithKey: + @brief Returns a keychain query of generic password without service field, which is used by + previous version of this class. + @param key The key for the value being manipulated, used as the account field in the query. + */ +- (NSDictionary *)legacyGenericPasswordQueryWithKey:(NSString *)key { + return @{ + (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, + (__bridge id)kSecAttrAccount : key, + }; +} + +@end diff --git a/Firebase/Auth/Source/FIRAuthNotificationManager.m b/Firebase/Auth/Source/FIRAuthNotificationManager.m new file mode 100644 index 0000000..0692562 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthNotificationManager.m @@ -0,0 +1,175 @@ +/* + * 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. + */ + +#import "Private/FIRAuthNotificationManager.h" + +#import "FIRLogger.h" +#import "Private/FIRAuthAppCredential.h" +#import "Private/FIRAuthAppCredentialManager.h" +#import "FIRAuthGlobalWorkQueue.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @var kNotificationKey + @brief The key to locate payload data in the remote notification. + */ +static NSString *const kNotificationDataKey = @"com.google.firebase.auth"; + +/** @var kNotificationReceiptKey + @brief The key for the receipt in the remote notification payload data. + */ +static NSString *const kNotificationReceiptKey = @"receipt"; + +/** @var kNotificationSecretKey + @brief The key for the secret in the remote notification payload data. + */ +static NSString *const kNotificationSecretKey = @"secret"; + +/** @var kNotificationProberKey + @brief The key for marking the prober in the remote notification payload data. + */ +static NSString *const kNotificationProberKey = @"warning"; + +/** @var kProbingTimeout + @brief Timeout for probing whether the app delegate forwards the remote notification to us. + */ +static const NSTimeInterval kProbingTimeout = 1; + +@implementation FIRAuthNotificationManager { + /** @var _application + @brief The application. + */ + UIApplication *_application; + + /** @var _appCredentialManager + @brief The object to handle app credentials delivered via notification. + */ + FIRAuthAppCredentialManager *_appCredentialManager; + + /** @var _hasCheckedNotificationForwarding + @brief Whether notification forwarding has been checked or not. + */ + BOOL _hasCheckedNotificationForwarding; + + /** @var _isNotificationBeingForwarded + @brief Whether or not notification is being forwarded + */ + BOOL _isNotificationBeingForwarded; + + /** @var _pendingCallbacks + @brief All pending callbacks while a check is being performed. + */ + NSMutableArray<FIRAuthNotificationForwardingCallback> *_pendingCallbacks; +} + +- (instancetype)initWithApplication:(UIApplication *)application + appCredentialManager:(FIRAuthAppCredentialManager *)appCredentialManager { + self = [super init]; + if (self) { + _application = application; + _appCredentialManager = appCredentialManager; + _timeout = kProbingTimeout; + } + return self; +} + +- (void)checkNotificationForwardingWithCallback:(FIRAuthNotificationForwardingCallback)callback { + if (_pendingCallbacks) { + [_pendingCallbacks addObject:callback]; + return; + } + if (_hasCheckedNotificationForwarding) { + callback(_isNotificationBeingForwarded); + return; + } + _hasCheckedNotificationForwarding = YES; + _pendingCallbacks = + [[NSMutableArray<FIRAuthNotificationForwardingCallback> alloc] initWithObjects:callback, nil]; + dispatch_async(dispatch_get_main_queue(), ^{ + NSDictionary *proberNotification = @{ + kNotificationDataKey : @{ + kNotificationProberKey : @"This fake notification should be forwarded to Firebase Auth." + } + }; + if ([_application.delegate respondsToSelector: + @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]) { + [_application.delegate application:_application + didReceiveRemoteNotification:proberNotification + fetchCompletionHandler:^(UIBackgroundFetchResult result) {}]; + } else if ([_application.delegate respondsToSelector: + @selector(application:didReceiveRemoteNotification:)]) { + [_application.delegate application:_application + didReceiveRemoteNotification:proberNotification]; + } else { + FIRLogError(kFIRLoggerAuth, @"I-AUT000015", + @"The UIApplicationDelegate must handle remote notifcation for phone number " + @"authentication to work."); + } + }); + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_timeout * NSEC_PER_SEC)), + FIRAuthGlobalWorkQueue(), ^{ + [self callBack]; + }); +} + +- (BOOL)canHandleNotification:(NSDictionary *)notification { + NSDictionary *data = notification[kNotificationDataKey]; + if ([data isKindOfClass:[NSString class]]) { + // Deserialize in case the data is a JSON string. + NSData *JSONData = [((NSString *)data) dataUsingEncoding:NSUTF8StringEncoding]; + data = [NSJSONSerialization JSONObjectWithData:JSONData options:0 error:NULL]; + } + if (![data isKindOfClass:[NSDictionary class]]) { + return NO; + } + if (data[kNotificationProberKey]) { + if (!_pendingCallbacks) { + // The prober notification probably comes from another instance, so pass it along. + return NO; + } + _isNotificationBeingForwarded = YES; + [self callBack]; + return YES; + } + NSString *receipt = data[kNotificationReceiptKey]; + if (![receipt isKindOfClass:[NSString class]]) { + return NO; + } + NSString *secret = data[kNotificationSecretKey]; + if (![receipt isKindOfClass:[NSString class]]) { + return NO; + } + return [_appCredentialManager canFinishVerificationWithReceipt:receipt secret:secret]; +} + +#pragma mark - Internal methods + +/** @fn callBack + @brief Calls back all pending callbacks with the result of notification forwarding check. + */ +- (void)callBack { + if (!_pendingCallbacks) { + return; + } + NSArray<FIRAuthNotificationForwardingCallback> *allCallbacks = _pendingCallbacks; + _pendingCallbacks = nil; + for (FIRAuthNotificationForwardingCallback callback in allCallbacks) { + callback(_isNotificationBeingForwarded); + } +}; + +@end +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRAuthProvider.m b/Firebase/Auth/Source/FIRAuthProvider.m new file mode 100644 index 0000000..6df86d7 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthProvider.m @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +// Declared 'extern' in FIRGoogleAuthProvider.h +NSString *const FIRGoogleAuthProviderID = @"google.com"; + +// Declared 'extern' in FIRFacebookAuthProvider.h +NSString *const FIRFacebookAuthProviderID = @"facebook.com"; + +// Declared 'extern' in FIREmailAuthProvider.h +NSString *const FIREmailAuthProviderID = @"password"; + +// Declared 'extern' in FIREmailAuthProvider.h +NSString *const FIREmailPasswordAuthProviderID = @"password"; + +// Declared 'extern' in FIRTwitterAuthProvider.h +NSString *const FIRTwitterAuthProviderID = @"twitter.com"; + +// Declared 'extern' in FIRGitHubAuthProvider.h +NSString *const FIRGitHubAuthProviderID = @"github.com"; + +// Declared 'extern' in FIRPhoneAuthProvider.h +NSString *const FIRPhoneAuthProviderID = @"phone"; diff --git a/Firebase/Auth/Source/FIRAuthSerialTaskQueue.m b/Firebase/Auth/Source/FIRAuthSerialTaskQueue.m new file mode 100644 index 0000000..47e6cd5 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthSerialTaskQueue.m @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#import "Private/FIRAuthSerialTaskQueue.h" + +#import "Private/FIRAuthGlobalWorkQueue.h" + +@implementation FIRAuthSerialTaskQueue { + /** @var _dispatchQueue + @brief The asyncronous dispatch queue into which tasks are enqueued and processed + serially. + */ + dispatch_queue_t _dispatchQueue; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _dispatchQueue = dispatch_queue_create("com.google.firebase.auth.serialTaskQueue", NULL); + dispatch_set_target_queue(_dispatchQueue, FIRAuthGlobalWorkQueue()); + } + return self; +} + +- (void)enqueueTask:(FIRAuthSerialTask)task { + // This dispatch queue will run tasks serially in FIFO order, as long as it's not suspended. + dispatch_async(_dispatchQueue, ^{ + // But as soon as a task is started, stop other tasks from running until the task calls it's + // completion handler, which allows the queue to resume processing of tasks. This allows the + // task to perform other asyncronous actions on other dispatch queues and "get back to us" when + // all of their sub-tasks are complete. + dispatch_suspend(_dispatchQueue); + task(^{ + dispatch_resume(_dispatchQueue); + }); + }); +} + +@end diff --git a/Firebase/Auth/Source/FIRAuthSwiftNameSupport.h b/Firebase/Auth/Source/FIRAuthSwiftNameSupport.h new file mode 100644 index 0000000..f58bdd7 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthSwiftNameSupport.h @@ -0,0 +1,29 @@ +/* + * 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. + */ + +#ifndef FIR_SWIFT_NAME + +#import <Foundation/Foundation.h> + +// NS_SWIFT_NAME can only translate factory methods before the iOS 9.3 SDK. +// // Wrap it in our own macro if it's a non-compatible SDK. +#ifdef __IPHONE_9_3 +#define FIR_SWIFT_NAME(X) NS_SWIFT_NAME(X) +#else +#define FIR_SWIFT_NAME(X) // Intentionally blank. +#endif // #ifdef __IPHONE_9_3 + +#endif // FIR_SWIFT_NAME diff --git a/Firebase/Auth/Source/FIRAuthUserDefaultsStorage.m b/Firebase/Auth/Source/FIRAuthUserDefaultsStorage.m new file mode 100644 index 0000000..ad23f41 --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthUserDefaultsStorage.m @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#import "Private/FIRAuthUserDefaultsStorage.h" + +#if FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE + +NS_ASSUME_NONNULL_BEGIN + +static NSString *const kPersistentDomainNamePrefix = @"com.google.Firebase.Auth."; + +@implementation FIRAuthUserDefaultsStorage { + /** @var _persistentDomainName + @brief The name of the persistent domain in user defaults. + */ + NSString *_persistentDomainName; + + /** @var _storage + @brief The backing NSUserDefaults storage for this instance. + */ + NSUserDefaults *_storage; +} + +- (id<FIRAuthStorage>)initWithService:(NSString *)service { + self = [super init]; + if (self) { + _persistentDomainName = [kPersistentDomainNamePrefix stringByAppendingString:service]; + _storage = [[NSUserDefaults alloc] init]; + } + return self; +} + +- (nullable NSData *)dataForKey:(NSString *)key error:(NSError **_Nullable)error { + if (error) { + *error = nil; + } + NSDictionary<NSString *, id> *allData = [_storage persistentDomainForName:_persistentDomainName]; + return allData[key]; +} + +- (BOOL)setData:(NSData *)data forKey:(NSString *)key error:(NSError **_Nullable)error { + NSMutableDictionary<NSString *, id> *allData = + [([_storage persistentDomainForName:_persistentDomainName] ?: @{}) mutableCopy]; + allData[key] = data; + [_storage setPersistentDomain:allData forName:_persistentDomainName]; + return YES; +} + +- (BOOL)removeDataForKey:(NSString *)key error:(NSError **_Nullable)error { + NSMutableDictionary<NSString *, id> *allData = + [[_storage persistentDomainForName:_persistentDomainName] mutableCopy]; + [allData removeObjectForKey:key]; + [_storage setPersistentDomain:allData forName:_persistentDomainName]; + return YES; +} + +- (void)clear { + [_storage setPersistentDomain:@{} forName:_persistentDomainName]; +} + +@end + +NS_ASSUME_NONNULL_END + +#endif // FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE diff --git a/Firebase/Auth/Source/FIRSecureTokenService.h b/Firebase/Auth/Source/FIRSecureTokenService.h new file mode 100644 index 0000000..cb29127 --- /dev/null +++ b/Firebase/Auth/Source/FIRSecureTokenService.h @@ -0,0 +1,96 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** @typedef FIRFetchAccessTokenCallback + @brief The callback used to return the value of attempting to fetch an access token. + + In the event the operation was successful @c token will be set and @c error will be @c nil. + In the event of failure @c token will be @c nil and @c error will be set. + @c tokenUpdated indicates whether either the access or the refresh token has been updated. + + The token returned should be considered ephemeral and not cached. It should be used immediately + and discarded. All operations that need this token should call fetchAccessToken and do their + work from the callback. + */ +typedef void(^FIRFetchAccessTokenCallback)(NSString *_Nullable token, + NSError *_Nullable error, + BOOL tokenUpdated); + +/** @class FIRSecureTokenService + @brief Provides services for token exchanges and refreshes. + */ +@interface FIRSecureTokenService : NSObject <NSSecureCoding> + +/** @property rawAccessToken + @brief The cached access token. + @remarks This method is specifically for providing the access token to internal clients during + deserialization and sign-in events, and should not be used to retrieve the access token by + anyone else. + */ +@property(nonatomic, copy, readonly) NSString *rawAccessToken; + +/** @property refreshToken + @brief The refresh token for the user, or @c nil if the user has yet completed sign-in flow. + */ +@property(nonatomic, copy, readonly, nullable) NSString *refreshToken; + +/** @property accessTokenExpirationDate + @brief The expiration date of the cached access token. + */ +@property(nonatomic, copy, readonly, nullable) NSDate *accessTokenExpirationDate; + +/** @fn init + @brief Please use @c initWithAPIKey:authorizationCode: . + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @fn initWithAPIKey:authorizationCode: + @brief Creates a @c FIRSecureTokenService with an authroization code. + @param APIKey A Google API key for making STS requests. + @param authorizationCode An authorization code which needs to be exchanged for STS tokens. + */ +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey + authorizationCode:(NSString *)authorizationCode; + +/** @fn initWithAPIKey:authorizationCode: + @brief Creates a @c FIRSecureTokenService with an authroization code. + @param APIKey A Google API key for making STS requests. + @param accessToken The STS access token. + @param accessTokenExpirationDate The approximate expiration date of the access token. + @param refreshToken The STS refresh token. + */ +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey + accessToken:(nullable NSString *)accessToken + accessTokenExpirationDate:(nullable NSDate *)accessTokenExpirationDate + refreshToken:(NSString *)refreshToken; + +/** @fn fetchAccessTokenForcingRefresh:callback: + @brief Fetch a fresh ephemeral access token for the ID associated with this instance. The token + received in the callback should be considered short lived and not cached. + @param forceRefresh Forces the token to be refreshed. + @param callback Callback block that will be called to return either the token or an error. + Invoked asyncronously on the auth global work queue in the future. + */ +- (void)fetchAccessTokenForcingRefresh:(BOOL)forceRefresh + callback:(FIRFetchAccessTokenCallback)callback; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRSecureTokenService.m b/Firebase/Auth/Source/FIRSecureTokenService.m new file mode 100644 index 0000000..e88b41c --- /dev/null +++ b/Firebase/Auth/Source/FIRSecureTokenService.m @@ -0,0 +1,214 @@ +/* + * 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. + */ + +#import "FIRSecureTokenService.h" + +#import "FIRAuth.h" +#import "Private/FIRAuthKeychain.h" +#import "Private/FIRAuthSerialTaskQueue.h" +#import "FIRAuthBackend.h" +#import "FIRSecureTokenRequest.h" +#import "FIRSecureTokenResponse.h" + +/** @var kAPIKeyCodingKey + @brief The key used to encode the APIKey for NSSecureCoding. + */ +static NSString *const kAPIKeyCodingKey = @"APIKey"; + +/** @var kRefreshTokenKey + @brief The key used to encode the refresh token for NSSecureCoding. + */ +static NSString *const kRefreshTokenKey = @"refreshToken"; + +/** @var kAccessTokenKey + @brief The key used to encode the access token for NSSecureCoding. + */ +static NSString *const kAccessTokenKey = @"accessToken"; + +/** @var kAccessTokenExpirationDateKey + @brief The key used to encode the access token expiration date for NSSecureCoding. + */ +static NSString *const kAccessTokenExpirationDateKey = @"accessTokenExpirationDate"; + +/** @var kFiveMinutes + @brief Five minutes (in seconds.) + */ +static const NSTimeInterval kFiveMinutes = 5 * 60; + +@interface FIRSecureTokenService () +/** @fn initWithAPIKey: + @brief Creates a @c FIRSecureTokenService without a credential. + @param APIKey A Google API key for making STS requests. + */ +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey NS_DESIGNATED_INITIALIZER; +@end + +@implementation FIRSecureTokenService { + /** @var _APIKey + @brief A Google API key for making Secure Token Service requests. + */ + NSString *_APIKey; + + /** @var _taskQueue + @brief Used to serialize all requests for access tokens. + */ + FIRAuthSerialTaskQueue *_taskQueue; + + /** @var _authorizationCode + @brief An authorization code which needs to be exchanged for Secure Token Service tokens. + */ + NSString *_Nullable _authorizationCode; + + /** @var _accessToken + @brief The currently cached access token. Or |nil| if no token is currently cached. + */ + NSString *_Nullable _accessToken; +} + +- (instancetype)init { + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey { + self = [super init]; + if (self) { + _APIKey = [APIKey copy]; + _taskQueue = [[FIRAuthSerialTaskQueue alloc] init]; + } + return self; +} + +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey + authorizationCode:(NSString *)authorizationCode { + self = [self initWithAPIKey:APIKey]; + if (self) { + _authorizationCode = [authorizationCode copy]; + } + return self; +} + +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey + accessToken:(nullable NSString *)accessToken + accessTokenExpirationDate:(nullable NSDate *)accessTokenExpirationDate + refreshToken:(NSString *)refreshToken { + self = [self initWithAPIKey:APIKey]; + if (self) { + _accessToken = [accessToken copy]; + _accessTokenExpirationDate = [accessTokenExpirationDate copy]; + _refreshToken = [refreshToken copy]; + } + return self; +} + +- (void)fetchAccessTokenForcingRefresh:(BOOL)forceRefresh + callback:(FIRFetchAccessTokenCallback)callback { + [_taskQueue enqueueTask:^(FIRAuthSerialTaskCompletionBlock complete) { + if (!forceRefresh && [self hasValidAccessToken]) { + complete(); + callback(_accessToken, nil, NO); + } else { + [self requestAccessToken:^(NSString *_Nullable token, + NSError *_Nullable error, + BOOL tokenUpdated) { + complete(); + callback(token, error, tokenUpdated); + }]; + } + }]; +} + +- (NSString *)rawAccessToken { + return _accessToken; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { + NSString *APIKey = [aDecoder decodeObjectOfClass:[NSString class] forKey:kAPIKeyCodingKey]; + NSString *refreshToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:kRefreshTokenKey]; + NSString *accessToken = [aDecoder decodeObjectOfClass:[NSString class] forKey:kAccessTokenKey]; + NSDate *accessTokenExpirationDate = + [aDecoder decodeObjectOfClass:[NSDate class] forKey:kAccessTokenExpirationDateKey]; + if (!APIKey || !refreshToken) { + return nil; + } + self = [self initWithAPIKey:APIKey]; + if (self) { + _refreshToken = refreshToken; + _accessToken = accessToken; + _accessTokenExpirationDate = accessTokenExpirationDate; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_APIKey forKey:kAPIKeyCodingKey]; + // Authorization code is not encoded because it is not long-lived. + [aCoder encodeObject:_refreshToken forKey:kRefreshTokenKey]; + [aCoder encodeObject:_accessToken forKey:kAccessTokenKey]; + [aCoder encodeObject:_accessTokenExpirationDate forKey:kAccessTokenExpirationDateKey]; +} + +#pragma mark - Private methods + +/** @fn requestAccessToken: + @brief Makes a request to STS for an access token. + @details This handles both the case that the token has not been granted yet and that it just + needs to be refreshed. The caller is responsible for making sure that this is occurring in + a @c _taskQueue task. + @param callback Called when the fetch is complete. Invoked asynchronously on the main thread in + the future. + @remarks Because this method is guaranteed to only be called from tasks enqueued in + @c _taskQueue, we do not need any @synchronized guards around access to _accessToken/etc. + since only one of those tasks is ever running at a time, and those tasks are the only + access to and mutation of these instance variables. + */ +- (void)requestAccessToken:(FIRFetchAccessTokenCallback)callback { + FIRSecureTokenRequest *request; + if (_refreshToken.length) { + request = [FIRSecureTokenRequest refreshRequestWithRefreshToken:_refreshToken APIKey:_APIKey]; + } else { + request = [FIRSecureTokenRequest authCodeRequestWithCode:_authorizationCode APIKey:_APIKey]; + } + [FIRAuthBackend secureToken:request + callback:^(FIRSecureTokenResponse *_Nullable response, + NSError *_Nullable error) { + BOOL tokenUpdated = NO; + NSString *newAccessToken = response.accessToken; + if (newAccessToken.length && ![newAccessToken isEqualToString:_accessToken]) { + _accessToken = [newAccessToken copy]; + _accessTokenExpirationDate = response.approximateExpirationDate; + tokenUpdated = YES; + } + NSString *newRefreshToken = response.refreshToken; + if (newRefreshToken.length && ![newRefreshToken isEqualToString:_refreshToken]) { + _refreshToken = [newRefreshToken copy]; + tokenUpdated = YES; + } + callback(newAccessToken, error, tokenUpdated); + }]; +} + +- (BOOL)hasValidAccessToken { + return _accessToken && [_accessTokenExpirationDate timeIntervalSinceNow] > kFiveMinutes; +} + +@end diff --git a/Firebase/Auth/Source/FIRUser.h b/Firebase/Auth/Source/FIRUser.h new file mode 100644 index 0000000..ebe8b81 --- /dev/null +++ b/Firebase/Auth/Source/FIRUser.h @@ -0,0 +1,463 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuth.h" +#import "FIRAuthDataResult.h" +#import "FIRAuthSwiftNameSupport.h" +#import "FIRUserInfo.h" + +@class FIRPhoneAuthCredential; +@class FIRUserProfileChangeRequest; + +NS_ASSUME_NONNULL_BEGIN + +/** @typedef FIRAuthTokenCallback + @brief The type of block called when a token is ready for use. + @see FIRUser.getIDTokenWithCompletion: + @see FIRUser.getIDTokenForcingRefresh:withCompletion: + + @param token Optionally; an access token if the request was successful. + @param error Optionally; the error which occurred - or nil if the request was successful. + + @remarks One of: @c token or @c error will always be non-nil. + */ +typedef void (^FIRAuthTokenCallback)(NSString *_Nullable token, NSError *_Nullable error) + FIR_SWIFT_NAME(AuthTokenCallback); + +/** @typedef FIRUserProfileChangeCallback + @brief The type of block called when a user profile change has finished. + + @param error Optionally; the error which occurred - or nil if the request was successful. + */ +typedef void (^FIRUserProfileChangeCallback)(NSError *_Nullable error) + FIR_SWIFT_NAME(UserProfileChangeCallback); + +/** @typedef FIRSendEmailVerificationCallback + @brief The type of block called when a request to send an email verification has finished. + + @param error Optionally; the error which occurred - or nil if the request was successful. + */ +typedef void (^FIRSendEmailVerificationCallback)(NSError *_Nullable error) + FIR_SWIFT_NAME(SendEmailVerificationCallback); + +/** @class FIRUser + @brief Represents a user. + @remarks This class is thread-safe. + */ +FIR_SWIFT_NAME(User) +@interface FIRUser : NSObject <FIRUserInfo> + +/** @property anonymous + @brief Indicates the user represents an anonymous user. + */ +@property(nonatomic, readonly, getter=isAnonymous) BOOL anonymous; + +/** @property emailVerified + @brief Indicates the email address associated with this user has been verified. + */ +@property(nonatomic, readonly, getter=isEmailVerified) BOOL emailVerified; + +/** @property refreshToken + @brief A refresh token; useful for obtaining new access tokens independently. + @remarks This property should only be used for advanced scenarios, and is not typically needed. + */ +@property(nonatomic, readonly, nullable) NSString *refreshToken; + +/** @property providerData + @brief Profile data for each identity provider, if any. + @remarks This data is cached on sign-in and updated when linking or unlinking. + */ +@property(nonatomic, readonly, nonnull) NSArray<id<FIRUserInfo>> *providerData; + +/** @fn init + @brief This class should not be instantiated. + @remarks To retrieve the current user, use @c FIRAuth.currentUser. To sign a user + in or out, use the methods on @c FIRAuth. + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @fn updateEmail:completion: + @brief Updates the email address for the user. On success, the cached user profile data is + updated. + @remarks May fail if there is already an account with this email address that was created using + email and password authentication. + + @param email The email address for the user. + @param completion Optionally; the block invoked when the user profile change has finished. + Invoked asynchronously on the main thread in the future. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeInvalidRecipientEmail - Indicates an invalid recipient email was + sent in the request. + </li> + <li>@c FIRAuthErrorCodeInvalidSender - Indicates an invalid sender email is set in + the console for this action. + </li> + <li>@c FIRAuthErrorCodeInvalidMessagePayload - Indicates an invalid email template for + sending update email. + </li> + <li>@c FIRAuthErrorCodeEmailAlreadyInUse - Indicates the email is already in use by another + account. + </li> + <li>@c FIRAuthErrorCodeInvalidEmail - Indicates the email address is malformed. + </li> + <li>@c FIRAuthErrorCodeRequiresRecentLogin - Updating a user’s email is a security + sensitive operation that requires a recent login from the user. This error indicates + the user has not signed in recently enough. To resolve, reauthenticate the user by + invoking reauthenticateWithCredential:completion: on FIRUser. + </li> + </ul> + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all FIRUser methods. + */ +- (void)updateEmail:(NSString *)email completion:(nullable FIRUserProfileChangeCallback)completion + FIR_SWIFT_NAME(updateEmail(to:completion:)); + +/** @fn updatePassword:completion: + @brief Updates the password for the user. On success, the cached user profile data is updated. + + @param password The new password for the user. + @param completion Optionally; the block invoked when the user profile change has finished. + Invoked asynchronously on the main thread in the future. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeOperationNotAllowed - Indicates the administrator disabled + sign in with the specified identity provider. + </li> + <li>@c FIRAuthErrorCodeRequiresRecentLogin - Updating a user’s password is a security + sensitive operation that requires a recent login from the user. This error indicates + the user has not signed in recently enough. To resolve, reauthenticate the user by + invoking reauthenticateWithCredential:completion: on FIRUser. + </li> + <li>@c FIRAuthErrorCodeWeakPassword - Indicates an attempt to set a password that is + considered too weak. The NSLocalizedFailureReasonErrorKey field in the NSError.userInfo + dictionary object will contain more detailed explanation that can be shown to the user. + </li> + </ul> + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all FIRUser methods. + */ +- (void)updatePassword:(NSString *)password + completion:(nullable FIRUserProfileChangeCallback)completion + FIR_SWIFT_NAME(updatePassword(to:completion:)); + +/** @fn updatePhoneNumberCredential:completion: + @brief Updates the phone number for the user. On success, the cached user profile data is + updated. + + @param phoneNumberCredential The new phone number credential corresponding to the phone number + to be added to the firebaes account, if a phone number is already linked to the account this + new phone number will replace it. + @param completion Optionally; the block invoked when the user profile change has finished. + Invoked asynchronously on the main thread in the future. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeRequiresRecentLogin - Updating a user’s phone number is a security + sensitive operation that requires a recent login from the user. This error indicates + the user has not signed in recently enough. To resolve, reauthenticate the user by + invoking reauthenticateWithCredential:completion: on FIRUser. + </li> + </ul> + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all FIRUser methods. + */ +- (void)updatePhoneNumberCredential:(FIRPhoneAuthCredential *)phoneNumberCredential + completion:(nullable FIRUserProfileChangeCallback)completion; + +/** @fn profileChangeRequest + @brief Creates an object which may be used to change the user's profile data. + + @remarks Set the properties of the returned object, then call + @c FIRUserProfileChangeRequest.commitChangesWithCallback: to perform the updates atomically. + + @return An object which may be used to change the user's profile data atomically. + */ +- (FIRUserProfileChangeRequest *)profileChangeRequest FIR_SWIFT_NAME(createProfileChangeRequest()); + +/** @fn reloadWithCompletion: + @brief Reloads the user's profile data from the server. + + @param completion Optionally; the block invoked when the reload has finished. Invoked + asynchronously on the main thread in the future. + + @remarks May fail with a @c FIRAuthErrorCodeRequiresRecentLogin error code. In this case + you should call @c FIRUser.reauthenticateWithCredential:completion: before re-invoking + @c FIRUser.updateEmail:completion:. + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods. + */ +- (void)reloadWithCompletion:(nullable FIRUserProfileChangeCallback)completion; + +/** @fn reauthenticateWithCredential:completion: + @brief Convenience method for @c reauthenticateAndRetrieveDataWithCredential:completion: This + method doesn't return additional identity provider data. + */ +- (void)reauthenticateWithCredential:(FIRAuthCredential *)credential + completion:(nullable FIRUserProfileChangeCallback)completion; + +/** @fn reauthenticateWithCredential:completion: + @brief Renews the user's authentication tokens by validating a fresh set of credentials supplied + by the user and returns additional identity provider data. + + @param credential A user-supplied credential, which will be validated by the server. This can be + a successful third-party identity provider sign-in, or an email address and password. + @param completion Optionally; the block invoked when the re-authentication operation has + finished. Invoked asynchronously on the main thread in the future. + + @remarks If the user associated with the supplied credential is different from the current user, + or if the validation of the supplied credentials fails; an error is returned and the current + user remains signed in. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeInvalidCredential - Indicates the supplied credential is invalid. + This could happen if it has expired or it is malformed. + </li> + <li>@c FIRAuthErrorCodeOperationNotAllowed - Indicates that accounts with the + identity provider represented by the credential are not enabled. Enable them in the + Auth section of the Firebase console. + </li> + <li>@c FIRAuthErrorCodeEmailAlreadyInUse - Indicates the email asserted by the credential + (e.g. the email in a Facebook access token) is already in use by an existing account, + that cannot be authenticated with this method. Call fetchProvidersForEmail for + this user’s email and then prompt them to sign in with any of the sign-in providers + returned. This error will only be thrown if the "One account per email address" + setting is enabled in the Firebase console, under Auth settings. Please note that the + error code raised in this specific situation may not be the same on Web and Android. + </li> + <li>@c FIRAuthErrorCodeUserDisabled - Indicates the user's account is disabled. + </li> + <li>@c FIRAuthErrorCodeWrongPassword - Indicates the user attempted reauthentication with + an incorrect password, if credential is of the type EmailPasswordAuthCredential. + </li> + <li>@c FIRAuthErrorCodeUserMismatch - Indicates that an attempt was made to + reauthenticate with a user which is not the current user. + </li> + <li>@c FIRAuthErrorCodeInvalidEmail - Indicates the email address is malformed.</li> + </ul> + @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods. + */ +- (void)reauthenticateAndRetrieveDataWithCredential:(FIRAuthCredential *) credential + completion:(nullable FIRAuthDataResultCallback) completion; + +/** @fn getIDTokenWithCompletion: + @brief Retrieves the Firebase authentication token, possibly refreshing it if it has expired. + + @param completion Optionally; the block invoked when the token is available. Invoked + asynchronously on the main thread in the future. + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods. + */ +- (void)getIDTokenWithCompletion:(nullable FIRAuthTokenCallback)completion + FIR_SWIFT_NAME(getIDToken(completion:)); + +/** @fn getTokenWithCompletion: + @brief Please use @c getIDTokenWithCompletion: instead. + + @param completion Optionally; the block invoked when the token is available. Invoked + asynchronously on the main thread in the future. + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods. + */ +- (void)getTokenWithCompletion:(nullable FIRAuthTokenCallback)completion + FIR_SWIFT_NAME(getToken(completion:)) __attribute__((deprecated)); + +/** @fn getIDTokenForcingRefresh:completion: + @brief Retrieves the Firebase authentication token, possibly refreshing it if it has expired. + + @param forceRefresh Forces a token refresh. Useful if the token becomes invalid for some reason + other than an expiration. + @param completion Optionally; the block invoked when the token is available. Invoked + asynchronously on the main thread in the future. + + @remarks The authentication token will be refreshed (by making a network request) if it has + expired, or if @c forceRefresh is YES. + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods. + */ +- (void)getIDTokenForcingRefresh:(BOOL)forceRefresh + completion:(nullable FIRAuthTokenCallback)completion; + +/** @fn getTokenForcingRefresh:completion: + @brief Please use getIDTokenForcingRefresh:completion instead. + + @param forceRefresh Forces a token refresh. Useful if the token becomes invalid for some reason + other than an expiration. + @param completion Optionally; the block invoked when the token is available. Invoked + asynchronously on the main thread in the future. + + @remarks The authentication token will be refreshed (by making a network request) if it has + expired, or if @c forceRefresh is YES. + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all API methods. + */ +- (void)getTokenForcingRefresh:(BOOL)forceRefresh + completion:(nullable FIRAuthTokenCallback)completion + __attribute__((deprecated)); + +/** @fn linkWithCredential:completion: + @brief Convenience method for @c linkAndRetrieveDataWithCredential:completion: This method + doesn't return additional identity provider data. + */ +- (void)linkWithCredential:(FIRAuthCredential *)credential + completion:(nullable FIRAuthResultCallback)completion; + +/** @fn linkAndRetrieveDataWithCredential:completion: + @brief Associates a user account from a third-party identity provider with this user and + returns additional identity provider data. + + @param credential The credential for the identity provider. + @param completion Optionally; the block invoked when the unlinking is complete, or fails. + Invoked asynchronously on the main thread in the future. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeProviderAlreadyLinked - Indicates an attempt to link a provider of a + type already linked to this account. + </li> + <li>@c FIRAuthErrorCodeCredentialAlreadyInUse - Indicates an attempt to link with a + credential + that has already been linked with a different Firebase account. + </li> + <li>@c FIRAuthErrorCodeOperationNotAllowed - Indicates that accounts with the identity + provider represented by the credential are not enabled. Enable them in the Auth section + of the Firebase console. + </li> + </ul> + + @remarks This method may also return error codes associated with updateEmail:completion: and + updatePassword:completion: on FIRUser. + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all FIRUser methods. + */ +- (void)linkAndRetrieveDataWithCredential:(FIRAuthCredential *) credential + completion:(nullable FIRAuthDataResultCallback) completion; + +/** @fn unlinkFromProvider:completion: + @brief Disassociates a user account from a third-party identity provider with this user. + + @param provider The provider ID of the provider to unlink. + @param completion Optionally; the block invoked when the unlinking is complete, or fails. + Invoked asynchronously on the main thread in the future. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeNoSuchProvider - Indicates an attempt to unlink a provider + that is not linked to the account. + </li> + <li>@c FIRAuthErrorCodeRequiresRecentLogin - Updating email is a security sensitive + operation that requires a recent login from the user. This error indicates the user + has not signed in recently enough. To resolve, reauthenticate the user by invoking + reauthenticateWithCredential:completion: on FIRUser. + </li> + </ul> + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all FIRUser methods. + */ +- (void)unlinkFromProvider:(NSString *)provider + completion:(nullable FIRAuthResultCallback)completion; + +/** @fn sendEmailVerificationWithCompletion: + @brief Initiates email verification for the user. + + @param completion Optionally; the block invoked when the request to send an email verification + is complete, or fails. Invoked asynchronously on the main thread in the future. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeInvalidRecipientEmail - Indicates an invalid recipient email was + sent in the request. + </li> + <li>@c FIRAuthErrorCodeInvalidSender - Indicates an invalid sender email is set in + the console for this action. + </li> + <li>@c FIRAuthErrorCodeInvalidMessagePayload - Indicates an invalid email template for + sending update email. + </li> + <li>@c FIRAuthErrorCodeUserNotFound - Indicates the user account was not found.</li> + </ul> + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all FIRUser methods. + */ +- (void)sendEmailVerificationWithCompletion:(nullable FIRSendEmailVerificationCallback)completion; + +/** @fn deleteWithCompletion: + @brief Deletes the user account (also signs out the user, if this was the current user). + + @param completion Optionally; the block invoked when the request to delete the account is + complete, or fails. Invoked asynchronously on the main thread in the future. + + @remarks Possible error codes: + <ul> + <li>@c FIRAuthErrorCodeRequiresRecentLogin - Updating email is a security sensitive + operation that requires a recent login from the user. This error indicates the user + has not signed in recently enough. To resolve, reauthenticate the user by invoking + reauthenticateWithCredential:completion: on FIRUser. + </li> + </ul> + + @remarks See @c FIRAuthErrors for a list of error codes that are common to all FIRUser methods. + + */ +- (void)deleteWithCompletion:(nullable FIRUserProfileChangeCallback)completion; + +@end + +/** @class FIRUserProfileChangeRequest + @brief Represents an object capable of updating a user's profile data. + @remarks Properties are marked as being part of a profile update when they are set. Setting a + property value to nil is not the same as leaving the property unassigned. + */ +FIR_SWIFT_NAME(UserProfileChangeRequest) +@interface FIRUserProfileChangeRequest : NSObject + +/** @fn init + @brief Please use @c FIRUser.profileChangeRequest + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @property displayName + @brief The user's display name. + @remarks It is an error to set this property after calling + @c FIRUserProfileChangeRequest.commitChangesWithCallback: + */ +@property(nonatomic, copy, nullable) NSString *displayName; + +/** @property photoURL + @brief The user's photo URL. + @remarks It is an error to set this property after calling + @c FIRUserProfileChangeRequest.commitChangesWithCallback: + */ +@property(nonatomic, copy, nullable) NSURL *photoURL; + +/** @fn commitChangesWithCompletion: + @brief Commits any pending changes. + @remarks This method should only be called once. Once called, property values should not be + changed. + + @param completion Optionally; the block invoked when the user profile change has been applied. + Invoked asynchronously on the main thread in the future. + */ +- (void)commitChangesWithCompletion:(nullable FIRUserProfileChangeCallback)completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRUser.m b/Firebase/Auth/Source/FIRUser.m new file mode 100644 index 0000000..f0c3226 --- /dev/null +++ b/Firebase/Auth/Source/FIRUser.m @@ -0,0 +1,1170 @@ +/* + * 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. + */ + +#import "Private/FIRUser_Internal.h" + +#import "AuthProviders/EmailPassword/FIREmailPasswordAuthCredential.h" +#import "AuthProviders/EmailPassword/FIREmailAuthProvider.h" +#import "AuthProviders/Phone/FIRPhoneAuthCredential_Internal.h" +#import "AuthProviders/Phone/FIRPhoneAuthProvider.h" +#import "Private/FIRAdditionalUserInfo_Internal.h" +#import "FIRAuth.h" +#import "Private/FIRAuthCredential_Internal.h" +#import "Private/FIRAuthDataResult_Internal.h" +#import "Private/FIRAuthErrorUtils.h" +#import "Private/FIRAuthGlobalWorkQueue.h" +#import "Private/FIRAuthSerialTaskQueue.h" +#import "Private/FIRAuth_Internal.h" +#import "FIRSecureTokenService.h" +#import "FIRUserInfoImpl.h" +#import "FIRAuthBackend.h" +#import "FIRDeleteAccountRequest.h" +#import "FIRDeleteAccountResponse.h" +#import "FIRGetAccountInfoRequest.h" +#import "FIRGetAccountInfoResponse.h" +#import "FIRGetOOBConfirmationCodeRequest.h" +#import "FIRGetOOBConfirmationCodeResponse.h" +#import "FIRSetAccountInfoRequest.h" +#import "FIRSetAccountInfoResponse.h" +#import "FIRVerifyAssertionRequest.h" +#import "FIRVerifyAssertionResponse.h" +#import "FIRVerifyCustomTokenRequest.h" +#import "FIRVerifyCustomTokenResponse.h" +#import "FIRVerifyPasswordRequest.h" +#import "FIRVerifyPasswordResponse.h" +#import "FIRVerifyPhoneNumberRequest.h" +#import "FIRVerifyPhoneNumberResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @var kUserIDCodingKey + @brief The key used to encode the user ID for NSSecureCoding. + */ +static NSString *const kUserIDCodingKey = @"userID"; + +/** @var kHasEmailPasswordCredentialCodingKey + @brief The key used to encode the hasEmailPasswordCredential property for NSSecureCoding. + */ +static NSString *const kHasEmailPasswordCredentialCodingKey = @"hasEmailPassword"; + +/** @var kAnonymousCodingKey + @brief The key used to encode the anonymous property for NSSecureCoding. + */ +static NSString *const kAnonymousCodingKey = @"anonymous"; + +/** @var kEmailCodingKey + @brief The key used to encode the email property for NSSecureCoding. + */ +static NSString *const kEmailCodingKey = @"email"; + +/** @var kEmailVerifiedCodingKey + @brief The key used to encode the isEmailVerified property for NSSecureCoding. + */ +static NSString *const kEmailVerifiedCodingKey = @"emailVerified"; + +/** @var kDisplayNameCodingKey + @brief The key used to encode the displayName property for NSSecureCoding. + */ +static NSString *const kDisplayNameCodingKey = @"displayName"; + +/** @var kPhotoURLCodingKey + @brief The key used to encode the photoURL property for NSSecureCoding. + */ +static NSString *const kPhotoURLCodingKey = @"photoURL"; + +/** @var kProviderDataKey + @brief The key used to encode the providerData instance variable for NSSecureCoding. + */ +static NSString *const kProviderDataKey = @"providerData"; + +/** @var kAPIKeyCodingKey + @brief The key used to encode the APIKey instance variable for NSSecureCoding. + */ +static NSString *const kAPIKeyCodingKey = @"APIKey"; + +/** @var kTokenServiceCodingKey + @brief The key used to encode the tokenService instance variable for NSSecureCoding. + */ +static NSString *const kTokenServiceCodingKey = @"tokenService"; + +/** @var kMissingUsersErrorMessage + @brief The error message when there is no users array in the getAccountInfo response. + */ +static NSString *const kMissingUsersErrorMessage = @"users"; + +/** @typedef CallbackWithError + @brief The type for a callback block that only takes an error parameter. + */ +typedef void (^CallbackWithError)(NSError *_Nullable); + +/** @typedef CallbackWithUserAndError + @brief The type for a callback block that takes a user parameter and an error parameter. + */ +typedef void (^CallbackWithUserAndError)(FIRUser *_Nullable, NSError *_Nullable); + +/** @typedef CallbackWithUserAndError + @brief The type for a callback block that takes a user parameter and an error parameter. + */ +typedef void (^CallbackWithAuthDataResultAndError)(FIRAuthDataResult *_Nullable, + NSError *_Nullable); + +/** @var kMissingPasswordReason + @brief The reason why the @c FIRAuthErrorCodeWeakPassword error is thrown. + @remarks This error message will be localized in the future. + */ +static NSString *const kMissingPasswordReason = @"Missing Password"; + +/** @fn callInMainThreadWithError + @brief Calls a callback in main thread with error. + @param callback The callback to be called in main thread. + @param error The error to pass to callback. + */ +static void callInMainThreadWithError(_Nullable CallbackWithError callback, + NSError *_Nullable error) { + if (callback) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(error); + }); + } +} + +/** @fn callInMainThreadWithUserAndError + @brief Calls a callback in main thread with user and error. + @param callback The callback to be called in main thread. + @param user The user to pass to callback if there is no error. + @param error The error to pass to callback. + */ +static void callInMainThreadWithUserAndError(_Nullable CallbackWithUserAndError callback, + FIRUser *_Nonnull user, + NSError *_Nullable error) { + if (callback) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(error ? nil : user, error); + }); + } +} + +/** @fn callInMainThreadWithUserAndError + @brief Calls a callback in main thread with user and error. + @param callback The callback to be called in main thread. + @param result The result to pass to callback if there is no error. + @param error The error to pass to callback. + */ +static void callInMainThreadWithAuthDataResultAndError( + _Nullable CallbackWithAuthDataResultAndError callback, + FIRAuthDataResult *_Nullable result, + NSError *_Nullable error) { + if (callback) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(result, error); + }); + } +} + +@interface FIRUserProfileChangeRequest () + +/** @fn initWithUser: + @brief Designated initializer. + @param user The user for which we are updating profile information. + */ +- (nullable instancetype)initWithUser:(FIRUser *)user NS_DESIGNATED_INITIALIZER; + +@end + +@interface FIRUser () + +/** @fn initWithAPIKey: + @brief Designated initializer + @param APIKey The client API key for making RPCs. + */ +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey NS_DESIGNATED_INITIALIZER; + +@end + +@implementation FIRUser { + /** @var _hasEmailPasswordCredential + @brief Whether or not the user can be authenticated by using Firebase email and password. + */ + BOOL _hasEmailPasswordCredential; + + /** @var _providerData + @brief Provider specific user data. + */ + NSDictionary<NSString *, FIRUserInfoImpl *> *_providerData; + + /** @var _APIKey + @brief The application's API Key. + */ + NSString *_APIKey; + + /** @var _taskQueue + @brief Used to serialize the update profile calls. + */ + FIRAuthSerialTaskQueue *_taskQueue; + + /** @var _tokenService + @brief A secure token service associated with this user. For performing token exchanges and + refreshing access tokens. + */ + FIRSecureTokenService *_tokenService; +} + +#pragma mark - Properties + +// Explicitly @synthesize because these properties are defined in FIRUserInfo protocol. +@synthesize uid = _userID; +@synthesize displayName = _displayName; +@synthesize photoURL = _photoURL; +@synthesize email = _email; +@synthesize phoneNumber = _phoneNumber; + +#pragma mark - + ++ (void)retrieveUserWithAPIKey:(NSString *)APIKey + accessToken:(NSString *)accessToken + accessTokenExpirationDate:(NSDate *)accessTokenExpirationDate + refreshToken:(NSString *)refreshToken + anonymous:(BOOL)anonymous + callback:(FIRRetrieveUserCallback)callback { + FIRSecureTokenService *tokenService = + [[FIRSecureTokenService alloc] initWithAPIKey:APIKey + accessToken:accessToken + accessTokenExpirationDate:accessTokenExpirationDate + refreshToken:refreshToken]; + FIRUser *user = [[self alloc] initWithAPIKey:APIKey + tokenService:tokenService]; + [user internalGetTokenWithCallback:^(NSString *_Nullable accessToken, NSError *_Nullable error) { + if (error) { + callback(nil, error); + return; + } + FIRGetAccountInfoRequest *getAccountInfoRequest = + [[FIRGetAccountInfoRequest alloc] initWithAPIKey:APIKey accessToken:accessToken]; + [FIRAuthBackend getAccountInfo:getAccountInfoRequest + callback:^(FIRGetAccountInfoResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + callback(nil, error); + return; + } + user->_anonymous = anonymous; + [user updateWithGetAccountInfoResponse:response]; + callback(user, nil); + }]; + }]; +} + +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey { + self = [super init]; + if (self) { + _APIKey = [APIKey copy]; + _providerData = @{ }; + _taskQueue = [[FIRAuthSerialTaskQueue alloc] init]; + } + return self; +} + +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey + tokenService:(FIRSecureTokenService *)tokenService { + self = [self initWithAPIKey:APIKey]; + if (self) { + _tokenService = tokenService; + } + return self; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { + NSString *userID = [aDecoder decodeObjectOfClass:[NSString class] forKey:kUserIDCodingKey]; + BOOL hasAnonymousKey = [aDecoder containsValueForKey:kAnonymousCodingKey]; + BOOL anonymous = [aDecoder decodeBoolForKey:kAnonymousCodingKey]; + BOOL hasEmailPasswordCredential = + [aDecoder decodeBoolForKey:kHasEmailPasswordCredentialCodingKey]; + NSString *displayName = + [aDecoder decodeObjectOfClass:[NSString class] forKey:kDisplayNameCodingKey]; + NSURL *photoURL = + [aDecoder decodeObjectOfClass:[NSURL class] forKey:kPhotoURLCodingKey]; + NSString *email = + [aDecoder decodeObjectOfClass:[NSString class] forKey:kEmailCodingKey]; + BOOL emailVerified = [aDecoder decodeBoolForKey:kEmailVerifiedCodingKey]; + NSSet *providerDataClasses = [NSSet setWithArray:@[ + [NSDictionary class], + [NSString class], + [FIRUserInfoImpl class] + ]]; + NSDictionary<NSString *, FIRUserInfoImpl *> *providerData = + [aDecoder decodeObjectOfClasses:providerDataClasses forKey:kProviderDataKey]; + NSString *APIKey = + [aDecoder decodeObjectOfClass:[NSString class] forKey:kAPIKeyCodingKey]; + FIRSecureTokenService *tokenService = + [aDecoder decodeObjectOfClass:[FIRSecureTokenService class] forKey:kTokenServiceCodingKey]; + if (!userID || !APIKey || !tokenService) { + return nil; + } + self = [self initWithAPIKey:APIKey]; + if (self) { + _tokenService = tokenService; + _userID = userID; + // Previous version of this code didn't save 'anonymous' bit directly but deduced it from + // 'hasEmailPasswordCredential' and 'providerData' instead, so here backward compatibility is + // provided to read old format data. + _anonymous = hasAnonymousKey ? anonymous : (!hasEmailPasswordCredential && !providerData.count); + _hasEmailPasswordCredential = hasEmailPasswordCredential; + _email = email; + _emailVerified = emailVerified; + _displayName = displayName; + _photoURL = photoURL; + _providerData = providerData; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_userID forKey:kUserIDCodingKey]; + [aCoder encodeBool:_anonymous forKey:kAnonymousCodingKey]; + [aCoder encodeBool:_hasEmailPasswordCredential forKey:kHasEmailPasswordCredentialCodingKey]; + [aCoder encodeObject:_providerData forKey:kProviderDataKey]; + [aCoder encodeObject:_email forKey:kEmailCodingKey]; + [aCoder encodeBool:_emailVerified forKey:kEmailVerifiedCodingKey]; + [aCoder encodeObject:_photoURL forKey:kPhotoURLCodingKey]; + [aCoder encodeObject:_displayName forKey:kDisplayNameCodingKey]; + [aCoder encodeObject:_APIKey forKey:kAPIKeyCodingKey]; + [aCoder encodeObject:_tokenService forKey:kTokenServiceCodingKey]; +} + +#pragma mark - + +- (NSString *)providerID { + return @"Firebase"; +} + +- (NSArray<id<FIRUserInfo>> *)providerData { + return _providerData.allValues; +} + +/** @fn getAccountInfoRefreshingCache: + @brief Gets the users's account data from the server, updating our local values. + @param callback Invoked when the request to getAccountInfo has completed, or when an error has + been detected. Invoked asynchronously on the auth global work queue in the future. + */ +- (void)getAccountInfoRefreshingCache:(void(^)(FIRGetAccountInfoResponseUser *_Nullable user, + NSError *_Nullable error))callback { + [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken, NSError *_Nullable error) { + if (error) { + callback(nil, error); + return; + } + FIRGetAccountInfoRequest *getAccountInfoRequest = + [[FIRGetAccountInfoRequest alloc] initWithAPIKey:_APIKey accessToken:accessToken]; + [FIRAuthBackend getAccountInfo:getAccountInfoRequest + callback:^(FIRGetAccountInfoResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + callback(nil, error); + return; + } + [self updateWithGetAccountInfoResponse:response]; + if (![self updateKeychain:&error]) { + callback(nil, error); + return; + } + callback(response.users.firstObject, nil); + }]; + }]; +} + +- (void)updateWithGetAccountInfoResponse:(FIRGetAccountInfoResponse *)response { + FIRGetAccountInfoResponseUser *user = response.users.firstObject; + _userID = user.localID; + _email = user.email; + _emailVerified = user.emailVerified; + _displayName = user.displayName; + _photoURL = user.photoURL; + _phoneNumber = user.phoneNumber; + _hasEmailPasswordCredential = user.passwordHash.length > 0; + + NSMutableDictionary<NSString *, FIRUserInfoImpl *> *providerData = + [NSMutableDictionary dictionary]; + for (FIRGetAccountInfoResponseProviderUserInfo *providerUserInfo in user.providerUserInfo) { + FIRUserInfoImpl *userInfo = + [FIRUserInfoImpl userInfoWithGetAccountInfoResponseProviderUserInfo:providerUserInfo]; + if (userInfo) { + providerData[providerUserInfo.providerID] = userInfo; + } + } + _providerData = [providerData copy]; +} + +/** @fn executeUserUpdateWithChanges:callback: + @brief Performs a setAccountInfo request by mutating the results of a getAccountInfo response, + atomically in regards to other calls to this method. + @param changeBlock A block responsible for mutating a template @c FIRSetAccountInfoRequest + @param callback A block to invoke when the change is complete. Invoked asynchronously on the + auth global work queue in the future. + */ +- (void)executeUserUpdateWithChanges:(void(^)(FIRGetAccountInfoResponseUser *, + FIRSetAccountInfoRequest *))changeBlock + callback:(nonnull FIRUserProfileChangeCallback)callback { + [_taskQueue enqueueTask:^(FIRAuthSerialTaskCompletionBlock _Nonnull complete) { + [self getAccountInfoRefreshingCache:^(FIRGetAccountInfoResponseUser *_Nullable user, + NSError *_Nullable error) { + if (error) { + complete(); + callback(error); + return; + } + [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken, + NSError *_Nullable error) { + if (error) { + complete(); + callback(error); + return; + } + // Mutate setAccountInfoRequest in block: + FIRSetAccountInfoRequest *setAccountInfoRequest = + [[FIRSetAccountInfoRequest alloc] initWithAPIKey:_APIKey]; + setAccountInfoRequest.accessToken = accessToken; + changeBlock(user, setAccountInfoRequest); + // Execute request: + [FIRAuthBackend setAccountInfo:setAccountInfoRequest + callback:^(FIRSetAccountInfoResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + complete(); + callback(error); + return; + } + if (response.IDToken && response.refreshToken) { + FIRSecureTokenService *tokenService = + [[FIRSecureTokenService alloc] initWithAPIKey:_APIKey + accessToken:response.IDToken + accessTokenExpirationDate:response.approximateExpirationDate + refreshToken:response.refreshToken]; + [self setTokenService:tokenService callback:^(NSError *_Nullable error) { + complete(); + callback(error); + }]; + return; + } + complete(); + callback(nil); + }]; + }]; + }]; + }]; +} + +/** @fn updateKeychain: + @brief Updates the keychain for user token or info changes. + @param error The error if NO is returned. + @return Wether the operation is successful. + */ +- (BOOL)updateKeychain:(NSError *_Nullable *_Nullable)error { + return !_auth || [_auth updateKeychainWithUser:self error:error]; +} + +/** @fn setTokenService:callback: + @brief Sets a new token service for the @c FIRUser instance. + @param tokenService The new token service object. + @param callback The block to be called in the global auth working queue once finished. + @remarks The method makes sure the token service has access and refresh token and the new tokens + are saved in the keychain before calling back. + */ +- (void)setTokenService:(FIRSecureTokenService *)tokenService + callback:(nonnull CallbackWithError)callback { + [tokenService fetchAccessTokenForcingRefresh:NO + callback:^(NSString *_Nullable token, + NSError *_Nullable error, + BOOL tokenUpdated) { + if (error) { + callback(error); + return; + } + _tokenService = tokenService; + if (![self updateKeychain:&error]) { + callback(error); + return; + } + [_auth notifyListenersOfAuthStateChangeWithUser:self token:token]; + callback(nil); + }]; +} + +#pragma mark - + +/** @fn updateEmail:password:callback: + @brief Updates email address and/or password for the current user. + @remarks May fail if there is already an email/password-based account for the same email + address. + @param email The email address for the user, if to be updated. + @param password The new password for the user, if to be updated. + @param callback The block called when the user profile change has finished. Invoked + asynchronously on the auth global work queue in the future. + @remarks May fail with a @c FIRAuthErrorCodeRequiresRecentLogin error code. + Call @c reauthentateWithCredential:completion: beforehand to avoid this error case. + */ +- (void)updateEmail:(nullable NSString *)email + password:(nullable NSString *)password + callback:(nonnull FIRUserProfileChangeCallback)callback { + if (password && ![password length]){ + callback([FIRAuthErrorUtils weakPasswordErrorWithServerResponseReason:kMissingPasswordReason]); + return; + } + BOOL hadEmailPasswordCredential = _hasEmailPasswordCredential; + [self executeUserUpdateWithChanges:^(FIRGetAccountInfoResponseUser *user, + FIRSetAccountInfoRequest *request) { + if (email) { + request.email = email; + } + if (password) { + request.password = password; + } + } + callback:^(NSError *error) { + if (error) { + callback(error); + return; + } + if (email) { + _email = email; + } + if (_email && password) { + _anonymous = NO; + _hasEmailPasswordCredential = YES; + if (!hadEmailPasswordCredential) { + // The list of providers need to be updated for the newly added email-password provider. + [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken, + NSError *_Nullable error) { + if (error) { + callback(error); + return; + } + FIRGetAccountInfoRequest *getAccountInfoRequest = + [[FIRGetAccountInfoRequest alloc] initWithAPIKey:_APIKey accessToken:accessToken]; + [FIRAuthBackend getAccountInfo:getAccountInfoRequest + callback:^(FIRGetAccountInfoResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + callback(error); + return; + } + [self updateWithGetAccountInfoResponse:response]; + if (![self updateKeychain:&error]) { + callback(error); + return; + } + callback(nil); + }]; + }]; + return; + } + } + if (![self updateKeychain:&error]) { + callback(error); + return; + } + callback(nil); + }]; +} + +- (void)updateEmail:(NSString *)email completion:(nullable FIRUserProfileChangeCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + [self updateEmail:email password:nil callback:^(NSError *_Nullable error) { + callInMainThreadWithError(completion, error); + }]; + }); +} + +- (void)updatePassword:(NSString *)password + completion:(nullable FIRUserProfileChangeCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + [self updateEmail:nil password:password callback:^(NSError *_Nullable error){ + callInMainThreadWithError(completion, error); + }]; + }); +} + +/** @fn internalUpdatePhoneNumberCredential:completion: + @brief Updates the phone number for the user. On success, the cached user profile data is + updated. + + @param phoneAuthCredential The new phone number credential corresponding to the phone number + to be added to the firebaes account, if a phone number is already linked to the account this + new phone number will replace it. + @param completion Optionally; the block invoked when the user profile change has finished. + Invoked asynchronously on the global work queue in the future. + */ +- (void)internalUpdatePhoneNumberCredential:(FIRPhoneAuthCredential *)phoneAuthCredential + completion:(FIRUserProfileChangeCallback)completion { + [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken, + NSError *_Nullable error) { + if (error) { + completion(error); + return; + } + FIRVerifyPhoneNumberRequest *request = [[FIRVerifyPhoneNumberRequest alloc] + initWithVerificationID:phoneAuthCredential.verificationID + verificationCode:phoneAuthCredential.verificationCode + APIKey:_APIKey]; + request.accessToken = accessToken; + [FIRAuthBackend verifyPhoneNumber:request + callback:^(FIRVerifyPhoneNumberResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + completion(error);; + return; + } + // Get account info to update cached user info. + [self getAccountInfoRefreshingCache:^(FIRGetAccountInfoResponseUser *_Nullable user, + NSError *_Nullable error) { + if (![self updateKeychain:&error]) { + completion(error); + return; + } + completion(nil); + }]; + }]; + }]; +} + +- (void)updatePhoneNumberCredential:(FIRPhoneAuthCredential *)phoneAuthCredential + completion:(nullable FIRUserProfileChangeCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + [self internalUpdatePhoneNumberCredential:phoneAuthCredential + completion:^(NSError *_Nullable error) { + callInMainThreadWithError(completion, error); + }]; + }); +} + +- (FIRUserProfileChangeRequest *)profileChangeRequest { + __block FIRUserProfileChangeRequest *result; + dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ + result = [[FIRUserProfileChangeRequest alloc] initWithUser:self]; + }); + return result; +} + +- (void)setDisplayName:(NSString *)displayName { + _displayName = [displayName copy]; +} + +- (void)setPhotoURL:(NSURL *)photoURL { + _photoURL = [photoURL copy]; +} + +- (NSString *)rawAccessToken { + return _tokenService.rawAccessToken; +} + +- (NSDate *)accessTokenExpirationDate { + return _tokenService.accessTokenExpirationDate; +} + +#pragma mark - + +- (void)reloadWithCompletion:(nullable FIRUserProfileChangeCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + [self getAccountInfoRefreshingCache:^(FIRGetAccountInfoResponseUser *_Nullable user, + NSError *_Nullable error) { + callInMainThreadWithError(completion, error); + }]; + }); +} + +#pragma mark - + +- (void)reauthenticateWithCredential:(FIRAuthCredential *)credential + completion:(nullable FIRUserProfileChangeCallback)completion { + FIRAuthDataResultCallback callback = ^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + completion(error); + }; + [self reauthenticateAndRetrieveDataWithCredential:credential completion:callback]; +} + +- (void) + reauthenticateAndRetrieveDataWithCredential:(FIRAuthCredential *) credential + completion:(nullable FIRAuthDataResultCallback) completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + [_auth internalSignInAndRetrieveDataWithCredential:credential + isReauthentication:YES + callback:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + if (error) { + // If "user not found" error returned by backend, translate to user mismatch error which is + // more accurate. + if (error.code == FIRAuthErrorCodeUserNotFound) { + error = [FIRAuthErrorUtils userMismatchError]; + } + callInMainThreadWithAuthDataResultAndError(completion, authResult, error); + return; + } + if (![authResult.user.uid isEqual:_auth.currentUser.uid]) { + callInMainThreadWithAuthDataResultAndError(completion, authResult, + [FIRAuthErrorUtils userMismatchError]); + return; + } + // Successful reauthenticate + [self setTokenService:authResult.user->_tokenService callback:^(NSError *_Nullable error) { + callInMainThreadWithAuthDataResultAndError(completion, authResult, error); + }]; + }]; + }); +} + +- (nullable NSString *)refreshToken { + __block NSString *result; + dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ + result = _tokenService.refreshToken; + }); + return result; +} + +- (void)getIDTokenWithCompletion:(nullable FIRAuthTokenCallback)completion { + // |getTokenForcingRefresh:completion:| is also a public API so there is no need to dispatch to + // global work queue here. + [self getIDTokenForcingRefresh:NO completion:completion]; +} + +- (void)getTokenWithCompletion:(nullable FIRAuthTokenCallback)completion { + [self getIDTokenWithCompletion:completion]; +} + +- (void)getIDTokenForcingRefresh:(BOOL)forceRefresh + completion:(nullable FIRAuthTokenCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + [self internalGetTokenForcingRefresh:forceRefresh + callback:^(NSString *_Nullable token, NSError *_Nullable error) { + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(token, error); + }); + } + }]; + }); +} + +- (void)getTokenForcingRefresh:(BOOL)forceRefresh + completion:(nullable FIRAuthTokenCallback)completion { + [self getIDTokenForcingRefresh:forceRefresh completion:completion]; +} + +/** @fn internalGetTokenForcingRefresh:callback: + @brief Retrieves the Firebase authentication token, possibly refreshing it if it has expired. + @param callback The block to invoke when the token is available. Invoked asynchronously on the + global work thread in the future. + */ +- (void)internalGetTokenWithCallback:(nonnull FIRAuthTokenCallback)callback { + [self internalGetTokenForcingRefresh:NO callback:callback]; +} + +- (void)internalGetTokenForcingRefresh:(BOOL)forceRefresh + callback:(nonnull FIRAuthTokenCallback)callback { + [_tokenService fetchAccessTokenForcingRefresh:forceRefresh + callback:^(NSString *_Nullable token, + NSError *_Nullable error, + BOOL tokenUpdated) { + if (error) { + callback(nil, error); + return; + } + if (tokenUpdated) { + if (![self updateKeychain:&error]) { + callback(nil, error); + return; + } + [_auth notifyListenersOfAuthStateChangeWithUser:self token:token]; + } + callback(token, nil); + }]; +} + +- (void)linkWithCredential:(FIRAuthCredential *)credential + completion:(nullable FIRAuthResultCallback)completion { + FIRAuthDataResultCallback callback = ^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + completion(authResult.user, error); + }; + [self linkAndRetrieveDataWithCredential:credential completion:callback]; +} + +- (void)linkAndRetrieveDataWithCredential:(FIRAuthCredential *)credential + completion:(nullable FIRAuthDataResultCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + if (_providerData[credential.provider]) { + callInMainThreadWithAuthDataResultAndError(completion, + nil, + [FIRAuthErrorUtils providerAlreadyLinkedError]); + return; + } + FIRAuthDataResult *result = + [[FIRAuthDataResult alloc] initWithUser:self additionalUserInfo:nil]; + if ([credential isKindOfClass:[FIREmailPasswordAuthCredential class]]) { + if (_hasEmailPasswordCredential) { + callInMainThreadWithAuthDataResultAndError(completion, + nil, + [FIRAuthErrorUtils providerAlreadyLinkedError]); + return; + } + FIREmailPasswordAuthCredential *emailPasswordCredential = + (FIREmailPasswordAuthCredential *)credential; + [self updateEmail:emailPasswordCredential.email + password:emailPasswordCredential.password + callback:^(NSError *error) { + if (error) { + callInMainThreadWithAuthDataResultAndError(completion, nil, error); + } else { + callInMainThreadWithAuthDataResultAndError(completion, result, nil); + } + }]; + return; + } + + if ([credential isKindOfClass:[FIRPhoneAuthCredential class]]) { + FIRPhoneAuthCredential *phoneAuthCredential = (FIRPhoneAuthCredential *)credential; + [self internalUpdatePhoneNumberCredential:phoneAuthCredential + completion:^(NSError *_Nullable error) { + if (error){ + callInMainThreadWithAuthDataResultAndError(completion, nil, error); + } else { + callInMainThreadWithAuthDataResultAndError(completion, result, nil); + } + }]; + return; + } + + [_taskQueue enqueueTask:^(FIRAuthSerialTaskCompletionBlock _Nonnull complete) { + CallbackWithAuthDataResultAndError completeWithError = + ^(FIRAuthDataResult *result, NSError *error) { + complete(); + callInMainThreadWithAuthDataResultAndError(completion, result, error); + }; + [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken, + NSError *_Nullable error) { + if (error) { + completeWithError(nil, error); + return; + } + FIRVerifyAssertionRequest *request = + [[FIRVerifyAssertionRequest alloc] initWithAPIKey:_APIKey providerID:credential.provider]; + [credential prepareVerifyAssertionRequest:request]; + request.accessToken = accessToken; + [FIRAuthBackend verifyAssertion:request + callback:^(FIRVerifyAssertionResponse *response, NSError *error) { + if (error) { + completeWithError(nil, error); + return; + } + FIRAdditionalUserInfo *additionalUserInfo = + [FIRAdditionalUserInfo userInfoWithVerifyAssertionResponse:response]; + FIRAuthDataResult *result = + [[FIRAuthDataResult alloc] initWithUser:self additionalUserInfo:additionalUserInfo]; + // Update the new token and refresh user info again. + _tokenService = + [[FIRSecureTokenService alloc] initWithAPIKey:_APIKey + accessToken:response.IDToken + accessTokenExpirationDate:response.approximateExpirationDate + refreshToken:response.refreshToken]; + [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken, + NSError *_Nullable error) { + if (error) { + completeWithError(nil, error); + return; + } + FIRGetAccountInfoRequest *getAccountInfoRequest = + [[FIRGetAccountInfoRequest alloc] initWithAPIKey:_APIKey accessToken:accessToken]; + [FIRAuthBackend getAccountInfo:getAccountInfoRequest + callback:^(FIRGetAccountInfoResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + completeWithError(nil, error); + return; + } + _anonymous = NO; + [self updateWithGetAccountInfoResponse:response]; + if (![self updateKeychain:&error]) { + completeWithError(nil, error); + return; + } + completeWithError(result, nil); + }]; + }]; + }]; + }]; + }]; + }); +} + +- (void)unlinkFromProvider:(NSString *)provider + completion:(nullable FIRAuthResultCallback)completion { + [_taskQueue enqueueTask:^(FIRAuthSerialTaskCompletionBlock _Nonnull complete) { + CallbackWithError completeAndCallbackWithError = ^(NSError *error) { + complete(); + callInMainThreadWithUserAndError(completion, self, error); + }; + [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken, + NSError *_Nullable error) { + if (error) { + completeAndCallbackWithError(error); + return; + } + FIRSetAccountInfoRequest *setAccountInfoRequest = + [[FIRSetAccountInfoRequest alloc] initWithAPIKey:_APIKey]; + setAccountInfoRequest.accessToken = accessToken; + BOOL isEmailPasswordProvider = [provider isEqualToString:FIREmailAuthProviderID]; + if (isEmailPasswordProvider) { + if (!_hasEmailPasswordCredential) { + completeAndCallbackWithError([FIRAuthErrorUtils noSuchProviderError]); + return; + } + setAccountInfoRequest.deleteAttributes = @[ FIRSetAccountInfoUserAttributePassword ]; + } else { + if (!_providerData[provider]) { + completeAndCallbackWithError([FIRAuthErrorUtils noSuchProviderError]); + return; + } + setAccountInfoRequest.deleteProviders = @[ provider ]; + } + [FIRAuthBackend setAccountInfo:setAccountInfoRequest + callback:^(FIRSetAccountInfoResponse *_Nullable response, + NSError *_Nullable error) { + if (error) { + completeAndCallbackWithError(error); + return; + } + if (isEmailPasswordProvider) { + _hasEmailPasswordCredential = NO; + } else { + // We can't just use the provider info objects in FIRSetAcccountInfoResponse because they + // don't have localID and email fields. Remove the specific provider manually. + NSMutableDictionary *mutableProviderData = [_providerData mutableCopy]; + [mutableProviderData removeObjectForKey:provider]; + _providerData = [mutableProviderData copy]; + + // After successfully unlinking a phone auth provider, remove the phone number from the + // cached user info. + if ([provider isEqualToString:FIRPhoneAuthProviderID]) { + _phoneNumber = nil; + } + } + if (response.IDToken && response.refreshToken) { + FIRSecureTokenService *tokenService = + [[FIRSecureTokenService alloc] initWithAPIKey:_APIKey + accessToken:response.IDToken + accessTokenExpirationDate:response.approximateExpirationDate + refreshToken:response.refreshToken]; + [self setTokenService:tokenService callback:^(NSError *_Nullable error) { + completeAndCallbackWithError(error); + }]; + return; + } + if (![self updateKeychain:&error]) { + completeAndCallbackWithError(error); + return; + } + completeAndCallbackWithError(nil); + }]; + }]; + }]; +} + +- (void)sendEmailVerificationWithCompletion:(nullable FIRSendEmailVerificationCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken, + NSError *_Nullable error) { + if (error) { + callInMainThreadWithError(completion, error); + return; + } + FIRGetOOBConfirmationCodeRequest *request = + [FIRGetOOBConfirmationCodeRequest verifyEmailRequestWithAccessToken:accessToken + APIKey:_APIKey]; + [FIRAuthBackend getOOBConfirmationCode:request + callback:^(FIRGetOOBConfirmationCodeResponse *_Nullable + response, + NSError *_Nullable error) { + callInMainThreadWithError(completion, error); + }]; + }]; + }); +} + +- (void)deleteWithCompletion:(nullable FIRUserProfileChangeCallback)completion { + dispatch_async(FIRAuthGlobalWorkQueue(), ^{ + [self internalGetTokenWithCallback:^(NSString *_Nullable accessToken, + NSError *_Nullable error) { + if (error) { + callInMainThreadWithError(completion, error); + return; + } + FIRDeleteAccountRequest *deleteUserRequest = + [[FIRDeleteAccountRequest alloc] initWithAPIKey:_APIKey + localID:_userID + accessToken:accessToken]; + [FIRAuthBackend deleteAccount:deleteUserRequest callback:^(NSError *_Nullable error) { + if (error) { + callInMainThreadWithError(completion, error); + return; + } + if (![[FIRAuth auth] signOutByForceWithUserID:_userID error:&error]) { + callInMainThreadWithError(completion, error); + return; + } + callInMainThreadWithError(completion, error); + }]; + }]; + }); +} + +@end + +@implementation FIRUserProfileChangeRequest { + /** @var _user + @brief The user associated with the change request. + */ + FIRUser *_user; + + /** @var _displayName + @brief The display name value to set if @c _displayNameSet is YES. + */ + NSString *_displayName; + + /** @var _displayNameSet + @brief Indicates the display name should be part of the change request. + */ + BOOL _displayNameSet; + + /** @var _photoURL + @brief The photo URL value to set if @c _displayNameSet is YES. + */ + NSURL *_photoURL; + + /** @var _photoURLSet + @brief Indicates the photo URL should be part of the change request. + */ + BOOL _photoURLSet; + + /** @var _consumed + @brief Indicates the @c commitChangesWithCallback: method has already been invoked. + */ + BOOL _consumed; +} + +- (nullable instancetype)initWithUser:(FIRUser *)user { + self = [super init]; + if (self) { + _user = user; + } + return self; +} + +- (nullable NSString *)displayName { + return _displayName; +} + +- (void)setDisplayName:(nullable NSString *)displayName { + dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ + if (_consumed) { + [NSException raise:NSInternalInconsistencyException + format:@"%@", + @"Invalid call to setDisplayName: after commitChangesWithCallback:."]; + return; + } + _displayNameSet = YES; + _displayName = [displayName copy]; + }); +} + +- (nullable NSURL *)photoURL { + return _photoURL; +} + +- (void)setPhotoURL:(nullable NSURL *)photoURL { + dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ + if (_consumed) { + [NSException raise:NSInternalInconsistencyException + format:@"%@", + @"Invalid call to setPhotoURL: after commitChangesWithCallback:."]; + return; + } + _photoURLSet = YES; + _photoURL = [photoURL copy]; + }); +} + +/** @fn hasUpdates + @brief Indicates at least one field has a value which needs to be committed. + */ +- (BOOL)hasUpdates { + return _displayNameSet || _photoURLSet; +} + +- (void)commitChangesWithCompletion:(nullable FIRUserProfileChangeCallback)completion { + dispatch_sync(FIRAuthGlobalWorkQueue(), ^{ + if (_consumed) { + [NSException raise:NSInternalInconsistencyException + format:@"%@", + @"commitChangesWithCallback: should only be called once."]; + return; + } + _consumed = YES; + // Return fast if there is nothing to update: + if (![self hasUpdates]) { + callInMainThreadWithError(completion, nil); + return; + } + NSString *displayName = [_displayName copy]; + BOOL displayNameWasSet = _displayNameSet; + NSURL *photoURL = [_photoURL copy]; + BOOL photoURLWasSet = _photoURLSet; + [_user executeUserUpdateWithChanges:^(FIRGetAccountInfoResponseUser *user, + FIRSetAccountInfoRequest *request) { + if (photoURLWasSet) { + request.photoURL = photoURL; + } + if (displayNameWasSet) { + request.displayName = displayName; + } + } + callback:^(NSError *_Nullable error) { + if (error) { + callInMainThreadWithError(completion, error); + return; + } + if (displayNameWasSet) { + [_user setDisplayName:displayName]; + } + if (photoURLWasSet) { + [_user setPhotoURL:photoURL]; + } + if (![_user updateKeychain:&error]) { + callInMainThreadWithError(completion, error); + return; + } + callInMainThreadWithError(completion, nil); + }]; + }); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRUserInfo.h b/Firebase/Auth/Source/FIRUserInfo.h new file mode 100644 index 0000000..03f2038 --- /dev/null +++ b/Firebase/Auth/Source/FIRUserInfo.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthSwiftNameSupport.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + @brief Represents user data returned from an identity provider. + */ +FIR_SWIFT_NAME(UserInfo) +@protocol FIRUserInfo <NSObject> + +/** @property providerID + @brief The provider identifier. + */ +@property(nonatomic, copy, readonly) NSString *providerID; + +/** @property uid + @brief The provider's user ID for the user. + */ +@property(nonatomic, copy, readonly) NSString *uid; + +/** @property displayName + @brief The name of the user. + */ +@property(nonatomic, copy, readonly, nullable) NSString *displayName; + +/** @property photoURL + @brief The URL of the user's profile photo. + */ +@property(nonatomic, copy, readonly, nullable) NSURL *photoURL; + +/** @property email + @brief The user's email address. + */ +@property(nonatomic, copy, readonly, nullable) NSString *email; + +/** @property phoneNumber + @brief A phone number associated with the user. + @remarks This property is only available for users authenticated via phone number auth. + */ +@property(nonatomic, readonly, nullable) NSString *phoneNumber; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRUserInfoImpl.h b/Firebase/Auth/Source/FIRUserInfoImpl.h new file mode 100644 index 0000000..0022a68 --- /dev/null +++ b/Firebase/Auth/Source/FIRUserInfoImpl.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRUserInfo.h" + +@class FIRGetAccountInfoResponseProviderUserInfo; + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRUserInfoImpl : NSObject <FIRUserInfo, NSSecureCoding> + +/** @fn userInfoWithGetAccountInfoResponseProviderUserInfo: + @brief A convenience factory method for constructing a @c FIRUserInfo instance from data + returned by the getAccountInfo endpoint. + @param providerUserInfo Data returned by the getAccountInfo endpoint. + @return A new instance of @c FIRUserInfo using data from the getAccountInfo endpoint. + */ ++ (nullable instancetype)userInfoWithGetAccountInfoResponseProviderUserInfo: + (FIRGetAccountInfoResponseProviderUserInfo *)providerUserInfo; + +/** @fn init + @brief This class should not be initialized manually. + @see FIRUser.providerData + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @fn initWithProviderID:userID:displayName:photoURL:email: + @brief Designated initializer. + @param providerID The provider identifier. + @param userID The unique user ID for the user (the value of the @c uid field in the token.) + @param displayName The name of the user. + @param photoURL The URL of the user's profile photo. + @param email The user's email address. + @param phoneNumber The user's phone number. + */ +- (nullable instancetype)initWithProviderID:(NSString *)providerID + userID:(NSString *)userID + displayName:(nullable NSString *)displayName + photoURL:(nullable NSURL *)photoURL + email:(nullable NSString *)email + phoneNumber:(nullable NSString *)phoneNumber + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/FIRUserInfoImpl.m b/Firebase/Auth/Source/FIRUserInfoImpl.m new file mode 100644 index 0000000..d172481 --- /dev/null +++ b/Firebase/Auth/Source/FIRUserInfoImpl.m @@ -0,0 +1,127 @@ +/* + * 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. + */ + +#import "FIRUserInfoImpl.h" + +#import "FIRGetAccountInfoResponse.h" + +/** @var kProviderIDCodingKey + @brief The key used to encode the providerID property for NSSecureCoding. + */ +static NSString *const kProviderIDCodingKey = @"providerID"; + +/** @var kUserIDCodingKey + @brief The key used to encode the userID property for NSSecureCoding. + */ +static NSString *const kUserIDCodingKey = @"userID"; + +/** @var kDisplayNameCodingKey + @brief The key used to encode the displayName property for NSSecureCoding. + */ +static NSString *const kDisplayNameCodingKey = @"displayName"; + +/** @var kProfileURLCodingKey + @brief The key used to encode the profileURL property for NSSecureCoding. + */ +static NSString *const kProfileURLCodingKey = @"profileURL"; + +/** @var kPhotoURLCodingKey + @brief The key used to encode the photoURL property for NSSecureCoding. + */ +static NSString *const kPhotoURLCodingKey = @"photoURL"; + +/** @var kEmailCodingKey + @brief The key used to encode the email property for NSSecureCoding. + */ +static NSString *const kEmailCodingKey = @"email"; + +/** @var kPhoneNumberCodingKey + @brief The key used to encode the phoneNumber property for NSSecureCoding. + */ +static NSString *const kPhoneNumberCodingKey = @"phoneNumber"; + +@implementation FIRUserInfoImpl + +@synthesize providerID = _providerID; +@synthesize uid = _userID; +@synthesize displayName = _displayName; +@synthesize photoURL = _photoURL; +@synthesize email = _email; +@synthesize phoneNumber = _phoneNumber; + ++ (nullable instancetype)userInfoWithGetAccountInfoResponseProviderUserInfo: + (FIRGetAccountInfoResponseProviderUserInfo *)providerUserInfo { + return [[self alloc] initWithProviderID:providerUserInfo.providerID + userID:providerUserInfo.federatedID + displayName:providerUserInfo.displayName + photoURL:providerUserInfo.photoURL + email:providerUserInfo.email + phoneNumber:providerUserInfo.phoneNumber]; +} + +- (nullable instancetype)initWithProviderID:(NSString *)providerID + userID:(NSString *)userID + displayName:(nullable NSString *)displayName + photoURL:(nullable NSURL *)photoURL + email:(nullable NSString *)email + phoneNumber:(nullable NSString *)phoneNumber { + self = [super init]; + if (self) { + _providerID = [providerID copy]; + _userID = [userID copy]; + _displayName = [displayName copy]; + _photoURL = [photoURL copy]; + _email = [email copy]; + _phoneNumber = [phoneNumber copy]; + } + return self; +} + +#pragma mark - NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder { + NSString *providerID = + [aDecoder decodeObjectOfClass:[NSString class] forKey:kProviderIDCodingKey]; + NSString *userID = [aDecoder decodeObjectOfClass:[NSString class] forKey:kUserIDCodingKey]; + NSString *displayName = + [aDecoder decodeObjectOfClass:[NSString class] forKey:kDisplayNameCodingKey]; + NSURL *photoURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:kPhotoURLCodingKey]; + NSString *email = [aDecoder decodeObjectOfClass:[NSString class] forKey:kEmailCodingKey]; + NSString *phoneNumber = + [aDecoder decodeObjectOfClass:[NSString class] forKey:kPhoneNumberCodingKey]; + + return [self initWithProviderID:providerID + userID:userID + displayName:displayName + photoURL:photoURL + email:email + phoneNumber:phoneNumber]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_providerID forKey:kProviderIDCodingKey]; + [aCoder encodeObject:_userID forKey:kUserIDCodingKey]; + [aCoder encodeObject:_displayName forKey:kDisplayNameCodingKey]; + [aCoder encodeObject:_photoURL forKey:kPhotoURLCodingKey]; + [aCoder encodeObject:_email forKey:kEmailCodingKey]; + [aCoder encodeObject:_phoneNumber forKey:kPhoneNumberCodingKey]; +} + +@end diff --git a/Firebase/Auth/Source/FirebaseAuth.h b/Firebase/Auth/Source/FirebaseAuth.h new file mode 100644 index 0000000..8dba24e --- /dev/null +++ b/Firebase/Auth/Source/FirebaseAuth.h @@ -0,0 +1,27 @@ +/* + * 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. + */ + +#import "FIREmailAuthProvider.h" +#import "FIRFacebookAuthProvider.h" +#import "FIRGitHubAuthProvider.h" +#import "FIRGoogleAuthProvider.h" +#import "FIRTwitterAuthProvider.h" +#import "FIRAuth.h" +#import "FIRAuthCredential.h" +#import "FIRAuthErrors.h" +#import "FIRUser.h" +#import "FIRUserInfo.h" +#import "FirebaseAuthVersion.h" diff --git a/Firebase/Auth/Source/FirebaseAuthVersion.h b/Firebase/Auth/Source/FirebaseAuthVersion.h new file mode 100755 index 0000000..1b2d06a --- /dev/null +++ b/Firebase/Auth/Source/FirebaseAuthVersion.h @@ -0,0 +1,27 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +/** + Version number for FirebaseAuth. + */ +extern const double FirebaseAuthVersionNumber; + +/** + Version string for FirebaseAuth. + */ +extern const unsigned char *const FirebaseAuthVersionString; diff --git a/Firebase/Auth/Source/FirebaseAuthVersion.m b/Firebase/Auth/Source/FirebaseAuthVersion.m new file mode 100644 index 0000000..fe4055b --- /dev/null +++ b/Firebase/Auth/Source/FirebaseAuthVersion.m @@ -0,0 +1,26 @@ +/* + * 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. + */ + +#import "FirebaseAuthVersion.h" + +// Convert the macro to a string +#define STR(x) STR_EXPAND(x) +#define STR_EXPAND(x) #x + +const double FirebaseAuthVersionNumber = FIRAuth_MINOR_VERSION; + +const unsigned char *const FirebaseAuthVersionString = + (const unsigned char *const)STR(FIRAuth_VERSION); diff --git a/Firebase/Auth/Source/Private/FIRActionCodeSettings.h b/Firebase/Auth/Source/Private/FIRActionCodeSettings.h new file mode 100644 index 0000000..adb9cbc --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRActionCodeSettings.h @@ -0,0 +1,91 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRActionCodeSettings + @brief Used to set and retrieve settings related to the handling action codes. + */ +@interface FIRActionCodeSettings : NSObject + +/** @property URL + @brief This URL represents the state/Continue URL in the form of a universal link. + @remarks This URL can should be contructed as a universal link that would either directly open + the app where the action code would be handled or continue to the app after the action code + handled by Firebase. + */ +@property(nonatomic, copy, nullable) NSURL *URL; + +/** @property handleCodeInApp + @brief Indicates whether or not the action code link will open the app directly or after being + redirected from a Firebase owned web widget. + */ +@property(assign, nonatomic) BOOL handleCodeInApp; + +/** @property iOSBundleID + @brief The iOS bundle ID, if available. + */ +@property(copy, nonatomic, readonly, nullable) NSString *iOSBundleID; + +/** @property iOSAppStoreID + @brief The iOS app store identifier, if available. + */ +@property(nonatomic, copy, readonly, nullable) NSString *iOSAppStoreID; + +/** @property androidPackageName + @brief The Android package name, if available. + */ +@property(nonatomic, copy, readonly, nullable) NSString *androidPackageName; + +/** @property androidMinimumVersion + @brief The minimum Android version supported, if available. + */ +@property(nonatomic, copy, readonly, nullable) NSString *androidMinimumVersion; + +/** @property androidInstallIfNotAvailable + @brief Indicates whether or not the Android app should be installed if not already available. + */ +@property(nonatomic, assign, readonly) BOOL androidInstallIfNotAvailable; + +/** @fn setIOSBundleID:appStoreID + @brief Sets the iOS bundle Id and appStoreID. + @param iOSBundleID The iOS bundle ID. + @param appStoreID The app's AppStore ID. + @remarks If the app is not already installed on an iOS device and an appStoreId is provided, the + app store page of the app will be opened. If no app store ID is provided, the web app link + will be used instead. + */ +- (void)setIOSBundleID:(NSString *)iOSBundleID appStoreID:(nullable NSString *)appStoreID; + +/** @fn setAndroidPackageName:installIfNotAvailable:minimumVersion: + @brief Sets the Android package name, the flag to indicate whether or not to install the app and + the minimum Android version supported. + @param androidPackageName The Android package name. + @param installIfNotAvailable Indicates whether or not the app should be installed if not + available. + @param minimumVersion The minimum version of Android supported. + @remarks If installIfNotAvailable is set to YES and the link is opened on an android device, it + will try to install the app if not already available. Otherwise the web URL is used. + */ +- (void)setAndroidPackageName:(NSString *)androidPackageName + installIfNotAvailable:(BOOL)installIfNotAvailable + minimumVersion:(nullable NSString *)minimumVersion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRAdditionalUserInfo_Internal.h b/Firebase/Auth/Source/Private/FIRAdditionalUserInfo_Internal.h new file mode 100644 index 0000000..a813566 --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAdditionalUserInfo_Internal.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#import "FIRAdditionalUserInfo.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRAdditionalUserInfo () <NSSecureCoding> + +/** @fn userInfoWithVerifyAssertionResponse: + @brief A convenience factory method for constructing a @c FIRAdditionalUserInfo instance from + data returned by the verifyAssertion endpoint. + @param verifyAssertionResponse Data returned by the verifyAssertion endpoint. + @return A new instance of @c FIRAdditionalUserInfo using data from the verifyAssertion endpoint. + */ ++ (nullable instancetype)userInfoWithVerifyAssertionResponse: + (FIRVerifyAssertionResponse *)verifyAssertionResponse; + +/** @fn initWithProviderID:profile:username: + @brief Designated initializer. + @param providerID The provider identifier. + @param profile Dictionary containing the additional IdP specific information. + @param username The name of the user. + @param isNewUser Indicates whether or not the current user was signed in for the first time. + */ +- (nullable instancetype)initWithProviderID:(NSString *)providerID + profile:(nullable NSDictionary<NSString *, NSObject *> *)profile + username:(nullable NSString *)username + isNewUser:(BOOL)isNewUser NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRAuthAPNSToken.h b/Firebase/Auth/Source/Private/FIRAuthAPNSToken.h new file mode 100644 index 0000000..8efd4e1 --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuthAPNSToken.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthAPNSTokenType.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRAuthAPNSToken + @brief A data structure for an APNs token. + */ +@interface FIRAuthAPNSToken : NSObject + +/** @property data + @brief The APNs token data. + */ +@property(nonatomic, strong, readonly) NSData *data; + +/** @property type + @brief The APNs token type. + */ +@property(nonatomic, assign, readonly) FIRAuthAPNSTokenType type; + +/** @fn initWithData:type: + @brief Initializes the instance. + @param data The APNs token data. + @param type The APNs token type. + @return The initialized instance. + */ +- (instancetype)initWithData:(NSData *)data type:(FIRAuthAPNSTokenType)type + NS_DESIGNATED_INITIALIZER; + +/** @fn init + @brief Call @c initWithData:type: to get an instance of this class. + */ +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRAuthAPNSTokenManager.h b/Firebase/Auth/Source/Private/FIRAuthAPNSTokenManager.h new file mode 100644 index 0000000..a2d6f4c --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuthAPNSTokenManager.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +@class FIRAuthAPNSToken; + +NS_ASSUME_NONNULL_BEGIN + +/** @typedef FIRAuthAPNSTokenCallback + @brief The type of block to receive an APNs token. + @param token The APNs token if one is available. + */ +typedef void (^FIRAuthAPNSTokenCallback)(FIRAuthAPNSToken *_Nullable token); + +/** @class FIRAuthAPNSTokenManager + @brief A class to manage APNs token in memory. + */ +@interface FIRAuthAPNSTokenManager : NSObject + +/** @property token + @brief The APNs token, if one is available. + @remarks Setting a token with FIRAuthAPNSTokenTypeUnknown will automatically converts it to + a token with the automatically detected type. + */ +@property(nonatomic, strong, nullable) FIRAuthAPNSToken *token; + +/** @property timeout + @brief The timeout for registering for remote notification. + @remarks Only tests should access this property. + */ +@property(nonatomic, assign) NSTimeInterval timeout; + +/** @fn init + @brief Call @c initWithApplication: to initialize an instance of this class. + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @fn initWithApplication:bundle + @brief Initializes the instance. + @param application The @c UIApplication to request the token from. + @return The initialized instance. + */ +- (instancetype)initWithApplication:(UIApplication *)application NS_DESIGNATED_INITIALIZER; + +/** @fn getTokenWithCallback: + @brief Attempts to get the APNs token. + @param callback The block to be called either immediately or in future, either when a token + becomes available, or when timeout occurs, whichever happens earlier. + */ +- (void)getTokenWithCallback:(FIRAuthAPNSTokenCallback)callback; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRAuthAppCredential.h b/Firebase/Auth/Source/Private/FIRAuthAppCredential.h new file mode 100644 index 0000000..57fa83a --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuthAppCredential.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRAuthAppCredential + @brief A class represents a credential that proves the identity of the app. + */ +@interface FIRAuthAppCredential : NSObject <NSSecureCoding> + +/** @property receipt + @brief The server acknowledgement of receiving client's claim of identity. + */ +@property(nonatomic, strong, readonly) NSString *receipt; + +/** @property secret + @brief The secret that the client received from server via a trusted channel, if ever. + */ +@property(nonatomic, strong, readonly, nullable) NSString *secret; + +/** @fn initWithReceipt:secret: + @brief Initializes the instance. + @param receipt The server acknowledgement of receiving client's claim of identity. + @param secret The secret that the client received from server via a trusted channel, if ever. + @return The initialized instance. + */ +- (instancetype)initWithReceipt:(NSString *)receipt secret:(nullable NSString *)secret + NS_DESIGNATED_INITIALIZER; + +/** @fn init + @brief Call @c initWithReceipt:secret: to get an instance of this class. + */ +- (instancetype)init NS_UNAVAILABLE; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRAuthAppCredentialManager.h b/Firebase/Auth/Source/Private/FIRAuthAppCredentialManager.h new file mode 100644 index 0000000..21c1545 --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuthAppCredentialManager.h @@ -0,0 +1,85 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +@class FIRAuthAppCredential; +@class FIRAuthKeychain; + +NS_ASSUME_NONNULL_BEGIN + +/** @typedef FIRAuthAppCredentialCallback + @brief The type of block to receive an app crdential. + @param credential The best available app credential at the time. + */ +typedef void (^FIRAuthAppCredentialCallback)(FIRAuthAppCredential *credential); + +/** @class FIRAuthAppCredentialManager + @brief A class to manage app credentials backed by iOS Keychain. + */ +@interface FIRAuthAppCredentialManager : NSObject + +/** @property credential + @brief The full credential (which has a secret) to be used by the app, if one is available. + */ +@property(nonatomic, strong, readonly, nullable) FIRAuthAppCredential *credential; + +/** @property maximumNumberOfPendingReceipts + @brief The maximum (but not necessarily the minimum) number of pending receipts to be kept. + @remarks Only tests should access this property. + */ +@property(nonatomic, assign, readonly) NSUInteger maximumNumberOfPendingReceipts; + +/** @fn init + @brief Call @c initWithKeychain: to initialize an instance of this class. + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @fn initWithKeychain: + @brief Initializes the instance. + @param keychain The iOS Keychain storage to back up the app credential with. + @return The initialized instance. + */ +- (instancetype)initWithKeychain:(FIRAuthKeychain *)keychain NS_DESIGNATED_INITIALIZER; + +/** @fn didStartVerificationWithReceipt:timeout:callback: + @brief Notifies that the app verification process has started. + @param receipt The receipt for verification. + @param timeout The timeout value for how long the callback is waited to be called. + @param callback The block to be called in future either when the verification finishes, or + when timeout occurs, whichever happens earlier. + */ +- (void)didStartVerificationWithReceipt:(NSString *)receipt + timeout:(NSTimeInterval)timeout + callback:(FIRAuthAppCredentialCallback)callback; + +/** @fn canFinishVerificationWithReceipt: + @brief Attempts to finish verification. + @param receipt The receipt to match the original receipt obtained when verification started. + @param secret The secret to complete the verification. + @return Whether or not the receipt matches a pending verification, and finishes verification + if it does. + */ +- (BOOL)canFinishVerificationWithReceipt:(NSString *)receipt secret:(NSString *)secret; + +/** @fn clearCredential + @brief Clears the saved credential, to be used in the case that it is rejected by the server. + */ +- (void)clearCredential; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRAuthAppDelegateProxy.h b/Firebase/Auth/Source/Private/FIRAuthAppDelegateProxy.h new file mode 100644 index 0000000..656e4f2 --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuthAppDelegateProxy.h @@ -0,0 +1,74 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +NS_ASSUME_NONNULL_BEGIN + +/** @protocol FIRAuthAppDelegateHandler + @brief The protocol to handle app delegate methods. + */ +@protocol FIRAuthAppDelegateHandler <NSObject> + +/** @fn setAPNSToken: + @brief Sets the APNs device token. + @param token The APNs device token. + */ +- (void)setAPNSToken:(NSData *)token; + +/** @fn canHandleNotification: + @brief Checks whether the notification can be handled by the receiver, and handles it if so. + @param notification The notification in question, which will be consumed if returns @c YES. + @return Whether the notification can be (and already has been) handled by the receiver. + */ +- (BOOL)canHandleNotification:(nonnull NSDictionary *)notification; + +@end + +/** @class FIRAuthAppDelegateProxy + @brief A manager for swizzling @c UIApplicationDelegate methods. + */ +@interface FIRAuthAppDelegateProxy : NSObject + +/** @fn initWithApplication + @brief Initialize the instance with the given @c UIApplication. + @returns An initialized instance, or @c nil if a proxy cannot be established. + @remarks This method should only be called from tests if called outside of this class. + */ +- (nullable instancetype)initWithApplication:(nullable UIApplication *)application + NS_DESIGNATED_INITIALIZER; + +/** @fn init + @brief Call @c sharedInstance to get an instance of this class. + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @fn addHandler: + @brief Adds a handler for UIApplicationDelegate methods. + @param handler The handler to be added. + */ +- (void)addHandler:(__weak id<FIRAuthAppDelegateHandler>)handler; + +/** @fn sharedInstance + @brief Gets the shared instance of this class. + @returns The shared instance of this class. + */ ++ (nullable instancetype)sharedInstance; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRAuthCredential_Internal.h b/Firebase/Auth/Source/Private/FIRAuthCredential_Internal.h new file mode 100644 index 0000000..e060cda --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuthCredential_Internal.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#import "FIRAuthCredential.h" + +@class FIRVerifyAssertionRequest; + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRAuthCredential () + +/** @fn initWithProvider: + @brief Designated initializer. + @remarks This is the designated initializer for internal/friend subclasses. + @param provider The provider name. + */ +- (nullable instancetype)initWithProvider:(NSString *)provider NS_DESIGNATED_INITIALIZER; + +/** @fn prepareVerifyAssertionRequest: + @brief Called immediately before a request to the verifyAssertion endpoint is made. Implementers + should update the passed request instance with their credentials. + @param request The request to be updated with credentials. + */ +- (void)prepareVerifyAssertionRequest:(FIRVerifyAssertionRequest *)request; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRAuthDataResult_Internal.h b/Firebase/Auth/Source/Private/FIRAuthDataResult_Internal.h new file mode 100644 index 0000000..b95edc2 --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuthDataResult_Internal.h @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#import "FIRAuthDataResult.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRAuthDataResult () <NSSecureCoding> + +/** @fn initWithUser:additionalUserInfo: + @brief Designated initializer. + @param user The signed in user reference. + @param additionalUserInfo The additional user info if available. + */ +- (nullable instancetype)initWithUser:(FIRUser *)user + additionalUserInfo:(nullable FIRAdditionalUserInfo *)additionalUserInfo + NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRAuthDispatcher.h b/Firebase/Auth/Source/Private/FIRAuthDispatcher.h new file mode 100644 index 0000000..f8ddca5 --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuthDispatcher.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** @typedef FIRAuthDispatcherImplBlock + @brief The type of block which can be set as the implementation for @c + dispatchAfterDelay:queue:callback: . + + @param delay The delay in seconds after which the task will be scheduled to execute. + @param queue The dispatch queue on which the task will be submitted. + @param task The task (block) to be scheduled for future execution. + */ +typedef void(^FIRAuthDispatcherImplBlock)(NSTimeInterval delay, + dispatch_queue_t queue, + void (^task)(void)); + +/** @class FIRAuthDispatchAfter + @brief A utility class used to facilitate scheduling tasks to be executed in the future. + */ +@interface FIRAuthDispatcher : NSObject + +/** @property dispatchAfterImplementation + @brief Allows custom implementation of dispatchAfterDelay:queue:callback:. + @remarks Set to nil to restore default implementation. + */ +@property(nonatomic, nullable, copy) FIRAuthDispatcherImplBlock dispatchAfterImplementation; + +/** @fn dispatchAfterDelay:queue:callback: + @brief Schedules task in the future after a specified delay. + + @param delay The delay in seconds after which the task will be scheduled to execute. + @param queue The dispatch queue on which the task will be submitted. + @param task The task (block) to be scheduled for future execution. + */ + - (void)dispatchAfterDelay:(NSTimeInterval)delay + queue:(dispatch_queue_t)queue + task:(void (^)(void))task; + +/** @fn sharedInstance + @brief Gets the shared instance of this class. + @returns The shared instance of this clss + */ ++ (instancetype)sharedInstance; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRAuthErrorUtils.h b/Firebase/Auth/Source/Private/FIRAuthErrorUtils.h new file mode 100644 index 0000000..e2ad4aa --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuthErrorUtils.h @@ -0,0 +1,418 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +@class FIRPhoneAuthCredential; + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRAuthErrorUtils + @brief Utility class used to construct @c NSError instances. + */ +@interface FIRAuthErrorUtils : NSObject + +/** @fn RPCRequestEncodingErrorWithUnderlyingError + @brief Constructs an @c NSError with the @c FIRAuthInternalErrorCodeRPCRequestEncodingError + code and a populated @c NSUnderlyingErrorKey in the @c NSError.userInfo dictionary. + @param underlyingError The value of the @c NSUnderlyingErrorKey key. + @remarks This error is used when an @c FIRAuthRPCRequest.unencodedHTTPRequestBodyWithError: + invocation returns an error. The error returned is wrapped in this internal error code. + */ ++ (NSError *)RPCRequestEncodingErrorWithUnderlyingError:(NSError *)underlyingError; + +/** @fn JSONSerializationErrorForUnencodableType + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeJSONSerializationError code. + @remarks This error is used when an @c NSJSONSerialization.isValidJSONObject: check fails, not + for when an error is returned from @c NSJSONSerialization.dataWithJSONObject:options:error:. + */ ++ (NSError *)JSONSerializationErrorForUnencodableType; + +/** @fn JSONSerializationErrorWithUnderlyingError: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeJSONSerializationError code, and the + @c underlyingError as the @c NSUnderlyingErrorKey value in the @c NSError.userInfo + dictionary. + @param underlyingError The value of the @c NSUnderlyingErrorKey key. + @remarks This error is used when an invocation of + @c NSJSONSerialization.dataWithJSONObject:options:error: returns an error. + */ ++ (NSError *)JSONSerializationErrorWithUnderlyingError:(NSError *)underlyingError; + +/** @fn networkErrorWithUnderlyingError: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeNetworkError code, and the + @c underlyingError as the @c NSUnderlyingErrorKey value in the @c NSError.userInfo + dictionary. + @param underlyingError The value of the @c NSUnderlyingErrorKey key. Should be the error from + GTM. + @remarks This error is used when a network request results in an error, and no body data was + returned. + */ ++ (NSError *)networkErrorWithUnderlyingError:(NSError *)underlyingError; + +/** @fn unexpectedErrorResponseWithUnderlyingError: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeNetworkError code, and the + @c underlyingError as the @c NSUnderlyingErrorKey value. + @param data The value of the @c FIRAuthErrorUserInfoDataKey key in the @c NSError.userInfo + dictionary. + @param underlyingError The value of the @c NSUnderlyingErrorKey key in the @c NSError.userInfo + dictionary. + @remarks This error is used when a network request results in an error, and unserializable body + data was returned. + */ ++ (NSError *)unexpectedErrorResponseWithData:(NSData *)data + underlyingError:(NSError *)underlyingError; + +/** @fn unexpectedErrorResponseWithDeserializedResponse: + @brief Constructs an @c NSError with the @c FIRAuthInternalErrorCodeUnexpectedErrorResponse + code, and a populated @c FIRAuthErrorUserInfoDeserializedResponseKey key in the + @c NSError.userInfo dictionary. + @param deserializedResponse The value of the @c FIRAuthErrorUserInfoDeserializedResponseKey key. + @remarks This error is used when a network request results in an error, and the body data was + deserializable as JSON, but couldn't be decoded as an error. + */ ++ (NSError *)unexpectedErrorResponseWithDeserializedResponse:(id)deserializedResponse; + +/** @fn unexpectedResponseWithData:underlyingError: + @brief Constructs an @c NSError with the @c FIRAuthInternalErrorCodeUnexpectedResponse + code, and a populated @c FIRAuthErrorUserInfoDataKey key in the @c NSError.userInfo + dictionary. + @param data The value of the @c FIRAuthErrorUserInfoDataKey key in the @c NSError.userInfo + dictionary. + @param underlyingError The value of the @c NSUnderlyingErrorKey key in the @c NSError.userInfo + dictionary. + @remarks This error is used when a network request is apparently successful, but the body data + couldn't be deserialized as JSON. + */ ++ (NSError *)unexpectedResponseWithData:(NSData *)data + underlyingError:(NSError *)underlyingError;; + +/** @fn unexpectedResponseWithDeserializedResponse: + @brief Constructs an @c NSError with the @c FIRAuthInternalErrorCodeUnexpectedResponse + code, and a populated @c FIRAuthErrorUserInfoDeserializedResponseKey key in the + @c NSError.userInfo dictionary. + @param deserializedResponse The value of the @c FIRAuthErrorUserInfoDeserializedResponseKey key. + @remarks This error is used when a network request is apparently successful, the body data was + successfully deserialized as JSON, but the JSON wasn't a dictionary. + */ ++ (NSError *)unexpectedResponseWithDeserializedResponse:(id)deserializedResponse; + +/** @fn unexpectedResponseWithDeserializedResponse:underlyingError: + @brief Constructs an @c NSError with the @c FIRAuthInternalErrorCodeUnexpectedResponse + code, and populated @c FIRAuthErrorUserInfoDeserializedResponseKey and + @c NSUnderlyingErrorKey keys in the @c NSError.userInfo dictionary. + @param deserializedResponse The value of the @c FIRAuthErrorUserInfoDeserializedResponseKey key. + @param underlyingError The value of the @c NSUnderlyingErrorKey key. + @remarks This error is used when a network request was apparently successful, the body data was + successfully deserialized as JSON, but the data type of the response was unexpected. + */ ++ (NSError *)unexpectedResponseWithDeserializedResponse:(nullable id)deserializedResponse + underlyingError:(NSError *)underlyingError; + +/** @fn RPCResponseDecodingErrorWithDeserializedResponse:underlyingError: + @brief Constructs an @c NSError with the @c FIRAuthInternalErrorCodeRPCResponseDecodingError + code, and populated @c FIRAuthErrorUserInfoDeserializedResponseKey and + @c NSUnderlyingErrorKey keys in the @c NSError.userInfo dictionary. + @param deserializedResponse The value of the @c FIRAuthErrorUserInfoDeserializedResponseKey key. + @param underlyingError The value of the @c NSUnderlyingErrorKey key. + @remarks This error is used when an invocation of @c FIRAuthRPCResponse.setWithDictionary:error: + resulted in an error. + */ ++ (NSError *)RPCResponseDecodingErrorWithDeserializedResponse:(id)deserializedResponse + underlyingError:(NSError *)underlyingError; + +/** @fn emailAlreadyInUseErrorWithEmail: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeEmailExists code. + @param email The email address that is already in use. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)emailAlreadyInUseErrorWithEmail:(nullable NSString *)email; + +/** @fn userDisabledErrorWithMessageWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeUserDisabled code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)userDisabledErrorWithMessage:(nullable NSString *)message; + +/** @fn wrongPasswordErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeWrongPassword code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)wrongPasswordErrorWithMessage:(nullable NSString *)message; + +/** @fn tooManyRequestsErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeTooManyRequests Code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)tooManyRequestsErrorWithMessage:(nullable NSString *)message; + +/** @fn invalidCustomTokenErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeInvalidCustomToken code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)invalidCustomTokenErrorWithMessage:(nullable NSString *)message; + +/** @fn customTokenMistmatchErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeCustomTokenMismatch code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)customTokenMistmatchErrorWithMessage:(nullable NSString *)message; + +/** @fn invalidCredentialErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeInvalidCredential code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)invalidCredentialErrorWithMessage:(nullable NSString *)message; + +/** @fn requiresRecentLoginError + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeRequiresRecentLogin code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)requiresRecentLoginErrorWithMessage:(nullable NSString *)message; + +/** @fn invalidUserTokenErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeInvalidUserToken code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)invalidUserTokenErrorWithMessage:(nullable NSString *)message; + +/** @fn invalidEmailErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeInvalidEmail code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)invalidEmailErrorWithMessage:(nullable NSString *)message; + +/** @fn accountExistsWithDifferentCredentialErrorWithEmail: + @brief Constructs an @c NSError with the @c FIRAuthErrorAccountExistsWithDifferentCredential + code. + @param Email The email address that is already associated with an existing account + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)accountExistsWithDifferentCredentialErrorWithEmail:(nullable NSString *)Email; + +/** @fn providerAlreadyLinkedErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeProviderAlreadyLinked code. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)providerAlreadyLinkedError; + +/** @fn noSuchProviderError + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeNoSuchProvider code. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)noSuchProviderError; + +/** @fn userTokenExpiredErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeUserTokenExpired code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)userTokenExpiredErrorWithMessage:(nullable NSString *)message; + +/** @fn userNotFoundErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeUserNotFound code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)userNotFoundErrorWithMessage:(nullable NSString *)message; + +/** @fn invalidLocalAPIKeyErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeInvalidAPIKey code. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)invalidAPIKeyError; + +/** @fn userMismatchError + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeUserMismatch code. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)userMismatchError; + +/** @fn credentialAlreadyInUseErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeCredentialAlreadyInUse code. + @param message Error message from the backend, if any. + @param credential Auth credential to be added to the Error User Info dictionary. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)credentialAlreadyInUseErrorWithMessage:(nullable NSString *)message + credential:(nullable FIRPhoneAuthCredential *)credential; + +/** @fn operationNotAllowedErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeOperationNotAllowed code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)operationNotAllowedErrorWithMessage:(nullable NSString *)message; + +/** @fn weakPasswordErrorWithServerResponseReason: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeWeakPassword code. + @param serverResponseReason A more detailed explanation string from server response. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)weakPasswordErrorWithServerResponseReason:(NSString *)serverResponseReason; + +/** @fn appNotAuthorizedError + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeAppNotAuthorized code. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)appNotAuthorizedError; + +/** @fn expiredActionCodeErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeExpiredActionCode code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)expiredActionCodeErrorWithMessage:(nullable NSString *)message; + +/** @fn invalidActionCodeErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeInvalidActionCode code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)invalidActionCodeErrorWithMessage:(nullable NSString *)message; + +/** @fn invalidMessagePayloadError: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeInvalidMessagePayload code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)invalidMessagePayloadErrorWithMessage:(nullable NSString *)message; + +/** @fn invalidSenderErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeInvalidSender code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)invalidSenderErrorWithMessage:(nullable NSString *)message; + +/** @fn invalidRecipientEmailError: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeInvalidRecipientEmail code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)invalidRecipientEmailErrorWithMessage:(nullable NSString *)message; + +/** @fn missingPhoneNumberErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeMissingPhoneNumber code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)missingPhoneNumberErrorWithMessage:(nullable NSString *)message; + +/** @fn invalidPhoneNumberErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeInvalidPhoneNumber code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)invalidPhoneNumberErrorWithMessage:(nullable NSString *)message; + +/** @fn missingVerificationCodeErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeMissingVerificationCode code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)missingVerificationCodeErrorWithMessage:(nullable NSString *)message; + +/** @fn invalidVerificationCodeErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeInvalidVerificationCode code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)invalidVerificationCodeErrorWithMessage:(nullable NSString *)message; + +/** @fn missingVerificationIDErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeMissingVerificationID code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)missingVerificationIDErrorWithMessage:(nullable NSString *)message; + +/** @fn invalidVerificationIDErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeInvalidVerificationID code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)invalidVerificationIDErrorWithMessage:(nullable NSString *)message; + +/** @fn sessionExpiredErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeSessionExpired code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)sessionExpiredErrorWithMessage:(nullable NSString *)message; + +/** @fn missingAppCredentialWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorMissingCredential code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)missingAppCredentialWithMessage:(nullable NSString *)message; + +/** @fn invalidAppCredentialWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorInvalidCredential code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)invalidAppCredentialWithMessage:(nullable NSString *)message; + +/** @fn quotaExceededErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeQuotaExceeded code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)quotaExceededErrorWithMessage:(nullable NSString *)message; + +/** @fn missingAppTokenError + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeMissingAppToken code. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)missingAppTokenError; + +/** @fn notificationNotForwardedError + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeNotificationNotForwarded code. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)notificationNotForwardedError; + +/** @fn appNotVerifiedErrorWithMessage: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeAppNotVerified code. + @param message Error message from the backend, if any. + @return The NSError instance associated with the given FIRAuthError. + */ ++ (NSError *)appNotVerifiedErrorWithMessage:(nullable NSString *)message; + +/** @fn keychainErrorWithFunction:status: + @brief Constructs an @c NSError with the @c FIRAuthErrorCodeKeychainError code. + @param keychainFunction The keychain function which was invoked and yielded an unexpected + response. The @c NSLocalizedFailureReasonErrorKey field in the @c NSError.userInfo + dictionary will contain a string partially comprised of this value. + @param status The response status from the invoked keychain function. The + @c NSLocalizedFailureReasonErrorKey field in the @c NSError.userInfo dictionary will contain + a string partially comprised of this value. + */ ++ (NSError *)keychainErrorWithFunction:(NSString *)keychainFunction status:(OSStatus)status; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRAuthGlobalWorkQueue.h b/Firebase/Auth/Source/Private/FIRAuthGlobalWorkQueue.h new file mode 100644 index 0000000..0950ff4 --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuthGlobalWorkQueue.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** @fn FIRAuthGlobalWorkQueue + @brief Retrieves the global serial work queue for Firebase Auth. + @return The global serial dispatch queue. + @remarks To ensure thread safety, all auth code must be executed in either this global work + queue, or a serial queue that has its target queue set to this work queue. All public method + implementations that may involve contested code shall dispatch to this work queue as the + first thing they do. + */ +extern dispatch_queue_t FIRAuthGlobalWorkQueue(); + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRAuthInternalErrors.h b/Firebase/Auth/Source/Private/FIRAuthInternalErrors.h new file mode 100644 index 0000000..7eebbde --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuthInternalErrors.h @@ -0,0 +1,365 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthErrors.h" + +/** @var FIRAuthPublicErrorCodeFlag + @brief Bitmask value indicating the error represents a public error code when this bit is + zeroed. Error codes which don't contain this flag will be wrapped in an @c NSError whose + code is @c FIRAuthErrorCodeInternalError. + */ +static const NSInteger FIRAuthPublicErrorCodeFlag = 1 << 20; + +/** @var FIRAuthInternalErrorDomain + @brief The Firebase Auth error domain for internal errors. + */ +extern NSString *const FIRAuthInternalErrorDomain; + +/** @var FIRAuthErrorUserInfoDeserializedResponseKey + @brief Errors with the code @c FIRAuthErrorCodeUnexpectedResponseError, + @c FIRAuthErrorCodeUnexpectedErrorResponseError, and + @c FIRAuthInternalErrorCodeRPCResponseDecodingError may contain an @c NSError.userInfo + dictionary which contains this key. The value associated with this key is an object of + unspecified contents containing the deserialized server response. + */ +extern NSString *const FIRAuthErrorUserInfoDeserializedResponseKey; + +/** @var FIRAuthErrorUserInfoDataKey + @brief Errors with the code @c FIRAuthErrorCodeUnexpectedResponseError or + @c FIRAuthErrorCodeUnexpectedErrorResponseError may contain an @c NSError.userInfo + dictionary which contains this key. The value associated with this key is an @c NSString + which represents the response from a server to an RPC which could not be deserialized. + */ +extern NSString *const FIRAuthErrorUserInfoDataKey; + + +/** @var FIRAuthInternalErrorCode + @brief Error codes used internally by Firebase Auth. + @remarks All errors are generated using an internal error code. These errors are automatically + converted to the appropriate public version of the @c NSError by the methods in + @c FIRAuthErrorUtils + */ +typedef NS_ENUM(NSInteger, FIRAuthInternalErrorCode) { + /** @var FIRAuthInternalErrorCodeNetworkError + @brief Indicates a network error occurred (such as a timeout, interrupted connection, or + unreachable host.) + @remarks These types of errors are often recoverable with a retry. + + See the @c NSUnderlyingError value in the @c NSError.userInfo dictionary for details about + the network error which occurred. + */ + FIRAuthInternalErrorCodeNetworkError = FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeNetworkError, + + /** @var FIRAuthInternalErrorCodeEmailAlreadyInUse + @brief The email used to attempt a sign-up already exists. + */ + FIRAuthInternalErrorCodeEmailAlreadyInUse = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeEmailAlreadyInUse, + + /** @var FIRAuthInternalErrorCodeUserDisabled + @brief Indicates the user's account is disabled on the server side. + */ + FIRAuthInternalErrorCodeUserDisabled = FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeUserDisabled, + + /** @var FIRAuthInternalErrorCodeWrongPassword + @brief Indicates the user attempted sign in with a wrong password + */ + FIRAuthInternalErrorCodeWrongPassword = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeWrongPassword, + + /** @var FIRAuthInternalErrorCodeKeychainError + @brief Indicates an error occurred accessing the keychain. + @remarks The @c NSLocalizedFailureReasonErrorKey field in the @c NSError.userInfo dictionary + will contain more information about the error encountered. + */ + FIRAuthInternalErrorCodeKeychainError = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeKeychainError, + + /** @var FIRAuthInternalErrorCodeInternalError + @brief An internal error occurred. + @remarks This value is here for consistency. It's also used to make the implementation of + wrapping internal errors simpler. + */ + FIRAuthInternalErrorCodeInternalError = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInternalError, + + /** @var FIRAuthInternalErrorCodeTooManyRequests + @brief Indicates that too many requests were made to a server method. + */ + FIRAuthInternalErrorCodeTooManyRequests = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeTooManyRequests, + + /** @var FIRAuthInternalErrorCodeInvalidCustomToken + @brief Indicates a validation error with the custom token. + */ + FIRAuthInternalErrorCodeInvalidCustomToken = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInvalidCustomToken, + + /** @var FIRAuthInternalErrorCodeCredentialMismatch + @brief Indicates the service account and the API key belong to different projects. + */ + FIRAuthInternalErrorCodeCustomTokenMismatch = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeCustomTokenMismatch, + + /** @var FIRAuthInternalErrorCodeInvalidCredential + @brief Indicates the IDP token or requestUri is invalid. + */ + FIRAuthInternalErrorCodeInvalidCredential = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInvalidCredential, + + /** @var FIRAuthInternalErrorCodeRequiresRecentLogin + @brief Indicates the user has attemped to change email or password more than 5 minutes after + signing in. + */ + FIRAuthInternalErrorCodeRequiresRecentLogin = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeRequiresRecentLogin, + + /** @var FIRAuthInternalErrorCodeInvalidUserToken + @brief Indicates user's saved auth credential is invalid, the user needs to sign in again. + */ + FIRAuthInternalErrorCodeInvalidUserToken = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInvalidUserToken, + + /** @var FIRAuthInternalErrorCodeInvalidEmail + @brief Indicates the email identifier is invalid. + */ + FIRAuthInternalErrorCodeInvalidEmail = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInvalidEmail, + + /** @var FIRAuthInternalErrorCodeAccountExistsWithDifferentCredential + @brief Indicates account linking is needed. + */ + FIRAuthInternalErrorCodeAccountExistsWithDifferentCredential = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeAccountExistsWithDifferentCredential, + + /** @var FIRAuthInternalErrorCodeProviderAlreadyLinked + @brief Indicates an attempt to link a provider to which we are already linked. + */ + FIRAuthInternalErrorCodeProviderAlreadyLinked = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeProviderAlreadyLinked, + + /** @var FIRAuthInternalErrorCodeNoSuchProvider + @brief Indicates an attempt to unlink a provider that is not is not linked. + */ + FIRAuthInternalErrorCodeNoSuchProvider = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeNoSuchProvider, + + /** @var FIRAuthInternalErrorCodeUserTokenExpired + @brief Indicates the token issue time is older than account's valid_since time. + */ + FIRAuthInternalErrorCodeUserTokenExpired = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeUserTokenExpired, + + /** @var FIRAuthInternalErrorCodeUserNotFound + @brief Indicates the user account was been found. + */ + FIRAuthInternalErrorCodeUserNotFound = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeUserNotFound, + + /** @var FIRAuthInternalErrorCodeInvalidAPIKey + @brief Indicates an invalid API Key was supplied in the request. + */ + FIRAuthInternalErrorCodeInvalidAPIKey = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInvalidAPIKey, + + /** @var FIRAuthInternalErrorCodeOperationNotAllowed + @brief Indicates that admin disabled sign-in with the specified IDP. + */ + FIRAuthInternalErrorCodeOperationNotAllowed = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeOperationNotAllowed, + + /** @var FIRAuthInternalErrorCodeUserMismatch + @brief Indicates that user attempted to reauthenticate with a user other than the current + user. + */ + FIRAuthInternalErrorCodeUserMismatch = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeUserMismatch, + + /** @var FIRAuthInternalErrorCodeCredentialAlreadyInUse + @brief Indicates an attempt to link with a credential that has already been linked with a + different Firebase account. + */ + FIRAuthInternalErrorCodeCredentialAlreadyInUse = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeCredentialAlreadyInUse, + + /** @var FIRAuthInternalErrorCodeWeakPassword + @brief Indicates an attempt to set a password that is considered too weak. + */ + FIRAuthInternalErrorCodeWeakPassword = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeWeakPassword, + + /** @var FIRAuthInternalErrorCodeAppNotAuthorized + @brief Indicates the App is not authorized to use Firebase Authentication with the + provided API Key. + */ + FIRAuthInternalErrorCodeAppNotAuthorized = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeAppNotAuthorized, + + /** @var FIRAuthInternalErrorCodeExpiredActionCode + @brief Indicates the OOB code is expired. + */ + FIRAuthInternalErrorCodeExpiredActionCode = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeExpiredActionCode, + + /** @var FIRAuthInternalErrorCodeInvalidActionCode + @brief Indicates the OOB code is invalid. + */ + FIRAuthInternalErrorCodeInvalidActionCode = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInvalidActionCode, + + /** Indicates that there are invalid parameters in the payload during a "send password reset email + * " attempt. + */ + FIRAuthInternalErrorCodeInvalidMessagePayload = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInvalidMessagePayload, + + /** Indicates that the sender email is invalid during a "send password reset email" attempt. + */ + FIRAuthInternalErrorCodeInvalidSender = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInvalidSender, + + /** Indicates that the recipient email is invalid. + */ + FIRAuthInternalErrorCodeInvalidRecipientEmail = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInvalidRecipientEmail, + + /** Indicates that a phone number was not provided in a call to @c verifyPhoneNumber:completion:. + */ + FIRAuthInternalErrorCodeMissingPhoneNumber = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeMissingPhoneNumber, + + /** Indicates that an invalid phone number was provided in a call to @c + verifyPhoneNumber:completion:. + */ + FIRAuthInternalErrorCodeInvalidPhoneNumber = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInvalidPhoneNumber, + + /** Indicates that the phone auth credential was created with an empty verification code. + */ + FIRAuthInternalErrorCodeMissingVerificationCode = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeMissingVerificationCode, + + /** Indicates that an invalid verification code was used in the verifyPhoneNumber request. + */ + FIRAuthInternalErrorCodeInvalidVerificationCode = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInvalidVerificationCode, + + /** Indicates that the phone auth credential was created with an empty verification ID. + */ + FIRAuthInternalErrorCodeMissingVerificationID = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeMissingVerificationID, + + /** Indicates that the APNS device token is missing in the verifyClient request. + */ + FIRAuthInternalErrorCodeMissingAppCredential = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeMissingAppCredential, + + /** Indicates that an invalid APNS device token was used in the verifyClient request. + */ + FIRAuthInternalErrorCodeInvalidAppCredential = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInvalidAppCredential, + + /** Indicates that an invalid verification ID was used in the verifyPhoneNumber request. + */ + FIRAuthInternalErrorCodeInvalidVerificationID = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeInvalidVerificationID, + + /** Indicates that the quota of SMS messages for a given project has been exceeded. + */ + FIRAuthInternalErrorCodeQuotaExceeded = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeQuotaExceeded, + + // The enum values between 17046 and 17051 are reserved and should NOT be used for new error + // codes. + + /** Indicates that the SMS code has expired + */ + FIRAuthInternalErrorCodeSessionExpired = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeSessionExpired, + + FIRAuthInternalErrorCodeMissingAppToken = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeMissingAppToken, + + FIRAuthInternalErrorCodeNotificationNotForwarded = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeNotificationNotForwarded, + + FIRAuthInternalErrorCodeAppNotVerified = + FIRAuthPublicErrorCodeFlag | FIRAuthErrorCodeAppNotVerified, + + /** @var FIRAuthInternalErrorCodeRPCRequestEncodingError + @brief Indicates an error encoding the RPC request. + @remarks This is typically due to some sort of unexpected input value. + + See the @c NSUnderlyingError value in the @c NSError.userInfo dictionary for details. + */ + FIRAuthInternalErrorCodeRPCRequestEncodingError = 1, + + /** @var FIRAuthInternalErrorCodeJSONSerializationError + @brief Indicates an error serializing an RPC request. + @remarks This is typically due to some sort of unexpected input value. + + If an @c NSJSONSerialization.isValidJSONObject: check fails, the error will contain no + @c NSUnderlyingError key in the @c NSError.userInfo dictionary. If an error was + encountered calling @c NSJSONSerialization.dataWithJSONObject:options:error:, the + resulting error will be associated with the @c NSUnderlyingError key in the + @c NSError.userInfo dictionary. + */ + FIRAuthInternalErrorCodeJSONSerializationError = 2, + + /** @var FIRAuthInternalErrorCodeUnexpectedErrorResponse + @brief Indicates an HTTP error occurred and the data returned either couldn't be deserialized + or couldn't be decoded. + @remarks See the @c NSUnderlyingError value in the @c NSError.userInfo dictionary for details + about the HTTP error which occurred. + + If the response could be deserialized as JSON then the @c NSError.userInfo dictionary will + contain a value for the key @c FIRAuthErrorUserInfoDeserializedResponseKey which is the + deserialized response value. + + If the response could not be deserialized as JSON then the @c NSError.userInfo dictionary + will contain values for the @c NSUnderlyingErrorKey and @c FIRAuthErrorUserInfoDataKey + keys. + */ + FIRAuthInternalErrorCodeUnexpectedErrorResponse = 3, + + /** @var FIRAuthInternalErrorCodeUnexpectedResponse + @brief Indicates the HTTP response indicated the request was a successes, but the response + contains something other than a JSON-encoded dictionary, or the data type of the response + indicated it is different from the type of response we expected. + @remarks See the @c NSUnderlyingError value in the @c NSError.userInfo dictionary. + If this key is present in the dictionary, it may contain an error from + @c NSJSONSerialization error (indicating the response received was of the wrong data + type). + + See the @c FIRAuthErrorUserInfoDeserializedResponseKey value in the @c NSError.userInfo + dictionary. If the response could be deserialized, it's deserialized representation will + be associated with this key. If the @c NSUnderlyingError value in the @c NSError.userInfo + dictionary is @c nil, this indicates the JSON didn't represent a dictionary. + */ + FIRAuthInternalErrorCodeUnexpectedResponse = 4, + + /** @var FIRAuthInternalErrorCodeRPCResponseDecodingError + @brief Indicates an error decoding the RPC response. + This is typically due to some sort of unexpected response value from the server. + @remarks See the @c NSUnderlyingError value in the @c NSError.userInfo dictionary for details. + + See the @c FIRErrorUserInfoDecodedResponseKey value in the @c NSError.userInfo dictionary. + The deserialized representation of the response will be associated with this key. + */ + FIRAuthInternalErrorCodeRPCResponseDecodingError = 5, +}; diff --git a/Firebase/Auth/Source/Private/FIRAuthKeychain.h b/Firebase/Auth/Source/Private/FIRAuthKeychain.h new file mode 100644 index 0000000..c52e26a --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuthKeychain.h @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** + @brief The protocol for permanant data storage. + */ +@protocol FIRAuthStorage <NSObject> + +/** @fn initWithService: + @brief Initialize a @c FIRAuthStorage instance. + @param service The name of the storage service to use. + @return An initialized @c FIRAuthStorage instance for the specified service. + */ +- (id<FIRAuthStorage>)initWithService:(NSString *)service; + +/** @fn dataForKey:error: + @brief Gets the data for @c key in the storage. The key is set for the attribute + @c kSecAttrAccount of a generic password query. + @param key The key to use. + @param error The address to store any error that occurs during the process, if not NULL. + If the operation was successful, its content is set to @c nil . + @return The data stored in the storage for @c key, if any. + */ +- (nullable NSData *)dataForKey:(NSString *)key error:(NSError **_Nullable)error; + +/** @fn setData:forKey:error: + @brief Sets the data for @c key in the storage. The key is set for the attribute + @c kSecAttrAccount of a generic password query. + @param data The data to store. + @param key The key to use. + @param error The address to store any error that occurs during the process, if not NULL. + @return Whether the operation succeeded or not. + */ +- (BOOL)setData:(NSData *)data forKey:(NSString *)key error:(NSError **_Nullable)error; + +/** @fn removeDataForKey:error: + @brief Removes the data for @c key in the storage. The key is set for the attribute + @c kSecAttrAccount of a generic password query. + @param key The key to use. + @param error The address to store any error that occurs during the process, if not NULL. + @return Whether the operation succeeded or not. + */ +- (BOOL)removeDataForKey:(NSString *)key error:(NSError **_Nullable)error; + +@end + +/** @class FIRAuthKeychain + @brief The utility class to manipulate data in iOS Keychain. + */ +@interface FIRAuthKeychain : NSObject <FIRAuthStorage> +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRAuthNotificationManager.h b/Firebase/Auth/Source/Private/FIRAuthNotificationManager.h new file mode 100644 index 0000000..42e5db8 --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuthNotificationManager.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +@class FIRAuthAppCredentialManager; + +NS_ASSUME_NONNULL_BEGIN + +/** @typedef FIRAuthNotificationForwardingCallback + @brief The type of block to receive whether or not remote notifications are being forwarded. + @param isNotificationBeingForwarded Whether or not remote notifications are being forwarded. + */ +typedef void (^FIRAuthNotificationForwardingCallback)(BOOL isNotificationBeingForwarded); + +/** @class FIRAuthNotificationManager + */ +@interface FIRAuthNotificationManager : NSObject + +/** @property timeout + @brief The timeout for checking for notification forwarding. + @remarks Only tests should access this property. + */ +@property(nonatomic, assign) NSTimeInterval timeout; + +/** @fn initWithApplication:appCredentialManager: + @brief Initializes the instance. + @param application The application. + @param appCredentialManager The object to handle app credentials delivered via notification. + @return The initialized instance. + */ +- (instancetype)initWithApplication:(UIApplication *)application + appCredentialManager:(FIRAuthAppCredentialManager *)appCredentialManager + NS_DESIGNATED_INITIALIZER; + +/** @fn init + @brief please use initWithAppCredentialManager: instead. + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @fn checkNotificationForwardingWithCallback: + @brief Checks whether or not remote notifications are being forwarded to this class. + @param callback The block to be called either immediately or in future once a result + is available. + */ +- (void)checkNotificationForwardingWithCallback:(FIRAuthNotificationForwardingCallback)callback; + +/** @fn canHandleNotification: + @brief Attempts to handle the remote notification. + @param notification The notification in question. + @return Whether or the notification has been handled. + */ +- (BOOL)canHandleNotification:(NSDictionary *)notification; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRAuthSerialTaskQueue.h b/Firebase/Auth/Source/Private/FIRAuthSerialTaskQueue.h new file mode 100644 index 0000000..cdae046 --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuthSerialTaskQueue.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** @typedef FIRAuthSerialTaskCompletionBlock + @brief The type of method a @c FIRAuthSerialTask must call when it is complete. + */ +typedef void (^FIRAuthSerialTaskCompletionBlock)(void); + +/** @typedef FIRAuthSerialTask + @brief Represents a unit of work submitted to a task queue. + @param complete The task MUST call the complete method when done. + */ +typedef void (^FIRAuthSerialTask)(FIRAuthSerialTaskCompletionBlock complete); + +/** @class FIRAuthSerialTaskQueue + @brief An easy to use serial task queue which supports a callback-based completion notification + system for easy asyncronous call chaining. + */ +@interface FIRAuthSerialTaskQueue : NSObject + +/** @fn enqueueTask: + @brief Enqueues a task for serial execution in the queue. + @remarks The task MUST call the complete method when done. This method is thread-safe. + The task block won't be executed concurrently with any other blocks in other task queues or + the global work queue as returned by @c FIRAuthGlobalWorkQueue , but an uncompleted task + (e.g. task block finished executation before complete method is called at a later time) + does not affect other task queues or the global work queue. + */ +- (void)enqueueTask:(FIRAuthSerialTask)task; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRAuthUserDefaultsStorage.h b/Firebase/Auth/Source/Private/FIRAuthUserDefaultsStorage.h new file mode 100644 index 0000000..13774ab --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuthUserDefaultsStorage.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +// This class is only available in the simulator. +#if TARGET_OS_SIMULATOR +#ifndef FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE +#define FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE 1 +#endif +#endif + +#if FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE + +#import "FIRAuthKeychain.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRAuthUserDefaultsStorage + @brief The utility class to storage data in NSUserDefaults. + */ +@interface FIRAuthUserDefaultsStorage : NSObject <FIRAuthStorage> + +/** @fn clear + @brief Clears all data from the storage. + @remarks This method is only supposed to be called from tests. + */ +- (void)clear; + +@end + +NS_ASSUME_NONNULL_END + +#endif // FIRAUTH_USER_DEFAULTS_STORAGE_AVAILABLE diff --git a/Firebase/Auth/Source/Private/FIRAuth_Internal.h b/Firebase/Auth/Source/Private/FIRAuth_Internal.h new file mode 100644 index 0000000..bdbefce --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRAuth_Internal.h @@ -0,0 +1,123 @@ +/* + * 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. + */ + +#import "FIRAuth.h" + +@class FIRAuthAPNSTokenManager; +@class FIRAuthAppCredentialManager; +@class FIRAuthNotificationManager; + +NS_ASSUME_NONNULL_BEGIN + +/** @var FIRAuthStateDidChangeInternalNotification + @brief The name of the @c NSNotificationCenter notification which is posted when the auth state + changes (e.g. a new token has been produced, a user logs in or out). The object parameter of + the notification is a dictionary possibly containing the key: + @c FIRAuthStateDidChangeInternalNotificationTokenKey (the new access token.) If it does not + contain this key it indicates a sign-out event took place. + */ +extern NSString *const FIRAuthStateDidChangeInternalNotification; + +/** @var FIRAuthStateDidChangeInternalNotificationTokenKey + @brief A key present in the dictionary object parameter of the + @c FIRAuthStateDidChangeInternalNotification notification. The value associated with this + key will contain the new access token. + */ +extern NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey; + +@interface FIRAuth () + +/** @property APIKey + @brief The Google API key. + @remarks Needed for calls to identity toolkit and secure token service. + */ +@property(nonatomic, copy, readonly) NSString *APIKey; + +/** @property tokenManager + @brief The manager for APNs tokens used by phone number auth. + */ +@property(nonatomic, strong, readonly) FIRAuthAPNSTokenManager *tokenManager; + +/** @property appCredentailManager + @brief The manager for app credentials used by phone number auth. + */ +@property(nonatomic, strong, readonly) FIRAuthAppCredentialManager *appCredentialManager; + +/** @property notificationManager + @brief The manager for remote notifications used by phone number auth. + */ +@property(nonatomic, strong, readonly) FIRAuthNotificationManager *notificationManager; + +/** @fn initWithAPIKey:appName: + @brief Designated initializer. + @param APIKey The Google Developers Console API key for making requests from your app. + @param appName The name property of the previously created @c FIRApp instance. + */ +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey + appName:(NSString *)appName NS_DESIGNATED_INITIALIZER; + +/** @fn notifyListenersOfAuthStateChange + @brief Posts the @c FIRAuthStateDidChangeNotification notification. + @remarks Called by @c FIRUser when token changes occur. + @param user The user whose tokens changed. + @param token The new access token associated with the user. + */ +- (void)notifyListenersOfAuthStateChangeWithUser:(nullable FIRUser *)user + token:(nullable NSString *)token; + +/** @fn updateKeychainWithUser:error: + @brief Updates the keychain for the given user. + @param user The user to be updated. + @param error The error caused the method to fail if the method returns NO. + @return Whether updating keychain has succeeded or not. + @remarks Called by @c FIRUser when user info or token changes occur. + */ +- (BOOL)updateKeychainWithUser:(FIRUser *)user error:(NSError *_Nullable *_Nullable)error; + +/** @fn internalSignInWithCredential:callback: + @brief Convenience method for @c internalSignInAndRetrieveDataWithCredential:callback: + This method doesn't return additional identity provider data. +*/ +- (void)internalSignInWithCredential:(FIRAuthCredential *)credential + callback:(FIRAuthResultCallback)callback; + +/** @fn internalSignInAndRetrieveDataWithCredential:callback: + @brief Asynchronously signs in Firebase with the given 3rd party credentials (e.g. a Facebook + login Access Token, a Google ID Token/Access Token pair, etc.) and returns additional + identity provider data. + @param credential The credential supplied by the IdP. + @param isReauthentication Indicates whether or not the current invocation originated from an + attempt to reauthenticate. + @param callback A block which is invoked when the sign in finishes (or is cancelled.) Invoked + asynchronously on the auth global work queue in the future. + @remarks This is the internal counterpart of this method, which uses a callback that does not + update the current user. + */ +- (void)internalSignInAndRetrieveDataWithCredential:(FIRAuthCredential *)credential + isReauthentication:(BOOL)isReauthentication + callback:(nullable FIRAuthDataResultCallback)callback; + +/** @fn signOutByForceWithUserID:error: + @brief Signs out the current user. + @param userID The ID of the user to force sign out. + @param error An optional out parameter for error results. + @return @YES when the sign out request was successful. @NO otherwise. + */ +- (BOOL)signOutByForceWithUserID:(NSString *)userID error:(NSError *_Nullable *_Nullable)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/Private/FIRUser_Internal.h b/Firebase/Auth/Source/Private/FIRUser_Internal.h new file mode 100644 index 0000000..1447607 --- /dev/null +++ b/Firebase/Auth/Source/Private/FIRUser_Internal.h @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#import "FIRUser.h" + +@class FIRAuth; + +NS_ASSUME_NONNULL_BEGIN + +/** @typedef FIRRetrieveUserCallback + @brief The type of block that is invoked when the construction of a user succeeds or fails. + @param user The user that was constructed, or nil if user construction failed. + @param error The error which occurred, or nil if the request was successful. + */ +typedef void(^FIRRetrieveUserCallback)(FIRUser *_Nullable user, NSError *_Nullable error); + +@interface FIRUser () <NSSecureCoding> + +/** @property rawAccessToken + @brief The cached access token. + @remarks This method is specifically for providing the access token to internal clients during + deserialization and sign-in events, and should not be used to retrieve the access token by + anyone else. + */ +@property(nonatomic, copy, readonly) NSString *rawAccessToken; + +/** @property auth + @brief A weak reference to a FIRAuth instance which is used to notify auth of token changes. + */ +@property(nonatomic, weak) FIRAuth *auth; + +/** @var accessTokenExpirationDate + @brief The expiration date of the cached access token. + */ +@property(nonatomic, copy, readonly) NSDate *accessTokenExpirationDate; + +/** @fn retrieveUserWithAPIKey:accessToken:accessTokenExpirationDate:refreshToken:callback: + @brief Constructs a user with Secure Token Service tokens, and obtains user details from the + getAccountInfo endpoint. + @param APIKey The client API key for making RPCs. + @param accessToken The Secure Token Service access token. + @param accessTokenExpirationDate The approximate expiration date of the access token. + @param refreshToken The Secure Token Service refresh token. + @param anonymous Whether or not the user is anonymous. + @param callback A block which is invoked when the construction succeeds or fails. Invoked + asynchronously on the auth global work queue in the future. + */ ++ (void)retrieveUserWithAPIKey:(NSString *)APIKey + accessToken:(NSString *)accessToken + accessTokenExpirationDate:(NSDate *)accessTokenExpirationDate + refreshToken:(NSString *)refreshToken + anonymous:(BOOL)anonymous + callback:(FIRRetrieveUserCallback)callback; + +/** @fn internalGetTokenForcingRefresh:callback: + @brief Retrieves the Firebase authentication token, possibly refreshing it if it has expired. + @param forceRefresh Forces a token refresh. Useful if the token becomes invalid for some reason + other than an expiration. + @param callback The block to invoke when the token is available. Invoked asynchronously on the + global work thread in the future. + */ +- (void)internalGetTokenForcingRefresh:(BOOL)forceRefresh + callback:(nonnull FIRAuthTokenCallback)callback; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRAuthBackend.h b/Firebase/Auth/Source/RPCs/FIRAuthBackend.h new file mode 100644 index 0000000..519a6e7 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRAuthBackend.h @@ -0,0 +1,496 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +@class FIRCreateAuthURIRequest; +@class FIRCreateAuthURIResponse; +@class FIRGetAccountInfoRequest; +@class FIRGetAccountInfoResponse; +@class FIRGetOOBConfirmationCodeRequest; +@class FIRGetOOBConfirmationCodeResponse; +@class FIRResetPasswordRequest; +@class FIRResetPasswordResponse; +@class FIRSecureTokenRequest; +@class FIRSecureTokenResponse; +@class FIRSetAccountInfoRequest; +@class FIRSetAccountInfoResponse; +@class FIRVerifyAssertionRequest; +@class FIRVerifyAssertionResponse; +@class FIRVerifyClientRequest; +@class FIRVerifyClientResponse; +@class FIRVerifyCustomTokenRequest; +@class FIRVerifyCustomTokenResponse; +@class FIRVerifyPasswordRequest; +@class FIRVerifyPasswordResponse; +@class FIRVerifyPhoneNumberRequest; +@class FIRVerifyPhoneNumberResponse; +@class FIRSendVerificationCodeRequest; +@class FIRSendVerificationCodeResponse; +@class FIRSignUpNewUserRequest; +@class FIRSignUpNewUserResponse; +@class FIRDeleteAccountRequest; +@class FIRDeleteAccountResponse; +@protocol FIRAuthBackendImplementation; +@protocol FIRAuthBackendRPCIssuer; + +NS_ASSUME_NONNULL_BEGIN + +/** @typedef FIRAuthBackendRPCIssuerCompletionHandler + @brief The type of block used to return the result of a call to an endpoint. + @param data The HTTP response body. + @param error The error which occurred, if any. + @remarks One of response or error will be non-nil. + */ +typedef void (^FIRAuthBackendRPCIssuerCompletionHandler)(NSData *_Nullable data, + NSError *_Nullable error); + +/** @typedef FIRCreateAuthURIResponseCallback + @brief The type of block used to return the result of a call to the createAuthURI + endpoint. + @param response The received response, if any. + @param error The error which occurred, if any. + @remarks One of response or error will be non-nil. + */ +typedef void (^FIRCreateAuthURIResponseCallback) + (FIRCreateAuthURIResponse *_Nullable response, NSError *_Nullable error); + +/** @typedef FIRGetAccountInfoResponseCallback + @brief The type of block used to return the result of a call to the getAccountInfo + endpoint. + @param response The received response, if any. + @param error The error which occurred, if any. + @remarks One of response or error will be non-nil. + */ +typedef void (^FIRGetAccountInfoResponseCallback) + (FIRGetAccountInfoResponse *_Nullable response, NSError *_Nullable error); + +/** @typedef FIRSetAccountInfoResponseCallback + @brief The type of block used to return the result of a call to the setAccountInfo + endpoint. + @param response The received response, if any. + @param error The error which occurred, if any. + @remarks One of response or error will be non-nil. + */ +typedef void (^FIRSetAccountInfoResponseCallback) + (FIRSetAccountInfoResponse *_Nullable response, NSError *_Nullable error); + +/** @typedef FIRSecureTokenResponseCallback + @brief The type of block used to return the result of a call to the token endpoint. + @param response The received response, if any. + @param error The error which occurred, if any. + @remarks One of response or error will be non-nil. + */ +typedef void (^FIRSecureTokenResponseCallback) + (FIRSecureTokenResponse *_Nullable response, NSError *_Nullable error); + +/** @typedef FIRVerifyAssertionResponseCallback + @brief The type of block used to return the result of a call to the verifyAssertion + endpoint. + @param response The received response, if any. + @param error The error which occurred, if any. + @remarks One of response or error will be non-nil. + */ +typedef void (^FIRVerifyAssertionResponseCallback) + (FIRVerifyAssertionResponse *_Nullable response, NSError *_Nullable error); + +/** @typedef FIRVerifyPasswordResponseCallback + @brief The type of block used to return the result of a call to the verifyPassword + endpoint. + @param response The received response, if any. + @param error The error which occurred, if any. + @remarks One of response or error will be non-nil. + */ +typedef void (^FIRVerifyPasswordResponseCallback) + (FIRVerifyPasswordResponse *_Nullable response, NSError *_Nullable error); + +/** @typedef FIRVerifyCustomTokenResponseCallback + @brief The type of block used to return the result of a call to the verifyCustomToken + endpoint. + @param response The received response, if any. + @param error The error which occurred, if any. + @remarks One of response or error will be non-nil. + */ +typedef void (^FIRVerifyCustomTokenResponseCallback) + (FIRVerifyCustomTokenResponse *_Nullable response, NSError *_Nullable error); + +/** @typedef FIRDeleteCallBack + @brief The type of block called when a request delete account has finished. + @param error The error which occurred, or nil if the request was successful. + */ +typedef void (^FIRDeleteCallBack)(NSError *_Nullable error); + +/** @typedef FIRGetOOBConfirmationCodeResponseCallback + @brief The type of block used to return the result of a call to the getOOBConfirmationCode + endpoint. + @param response The received response, if any. + @param error The error which occurred, if any. + @remarks One of response or error will be non-nil. + */ +typedef void (^FIRGetOOBConfirmationCodeResponseCallback) + (FIRGetOOBConfirmationCodeResponse *_Nullable response, NSError *_Nullable error); + +/** @typedef FIRSignupNewUserCallback + @brief The type of block used to return the result of a call to the signupNewUser endpoint. + @param response The received response, if any. + @param error The error which occurred, if any. + @remarks One of response or error will be non-nil. + */ +typedef void (^FIRSignupNewUserCallback) + (FIRSignUpNewUserResponse *_Nullable response, NSError *_Nullable error); + +/** @typedef FIRResetPasswordCallback + @brief The type of block used to return the result of a call to the resetPassword endpoint. + @param response The received response, if any. + @param error The error which occurred, if any. + @remarks One of response or error will be non-nil. + */ +typedef void (^FIRResetPasswordCallback) + (FIRResetPasswordResponse *_Nullable response, NSError *_Nullable error); + +/** @typedef FIRSendVerificationCodeResponseCallback + @brief The type of block used to return the result of a call to the sendVerificationCode + endpoint. + @param response The received response, if any. + @param error The error which occurred, if any. + @remarks One of response or error will be non-nil. + */ +typedef void (^FIRSendVerificationCodeResponseCallback) + (FIRSendVerificationCodeResponse *_Nullable response, NSError *_Nullable error); + +/** @typedef FIRVerifyPhoneNumberResponseCallback + @brief The type of block used to return the result of a call to the verifyPhoneNumber endpoint. + @param response The received response, if any. + @param error The error which occurred, if any. + @remarks One of response or error will be non-nil. + */ +typedef void (^FIRVerifyPhoneNumberResponseCallback) + (FIRVerifyPhoneNumberResponse *_Nullable response, NSError *_Nullable error); + +/** @typedef FIRVerifyClientResponseCallback + @brief The type of block used to return the result of a call to the verifyClient endpoint. + @param response The received response, if any. + @param error The error which occurred, if any. + @remarks One of response or error will be non-nil. + */ +typedef void (^FIRVerifyClientResponseCallback) + (FIRVerifyClientResponse *_Nullable response, NSError *_Nullable error); + +/** @class FIRAuthBackend + @brief Simple static class with methods representing the backend RPCs. + @remarks All callback blocks passed as method parameters are invoked asynchronously on the + global work queue in the future. See + https://github.com/firebase/firebase-ios-sdk/tree/master/Firebase/Auth/Docs/threading.ml + */ +@interface FIRAuthBackend : NSObject + +/** @fn setBackendImplementation: + @brief Changes the default backend implementation to something else. + @param backendImplementation The backend implementation to use. + @remarks This is not, generally, safe to call in a scenario where other backend requests may + be occuring. This is specifically to help mock the backend for testing purposes. + */ ++ (void)setBackendImplementation:(id<FIRAuthBackendImplementation>)backendImplementation; + +/** @fn setDefaultBackendImplementationWithRPCIssuer: + @brief Uses the default backend implementation, but with a custom RPC issuer. + @param RPCIssuer The RPC issuer to use. If @c nil, will use the default implementation. + @remarks This is not, generally, safe to call in a scenario where other backend requests may + be occuring. This is specifically to help test the backend interfaces (requests, responses, + and shared FIRAuthBackend logic.) + */ ++ (void)setDefaultBackendImplementationWithRPCIssuer: + (nullable id<FIRAuthBackendRPCIssuer>)RPCIssuer; + +/** @fn createAuthURI:callback: + @brief Calls the createAuthURI endpoint, which is responsible for creating the URI used by the + IdP to authenticate the user. + @param request The request parameters. + @param callback The callback. + */ ++ (void)createAuthURI:(FIRCreateAuthURIRequest *)request + callback:(FIRCreateAuthURIResponseCallback)callback; + +/** @fn getAccountInfo:callback: + @brief Calls the getAccountInfo endpoint, which returns account info for a given account. + @param request The request parameters. + @param callback The callback. + */ ++ (void)getAccountInfo:(FIRGetAccountInfoRequest *)request + callback:(FIRGetAccountInfoResponseCallback)callback; + +/** @fn setAccountInfo:callback: + @brief Calls the setAccountInfo endpoint, which is responsible for setting account info for a + user, for example, to sign up a new user with email and password. + @param request The request parameters. + @param callback The callback. + */ ++ (void)setAccountInfo:(FIRSetAccountInfoRequest *)request + callback:(FIRSetAccountInfoResponseCallback)callback; + +/** @fn verifyAssertion:callback: + @brief Calls the verifyAssertion endpoint, which is responsible for authenticating a + user who has IDP-related credentials (an ID Token, an Access Token, etc.) + @param request The request parameters. + @param callback The callback. + */ ++ (void)verifyAssertion:(FIRVerifyAssertionRequest *)request + callback:(FIRVerifyAssertionResponseCallback)callback; + +/** @fn verifyCustomToken:callback: + @brief Calls the verifyCustomToken endpoint, which is responsible for authenticating a + user who has BYOAuth credentials (a self-signed token using their BYOAuth private key.) + @param request The request parameters. + @param callback The callback. + */ ++ (void)verifyCustomToken:(FIRVerifyCustomTokenRequest *)request + callback:(FIRVerifyCustomTokenResponseCallback)callback; + +/** @fn verifyPassword:callback: + @brief Calls the verifyPassword endpoint, which is responsible for authenticating a + user who has email and password credentials. + @param request The request parameters. + @param callback The callback. + */ ++ (void)verifyPassword:(FIRVerifyPasswordRequest *)request + callback:(FIRVerifyPasswordResponseCallback)callback; + +/** @fn secureToken:callback: + @brief Calls the token endpoint, which is responsible for performing STS token exchanges and + token refreshes. + @param request The request parameters. + @param callback The callback. + */ ++ (void)secureToken:(FIRSecureTokenRequest *)request + callback:(FIRSecureTokenResponseCallback)callback; + +/** @fn getOOBConfirmationCode:callback: + @brief Calls the getOOBConfirmationCode endpoint, which is responsible for sending email change + request emails, and password reset emails. + @param request The request parameters. + @param callback The callback. + */ ++ (void)getOOBConfirmationCode:(FIRGetOOBConfirmationCodeRequest *)request + callback:(FIRGetOOBConfirmationCodeResponseCallback)callback; + +/** @fn signUpNewUser: + @brief Calls the signUpNewUser endpoint, which is responsible anonymously signing up a user + or signing in a user anonymously. + @param request The request parameters. + @param callback The callback. + */ ++ (void)signUpNewUser:(FIRSignUpNewUserRequest *)request + callback:(FIRSignupNewUserCallback)callback; + +/** @fn resetPassword:callback + @brief Calls the resetPassword endpoint, which is responsible for resetting a user's password + given an OOB code and new password. + @param request The request parameters. + @param callback The callback. + */ ++ (void)resetPassword:(FIRResetPasswordRequest *)request + callback:(FIRResetPasswordCallback)callback; + +/** @fn deleteAccount: + @brief Calls the DeleteAccount endpoint, which is responsible for deleting a user. + @param request The request parameters. + @param callback The callback. + */ ++ (void)deleteAccount:(FIRDeleteAccountRequest *)request + callback:(FIRDeleteCallBack)callback; + +/** @fn sendVerificationCode:callback: + @brief Calls the sendVerificationCode endpoint, which is responsible for sending the + verification code to a phone number specified in the request parameters. + @param request The request parameters. + @param callback The callback. + */ ++ (void)sendVerificationCode:(FIRSendVerificationCodeRequest *)request + callback:(FIRSendVerificationCodeResponseCallback)callback; + +/** @fn verifyPhoneNumber:callback: + @brief Calls the verifyPhoneNumber endpoint, which is responsible for sending the verification + code to a phone number specified in the request parameters. + @param request The request parameters. + @param callback The callback. + */ ++ (void)verifyPhoneNumber:(FIRVerifyPhoneNumberRequest *)request + callback:(FIRVerifyPhoneNumberResponseCallback)callback; + +/** @fn verifyClient:callback: + @brief Calls the verifyClient endpoint, which is responsible for sending the silent push + notification used for app validation to the device provided in the request parameters. + @param request The request parameters. + @param callback The callback. + */ ++ (void)verifyClient:(FIRVerifyClientRequest *)request + callback:(FIRVerifyClientResponseCallback)callback; + +@end + +/** @protocol FIRAuthBackendRPCIssuer + @brief Used to make FIRAuthBackend + */ +@protocol FIRAuthBackendRPCIssuer <NSObject> + +/** @fn asyncPostToURL:body:contentType:completionHandler: + @brief Asynchronously sends a POST request. + @param URL URL of the request. + @param body Request body. + @param contentType Content type of the body. + @param handler provided that handles POST response. Invoked asynchronously on the auth global + work queue in the future. + */ +- (void)asyncPostToURL:(NSURL *)URL + body:(NSData *)body + contentType:(NSString *)contentType + completionHandler:(FIRAuthBackendRPCIssuerCompletionHandler)handler; + +@end + +/** @protocol FIRAuthBackendImplementation + @brief Used to make FIRAuthBackend provide a layer of indirection to an actual RPC-based backend + or a mock backend. + */ +@protocol FIRAuthBackendImplementation <NSObject> + +/** @fn createAuthURI:callback: + @brief Calls the createAuthURI endpoint, which is responsible for creating the URI used by the + IdP to authenticate the user. + @param request The request parameters. + @param callback The callback. + */ +- (void)createAuthURI:(FIRCreateAuthURIRequest *)request + callback:(FIRCreateAuthURIResponseCallback)callback; + +/** @fn getAccountInfo:callback: + @brief Calls the getAccountInfo endpoint, which returns account info for a given account. + @param request The request parameters. + @param callback The callback. + */ +- (void)getAccountInfo:(FIRGetAccountInfoRequest *)request + callback:(FIRGetAccountInfoResponseCallback)callback; + +/** @fn setAccountInfo:callback: + @brief Calls the setAccountInfo endpoint, which is responsible for setting account info for a + user, for example, to sign up a new user with email and password. + @param request The request parameters. + @param callback The callback. + */ +- (void)setAccountInfo:(FIRSetAccountInfoRequest *)request + callback:(FIRSetAccountInfoResponseCallback)callback; + +/** @fn verifyAssertion:callback: + @brief Calls the verifyAssertion endpoint, which is responsible for authenticating a + user who has IDP-related credentials (an ID Token, an Access Token, etc.) + @param request The request parameters. + @param callback The callback. + */ +- (void)verifyAssertion:(FIRVerifyAssertionRequest *)request + callback:(FIRVerifyAssertionResponseCallback)callback; + +/** @fn verifyCustomToken:callback: + @brief Calls the verifyCustomToken endpoint, which is responsible for authenticating a + user who has BYOAuth credentials (a self-signed token using their BYOAuth private key.) + @param request The request parameters. + @param callback The callback. + */ +- (void)verifyCustomToken:(FIRVerifyCustomTokenRequest *)request + callback:(FIRVerifyCustomTokenResponseCallback)callback; + +/** @fn verifyPassword:callback: + @brief Calls the verifyPassword endpoint, which is responsible for authenticating a + user who has email and password credentials. + @param request The request parameters. + @param callback The callback. + */ +- (void)verifyPassword:(FIRVerifyPasswordRequest *)request + callback:(FIRVerifyPasswordResponseCallback)callback; + +/** @fn secureToken:callback: + @brief Calls the token endpoint, which is responsible for performing STS token exchanges and + token refreshes. + @param request The request parameters. + @param callback The callback. + */ +- (void)secureToken:(FIRSecureTokenRequest *)request + callback:(FIRSecureTokenResponseCallback)callback; + +/** @fn getOOBConfirmationCode:callback: + @brief Calls the getOOBConfirmationCode endpoint, which is responsible for sending email change + request emails, and password reset emails. + @param request The request parameters. + @param callback The callback. + */ +- (void)getOOBConfirmationCode:(FIRGetOOBConfirmationCodeRequest *)request + callback:(FIRGetOOBConfirmationCodeResponseCallback)callback; + +/** @fn signUpNewUser: + @brief Calls the signUpNewUser endpoint, which is responsible anonymously signing up a user + or signing in a user anonymously. + @param request The request parameters. + @param callback The callback. + */ +- (void)signUpNewUser:(FIRSignUpNewUserRequest *)request + callback:(FIRSignupNewUserCallback)callback; + +/** @fn deleteAccount: + @brief Calls the DeleteAccount endpoint, which is responsible for deleting a user. + @param request The request parameters. + @param callback The callback. + */ +- (void)deleteAccount:(FIRDeleteAccountRequest *)request + callback:(FIRDeleteCallBack)callback; + +/** @fn sendVerificationCode:callback: + @brief Calls the sendVerificationCode endpoint, which is responsible for sending the + verification code to a phone number specified in the request parameters. + @param request The request parameters. + @param callback The callback. + */ +- (void)sendVerificationCode:(FIRSendVerificationCodeRequest *)request + callback:(FIRSendVerificationCodeResponseCallback)callback; + +/** @fn verifyPhoneNumber:callback: + @brief Calls the verifyPhoneNumber endpoint, which is responsible for sending the verification + code to a phone number specified in the request parameters. + @param request The request parameters. + @param callback The callback. + */ +- (void)verifyPhoneNumber:(FIRVerifyPhoneNumberRequest *)request + callback:(FIRVerifyPhoneNumberResponseCallback)callback; + +/** @fn verifyClient:callback: + @brief Calls the verifyClient endpoint, which is responsible for sending the silent push + notification used for app validation to the device provided in the request parameters. + @param request The request parameters. + @param callback The callback. + */ +- (void)verifyClient:(FIRVerifyClientRequest *)request + callback:(FIRVerifyClientResponseCallback)callback; + +/** @fn resetPassword:callback + @brief Calls the resetPassword endpoint, which is responsible for resetting a user's password + given an OOB code and new password. + @param request The request parameters. + @param callback The callback. + */ +- (void)resetPassword:(FIRResetPasswordRequest *)request + callback:(FIRResetPasswordCallback)callback; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m new file mode 100644 index 0000000..3efc602 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m @@ -0,0 +1,943 @@ +/* + * 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. + */ + +#import "FIRAuthBackend.h" + +#import "../AuthProviders/Phone/FIRPhoneAuthCredential_Internal.h" +#import "../AuthProviders/Phone/FIRPhoneAuthProvider.h" +#import "../Private/FIRAuthErrorUtils.h" +#import "../Private/FIRAuthGlobalWorkQueue.h" +#import "FirebaseAuth.h" +#import "FIRAuthRPCRequest.h" +#import "FIRAuthRPCResponse.h" +#import "FIRCreateAuthURIRequest.h" +#import "FIRCreateAuthURIResponse.h" +#import "FIRDeleteAccountRequest.h" +#import "FIRDeleteAccountResponse.h" +#import "FIRGetAccountInfoRequest.h" +#import "FIRGetAccountInfoResponse.h" +#import "FIRGetOOBConfirmationCodeRequest.h" +#import "FIRGetOOBConfirmationCodeResponse.h" +#import "FIRResetPasswordRequest.h" +#import "FIRResetPasswordResponse.h" +#import "FIRSendVerificationCodeRequest.h" +#import "FIRSendVerificationCodeResponse.h" +#import "FIRSecureTokenRequest.h" +#import "FIRSecureTokenResponse.h" +#import "FIRSetAccountInfoRequest.h" +#import "FIRSetAccountInfoResponse.h" +#import "FIRSignUpNewUserRequest.h" +#import "FIRSignUpNewUserResponse.h" +#import "FIRVerifyAssertionRequest.h" +#import "FIRVerifyAssertionResponse.h" +#import "FIRVerifyClientRequest.h" +#import "FIRVerifyClientResponse.h" +#import "FIRVerifyCustomTokenRequest.h" +#import "FIRVerifyCustomTokenResponse.h" +#import "FIRVerifyPasswordRequest.h" +#import "FIRVerifyPasswordResponse.h" +#import "FIRVerifyPhoneNumberRequest.h" +#import "FIRVerifyPhoneNumberResponse.h" +#import <GTMSessionFetcher/GTMSessionFetcher.h> +#import <GTMSessionFetcher/GTMSessionFetcherService.h> + +/** @var kIosBundleIdentifierHeader + @brief HTTP header name for iOS bundle ID. + */ +static NSString *const kIosBundleIdentifierHeader = @"X-Ios-Bundle-Identifier"; + +/** @var kJSONContentType + @brief The value of the HTTP content-type header for JSON payloads. + */ +static NSString *const kJSONContentType = @"application/json"; + +/** @var kErrorDataKey + @brief Key for error data in NSError returned by @c GTMSessionFetcher. + */ +static NSString * const kErrorDataKey = @"data"; + +/** @var kErrorKey + @brief The key for the "error" value in JSON responses from the server. + */ +static NSString *const kErrorKey = @"error"; + +/** @var kErrorsKey + @brief The key for the "errors" value in JSON responses from the server. + */ +static NSString *const kErrorsKey = @"errors"; + +/** @var kReasonKey + @brief The key for the "reason" value in JSON responses from the server. + */ +static NSString *const kReasonKey = @"reason"; + +/** @var kInvalidKeyReasonValue + @brief The value for the "reason" key indicating an invalid API Key was received by the server. + */ +static NSString *const kInvalidKeyReasonValue = @"keyInvalid"; + +/** @var kAppNotAuthorizedReasonValue + @brief The value for the "reason" key indicating the App is not authorized to use Firebase + Authentication. + */ +static NSString *const kAppNotAuthorizedReasonValue = @"ipRefererBlocked"; + +/** @var kErrorMessageKey + @brief The key for an error's "message" value in JSON responses from the server. + */ +static NSString *const kErrorMessageKey = @"message"; + +/** @var kUserNotFoundErrorMessage + @brief This is the error message returned when the user is not found, which means the user + account has been deleted given the token was once valid. + */ +static NSString *const kUserNotFoundErrorMessage = @"USER_NOT_FOUND"; + +/** @var kUserDeletedErrorMessage + @brief This is the error message the server will respond with if the user entered an invalid + email address. + */ +static NSString *const kUserDeletedErrorMessage = @"EMAIL_NOT_FOUND"; + +/** @var kInvalidLocalIDErrorMessage + @brief This is the error message the server responds with if the user local id in the id token + does not exit. + */ +static NSString *const kInvalidLocalIDErrorMessage = @"INVALID_LOCAL_ID"; + +/** @var kUserTokenExpiredErrorMessage + @brief The error returned by the server if the token issue time is older than the account's + valid_since time. + */ +static NSString *const kUserTokenExpiredErrorMessage = @"TOKEN_EXPIRED"; + +/** @var kTooManyRequestsErrorMessage + @brief This is the error message the server will respond with if too many requests were made to + a server method. + */ +static NSString *const kTooManyRequestsErrorMessage = @"TOO_MANY_ATTEMPTS_TRY_LATER"; + +/** @var kInvalidTokenCustomErrorMessage + @brief This is the error message the server will respond with if there is a validation error + with the custom token. + */ +static NSString *const kInvalidCustomTokenErrorMessage = @"INVALID_CUSTOM_TOKEN"; + +/** @var kCustomTokenMismatch + @brief This is the error message the server will respond with if the service account and API key + belong to different projects. + */ +static NSString *const kCustomTokenMismatch = @"CREDENTIAL_MISMATCH"; + +/** @var kInvalidCredentialErrorMessage + @brief This is the error message the server responds with if the IDP token or requestUri is + invalid. + */ +static NSString *const kInvalidCredentialErrorMessage = @"INVALID_IDP_RESPONSE"; + +/** @var kUserDisabledErrorMessage + @brief The error returned by the server if the user account is diabled. + */ +static NSString *const kUserDisabledErrorMessage = @"USER_DISABLED"; + +/** @var kOperationNotAllowedErrorMessage + @brief This is the error message the server will respond with if Admin disables IDP specified by + provider. + */ +static NSString *const kOperationNotAllowedErrorMessage = @"OPERATION_NOT_ALLOWED"; + +/** @var kPasswordLoginDisabledErrorMessage + @brief This is the error message the server responds with if password login is disabled. + */ +static NSString *const kPasswordLoginDisabledErrorMessage = @"PASSWORD_LOGIN_DISABLED"; + +/** @var kEmailAlreadyInUseErrorMessage + @brief This is the error message the server responds with if the email address already exists. + */ +static NSString *const kEmailAlreadyInUseErrorMessage = @"EMAIL_EXISTS"; + +/** @var kInvalidEmailErrorMessage + @brief The error returned by the server if the email is invalid. + */ +static NSString *const kInvalidEmailErrorMessage = @"INVALID_EMAIL"; + +/** @var kInvalidIdentifierErrorMessage + @brief The error returned by the server if the identifier is invalid. + */ +static NSString *const kInvalidIdentifierErrorMessage = @"INVALID_IDENTIFIER"; + +/** @var kWrongPasswordErrorMessage + @brief This is the error message the server will respond with if the user entered a wrong + password. + */ +static NSString *const kWrongPasswordErrorMessage = @"INVALID_PASSWORD"; + +/** @var kCredentialTooOldErrorMessage + @brief This is the error message the server responds with if account change is attempted 5 + minutes after signing in. + */ +static NSString *const kCredentialTooOldErrorMessage = @"CREDENTIAL_TOO_OLD_LOGIN_AGAIN"; + +/** @var kFederatedUserIDAlreadyLinkedMessage + @brief This is the error message the server will respond with if the federated user ID has been + already linked with another account. + */ +static NSString *const kFederatedUserIDAlreadyLinkedMessage = @"FEDERATED_USER_ID_ALREADY_LINKED"; + +/** @var kInvalidUserTokenErrorMessage + @brief This is the error message the server responds with if user's saved auth credential is + invalid, and the user needs to sign in again. + */ +static NSString *const kInvalidUserTokenErrorMessage = @"INVALID_ID_TOKEN"; + +/** @var kWeakPasswordErrorMessagePrefix + @brief This is the prefix for the error message the server responds with if user's new password + to be set is too weak. + */ +static NSString *const kWeakPasswordErrorMessagePrefix = @"WEAK_PASSWORD"; + +/** @var kExpiredActionCodeErrorMessage + @brief This is the error message the server will respond with if the action code is expired. + */ +static NSString *const kExpiredActionCodeErrorMessage = @"EXPIRED_OOB_CODE"; + +/** @var kInvalidActionCodeErrorMessage + @brief This is the error message the server will respond with if the action code is invalid. + */ +static NSString *const kInvalidActionCodeErrorMessage = @"INVALID_OOB_CODE"; + +/** @var kInvalidSenderEmailErrorMessage + @brief This is the error message the server will respond with if the sender email is invalid + during a "send password reset email" attempt. + */ +static NSString *const kInvalidSenderEmailErrorMessage = @"INVALID_SENDER"; + +/** @var kInvalidMessagePayloadErrorMessage + @brief This is the error message the server will respond with if there are invalid parameters in + the payload during a "send password reset email" attempt. + */ +static NSString *const kInvalidMessagePayloadErrorMessage = @"INVALID_MESSAGE_PAYLOAD"; + +/** @var kInvalidRecipientEmailErrorMessage + @brief This is the error message the server will respond with if the recipient email is invalid. + */ +static NSString *const kInvalidRecipientEmailErrorMessage = @"INVALID_RECIPIENT_EMAIL"; + +/** @var kInvalidPhoneNumberErrorMessage + @brief This is the error message the server will respond with if an incorrectly formatted phone + number is provided. + */ +static NSString *const kInvalidPhoneNumberErrorMessage = @"INVALID_PHONE_NUMBER"; + +/** @var kInvalidVerificationCodeErrorMessage + @brief This is the error message the server will respond with if an invalid verification code is + provided. + */ +static NSString *const kInvalidVerificationCodeErrorMessage = @"INVALID_CODE"; + +/** @var kInvalidSessionInfoErrorMessage + @brief This is the error message the server will respond with if an invalid session info + (verification ID) is provided. + */ +static NSString *const kInvalidSessionInfoErrorMessage = @"INVALID_SESSION_INFO"; + +/** @var kSessionExpiredErrorMessage + @brief This is the error message the server will respond with if the SMS code has expired before + it is used. + */ +static NSString *const kSessionExpiredErrorMessage = @"SESSION_EXPIRED"; + +/** @var kMissingAppCredentialErrorMessage + @brief This is the error message the server will respond with if the APNS token is missing in a + verifyClient request. + */ +static NSString *const kMissingAppCredentialErrorMessage = @"MISSING_APP_CREDENTIAL"; + +/** @var kMissingAppCredentialErrorMessage + @brief This is the error message the server will respond with if the APNS token in a + verifyClient request is invalid. + */ +static NSString *const kInvalidAppCredentialErrorMessage = @"INVALID_APP_CREDENTIAL"; + +/** @var kQuoutaExceededErrorMessage + @brief This is the error message the server will respond with if the quota for SMS text messages + has been exceeded for the project. + */ +static NSString *const kQuoutaExceededErrorMessage = @"QUOTA_EXCEEDED"; + +/** @var kAppNotVerifiedErrorMessage + @brief This is the error message the server will respond with if Firebase could not verify the + app during a phone authentication flow. + */ +static NSString *const kAppNotVerifiedErrorMessage = @"APP_NOT_VERIFIED"; + +/** @var gBackendImplementation + @brief The singleton FIRAuthBackendImplementation instance to use. + */ +static id<FIRAuthBackendImplementation> gBackendImplementation; + +/** @class FIRAuthBackendRPCImplementation + @brief The default RPC-based backend implementation. + */ +@interface FIRAuthBackendRPCImplementation : NSObject <FIRAuthBackendImplementation> + +/** @property RPCIssuer + @brief An instance of FIRAuthBackendRPCIssuer for making RPC requests. Allows the RPC + requests/responses to be easily faked. + */ +@property(nonatomic, strong) id<FIRAuthBackendRPCIssuer> RPCIssuer; + +@end + +@implementation FIRAuthBackend + ++ (id<FIRAuthBackendImplementation>)implementation { + if (!gBackendImplementation) { + gBackendImplementation = [[FIRAuthBackendRPCImplementation alloc] init]; + } + return gBackendImplementation; +} + ++ (void)setBackendImplementation:(id<FIRAuthBackendImplementation>)backendImplementation { + gBackendImplementation = backendImplementation; +} + ++ (void)setDefaultBackendImplementationWithRPCIssuer: + (nullable id<FIRAuthBackendRPCIssuer>)RPCIssuer { + FIRAuthBackendRPCImplementation *defaultImplementation = + [[FIRAuthBackendRPCImplementation alloc] init]; + if (RPCIssuer) { + defaultImplementation.RPCIssuer = RPCIssuer; + } + gBackendImplementation = defaultImplementation; +} + ++ (void)createAuthURI:(FIRCreateAuthURIRequest *)request + callback:(FIRCreateAuthURIResponseCallback)callback { + [[self implementation] createAuthURI:request callback:callback]; +} + ++ (void)getAccountInfo:(FIRGetAccountInfoRequest *)request + callback:(FIRGetAccountInfoResponseCallback)callback { + [[self implementation] getAccountInfo:request callback:callback]; +} + ++ (void)setAccountInfo:(FIRSetAccountInfoRequest *)request + callback:(FIRSetAccountInfoResponseCallback)callback { + [[self implementation] setAccountInfo:request callback:callback]; +} + ++ (void)verifyAssertion:(FIRVerifyAssertionRequest *)request + callback:(FIRVerifyAssertionResponseCallback)callback { + [[self implementation] verifyAssertion:request callback:callback]; +} + ++ (void)verifyCustomToken:(FIRVerifyCustomTokenRequest *)request + callback:(FIRVerifyCustomTokenResponseCallback)callback { + [[self implementation] verifyCustomToken:request callback:callback]; +} + ++ (void)verifyPassword:(FIRVerifyPasswordRequest *)request + callback:(FIRVerifyPasswordResponseCallback)callback { + [[self implementation] verifyPassword:request callback:callback]; +} + ++ (void)secureToken:(FIRSecureTokenRequest *)request + callback:(FIRSecureTokenResponseCallback)callback { + [[self implementation] secureToken:request callback:callback]; +} + ++ (void)getOOBConfirmationCode:(FIRGetOOBConfirmationCodeRequest *)request + callback:(FIRGetOOBConfirmationCodeResponseCallback)callback { + [[self implementation] getOOBConfirmationCode:request callback:callback]; +} + ++ (void)signUpNewUser:(FIRSignUpNewUserRequest *)request + callback:(FIRSignupNewUserCallback)callback { + [[self implementation] signUpNewUser:request callback:callback]; +} + ++ (void)deleteAccount:(FIRDeleteAccountRequest *)request callback:(FIRDeleteCallBack)callback { + [[self implementation] deleteAccount:request callback:callback]; +} + ++ (void)sendVerificationCode:(FIRSendVerificationCodeRequest *)request + callback:(FIRSendVerificationCodeResponseCallback)callback { + [[self implementation] sendVerificationCode:request callback:callback]; +} + ++ (void)verifyPhoneNumber:(FIRVerifyPhoneNumberRequest *)request + callback:(FIRVerifyPhoneNumberResponseCallback)callback { + [[self implementation] verifyPhoneNumber:request callback:callback]; +} + ++ (void)verifyClient:(id)request callback:(FIRVerifyClientResponseCallback)callback { + [[self implementation] verifyClient:request callback:callback]; +} + ++ (void)resetPassword:(FIRResetPasswordRequest *)request + callback:(FIRResetPasswordCallback)callback { + [[self implementation] resetPassword:request callback:callback]; +} + +@end + +@interface FIRAuthBackendRPCIssuerImplementation : NSObject <FIRAuthBackendRPCIssuer> +@end + +@implementation FIRAuthBackendRPCIssuerImplementation { + /** @var The session fetcher service. + */ + GTMSessionFetcherService *_fetcherService; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _fetcherService = [[GTMSessionFetcherService alloc] init]; + _fetcherService.userAgent = [NSString stringWithFormat:@"FirebaseAuth.iOS/%s %@", + FirebaseAuthVersionString, GTMFetcherStandardUserAgentString(nil)]; + _fetcherService.callbackQueue = FIRAuthGlobalWorkQueue(); + } + return self; +} + +- (void)asyncPostToURL:(NSURL *)URL + body:(NSData *)body + contentType:(NSString *)contentType + completionHandler:(void (^)(NSData *_Nullable, NSError *_Nullable))handler { + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; + [request setValue:contentType forHTTPHeaderField:@"Content-Type"]; + NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier]; + [request setValue:bundleID forHTTPHeaderField:kIosBundleIdentifierHeader]; + + NSArray<NSString *> *preferredLocalizations = [NSBundle mainBundle].preferredLocalizations; + if (preferredLocalizations.count) { + NSString *acceptLanguage = preferredLocalizations.firstObject; + [request setValue:acceptLanguage forHTTPHeaderField:@"Accept-Language"]; + } + + GTMSessionFetcher* fetcher = [_fetcherService fetcherWithRequest:request]; + fetcher.bodyData = body; + [fetcher beginFetchWithCompletionHandler:handler]; +} + +@end + +@implementation FIRAuthBackendRPCImplementation + +- (instancetype)init { + self = [super init]; + if (self) { + _RPCIssuer = [[FIRAuthBackendRPCIssuerImplementation alloc] init]; + } + return self; +} + +- (void)createAuthURI:(FIRCreateAuthURIRequest *)request + callback:(FIRCreateAuthURIResponseCallback)callback { + FIRCreateAuthURIResponse *response = [[FIRCreateAuthURIResponse alloc] init]; + [self postWithRequest:request response:response callback:^(NSError *error) { + if (error) { + callback(nil, error); + } else { + callback(response, nil); + } + }]; +} + +- (void)getAccountInfo:(FIRGetAccountInfoRequest *)request + callback:(FIRGetAccountInfoResponseCallback)callback { + FIRGetAccountInfoResponse *response = [[FIRGetAccountInfoResponse alloc] init]; + [self postWithRequest:request response:response callback:^(NSError *error) { + if (error) { + callback(nil, error); + } else { + callback(response, nil); + } + }]; +} + +- (void)setAccountInfo:(FIRSetAccountInfoRequest *)request + callback:(FIRSetAccountInfoResponseCallback)callback { + FIRSetAccountInfoResponse *response = [[FIRSetAccountInfoResponse alloc] init]; + [self postWithRequest:request response:response callback:^(NSError *error) { + if (error) { + callback(nil, error); + } else { + callback(response, nil); + } + }]; +} + +- (void)verifyAssertion:(FIRVerifyAssertionRequest *)request + callback:(FIRVerifyAssertionResponseCallback)callback { + FIRVerifyAssertionResponse *response = [[FIRVerifyAssertionResponse alloc] init]; + [self postWithRequest:request response:response callback:^(NSError *error) { + if (error) { + callback(nil, error); + return; + } + callback(response, nil); + }]; +} + +- (void)verifyCustomToken:(FIRVerifyCustomTokenRequest *)request + callback:(FIRVerifyCustomTokenResponseCallback)callback { + FIRVerifyCustomTokenResponse *response = [[FIRVerifyCustomTokenResponse alloc] init]; + [self postWithRequest:request response:response callback:^(NSError *error) { + if (error) { + callback(nil, error); + } else { + callback(response, nil); + } + }]; +} + +- (void)verifyPassword:(FIRVerifyPasswordRequest *)request + callback:(FIRVerifyPasswordResponseCallback)callback { + FIRVerifyPasswordResponse *response = [[FIRVerifyPasswordResponse alloc] init]; + [self postWithRequest:request response:response callback:^(NSError *error) { + if (error) { + callback(nil, error); + } else { + callback(response, nil); + } + }]; +} + +- (void)secureToken:(FIRSecureTokenRequest *)request + callback:(FIRSecureTokenResponseCallback)callback { + FIRSecureTokenResponse *response = [[FIRSecureTokenResponse alloc] init]; + [self postWithRequest:request response:response callback:^(NSError *error) { + if (error) { + callback(nil, error); + } else { + callback(response, nil); + } + }]; +} + +- (void)getOOBConfirmationCode:(FIRGetOOBConfirmationCodeRequest *)request + callback:(FIRGetOOBConfirmationCodeResponseCallback)callback { + FIRGetOOBConfirmationCodeResponse *response = [[FIRGetOOBConfirmationCodeResponse alloc] init]; + [self postWithRequest:request response:response callback:^(NSError *error) { + if (error) { + callback(nil, error); + } else { + callback(response, nil); + } + }]; +} + +- (void)signUpNewUser:(FIRSignUpNewUserRequest *)request + callback:(FIRSignupNewUserCallback)callback{ + FIRSignUpNewUserResponse *response = [[FIRSignUpNewUserResponse alloc] init]; + [self postWithRequest:request response:response callback:^(NSError *error) { + if (error) { + callback(nil, error); + } else { + callback(response, nil); + } + }]; +} + +- (void)deleteAccount:(FIRDeleteAccountRequest *)request callback:(FIRDeleteCallBack)callback { + FIRDeleteAccountResponse *response = [[FIRDeleteAccountResponse alloc] init]; + [self postWithRequest:request response:response callback:callback]; +} + +- (void)sendVerificationCode:(FIRSendVerificationCodeRequest *)request + callback:(FIRSendVerificationCodeResponseCallback)callback { + FIRSendVerificationCodeResponse *response = [[FIRSendVerificationCodeResponse alloc] init]; + [self postWithRequest:request response:response callback:^(NSError *error) { + if (error) { + callback(nil, error); + } else { + callback(response, error); + } + }]; +} + +- (void)verifyPhoneNumber:(FIRVerifyPhoneNumberRequest *)request + callback:(FIRVerifyPhoneNumberResponseCallback)callback { + FIRVerifyPhoneNumberResponse *response = [[FIRVerifyPhoneNumberResponse alloc] init]; + [self postWithRequest:request response:response callback:^(NSError *error) { + if (error) { + callback(nil, error); + return; + } + // Check whether or not the successful response is actually the special case phone auth flow + // that returns a temporary proof and phone number. + if (response.phoneNumber.length && response.temporaryProof.length) { + FIRPhoneAuthCredential *credential = + [[FIRPhoneAuthCredential alloc] initWithTemporaryProof:response.temporaryProof + phoneNumber:response.phoneNumber + providerID:FIRPhoneAuthProviderID]; + callback(nil, + [FIRAuthErrorUtils credentialAlreadyInUseErrorWithMessage:nil + credential:credential]); + return; + } + callback(response, nil); + }]; +} + +- (void)verifyClient:(id)request callback:(FIRVerifyClientResponseCallback)callback { + FIRVerifyClientResponse *response = [[FIRVerifyClientResponse alloc] init]; + [self postWithRequest:request response:response callback:^(NSError *error) { + if (error) { + callback(nil, error); + return; + } + callback(response, nil); + }]; +} + +- (void)resetPassword:(FIRResetPasswordRequest *)request + callback:(FIRResetPasswordCallback)callback { + FIRResetPasswordResponse *response = [[FIRResetPasswordResponse alloc] init]; + [self postWithRequest:request response:response callback:^(NSError *error) { + if (error) { + callback(nil, error); + return; + } + callback(response, nil); + }]; +} + +#pragma mark - Generic RPC handling methods + +/** @fn postWithRequest:response:callback: + @brief Calls the RPC using HTTP POST. + @remarks Possible error responses: + @see FIRAuthInternalErrorCodeRPCRequestEncodingError + @see FIRAuthInternalErrorCodeJSONSerializationError + @see FIRAuthInternalErrorCodeNetworkError + @see FIRAuthInternalErrorCodeUnexpectedErrorResponse + @see FIRAuthInternalErrorCodeUnexpectedResponse + @see FIRAuthInternalErrorCodeRPCResponseDecodingError + @param request The request. + @param response The empty response to be filled. + @param callback The callback for both success and failure. + */ +- (void)postWithRequest:(id<FIRAuthRPCRequest>)request + response:(id<FIRAuthRPCResponse>)response + callback:(void (^)(NSError *error))callback { + NSError *error; + id postBody = [request unencodedHTTPRequestBodyWithError:&error]; + if (!postBody) { + callback([FIRAuthErrorUtils RPCRequestEncodingErrorWithUnderlyingError:error]); + return; + } + NSJSONWritingOptions JSONWritingOptions = 0; + #if DEBUG + JSONWritingOptions |= NSJSONWritingPrettyPrinted; + #endif + + NSData *bodyData; + if ([NSJSONSerialization isValidJSONObject:postBody]) { + bodyData = [NSJSONSerialization dataWithJSONObject:postBody + options:JSONWritingOptions + error:&error]; + if (!bodyData) { + // This is an untested case. This happens exclusively when there is an error in the framework + // implementation of dataWithJSONObject:options:error:. This shouldn't normally occur as + // isValidJSONObject: should return NO in any case we should encounter an error. + error = [FIRAuthErrorUtils JSONSerializationErrorWithUnderlyingError:error]; + } + } else { + error = [FIRAuthErrorUtils JSONSerializationErrorForUnencodableType]; + } + if (!bodyData) { + callback(error); + return; + } + + [_RPCIssuer asyncPostToURL:[request requestURL] + body:bodyData + contentType:kJSONContentType + completionHandler:^(NSData *data, NSError *error) { + // If there is an error with no body data at all, then this must be a network error. + if (error && !data) { + callback([FIRAuthErrorUtils networkErrorWithUnderlyingError:error]); + return; + } + + // Try to decode the HTTP response data which may contain either a successful response or error + // message. + NSError *jsonError; + NSDictionary * dictionary = + [NSJSONSerialization JSONObjectWithData:data + options:NSJSONReadingMutableLeaves + error:&jsonError]; + if (!dictionary) { + if (error) { + // We have an error, but we couldn't decode the body, so we have no additional information + // other than the raw response and the original NSError (the jsonError is infered by the + // error code (FIRAuthErrorCodeUnexpectedHTTPResponse, and is irrelevant.) + callback([FIRAuthErrorUtils unexpectedErrorResponseWithData:data underlyingError:error]); + } else { + // This is supposed to be a "successful" response, but we couldn't deserialize the body. + callback([FIRAuthErrorUtils unexpectedResponseWithData:data underlyingError:jsonError]); + } + return; + } + if (![dictionary isKindOfClass:[NSDictionary class]]) { + if (error) { + callback([FIRAuthErrorUtils unexpectedErrorResponseWithDeserializedResponse:dictionary]); + } else { + callback([FIRAuthErrorUtils unexpectedResponseWithDeserializedResponse:dictionary]); + } + return; + } + + // At this point we either have an error with successfully decoded details in the body, or we + // have a response which must pass further validation before we know it's truly successful. + // We deal with the case where we have an error with successfully decoded error details first: + if (error) { + NSDictionary *errorDictionary = dictionary[kErrorKey]; + if ([errorDictionary isKindOfClass:[NSDictionary class]]) { + id<NSObject> errorMessage = errorDictionary[kErrorMessageKey]; + if ([errorMessage isKindOfClass:[NSString class]]) { + NSString *errorMessageString = (NSString *)errorMessage; + + // Contruct client error. + NSError *clientError = [[self class] clientErrorWithServerErrorMessage:errorMessageString + errorDictionary:errorDictionary + response:response]; + if (clientError) { + callback(clientError); + return; + } + } + // Not a message we know, return the message directly. + if (errorMessage) { + NSError *unexpecterErrorResponse = + [FIRAuthErrorUtils unexpectedErrorResponseWithDeserializedResponse:errorDictionary]; + callback(unexpecterErrorResponse); + return; + } + } + // No error message at all, return the decoded response. + callback([FIRAuthErrorUtils unexpectedErrorResponseWithDeserializedResponse:dictionary]); + return; + } + + // Finally, we try to populate the response object with the JSON values. + if (![response setWithDictionary:dictionary error:&error]) { + callback([FIRAuthErrorUtils RPCResponseDecodingErrorWithDeserializedResponse:dictionary + underlyingError:error]); + return; + } + + // Success! The response object originally passed in can be used by the caller. + callback(nil); + }]; +} + +/** @fn clientErrorWithServerErrorMessage:errorDictionary: + @brief Translates known server errors to client errors. + @param serverErrorMessage The error message from the server. + @param errorDictionary The error part of the response from the server. + @param response The response from the server RPC. + @return A client error, if any. + */ ++ (nullable NSError *)clientErrorWithServerErrorMessage:(NSString *)serverErrorMessage + errorDictionary:(NSDictionary *)errorDictionary + response:(id<FIRAuthRPCResponse>)response { + NSString *shortErrorMessage = serverErrorMessage; + NSString *serverDetailErrorMessage; + NSRange colonRange = [serverErrorMessage rangeOfString:@":"]; + if (colonRange.location != NSNotFound) { + shortErrorMessage = [serverErrorMessage substringToIndex:colonRange.location]; + shortErrorMessage = + [shortErrorMessage stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + serverDetailErrorMessage = [serverErrorMessage substringFromIndex:colonRange.location + 1]; + serverDetailErrorMessage = [serverDetailErrorMessage stringByTrimmingCharactersInSet: + [NSCharacterSet whitespaceCharacterSet]]; + } + + // Delegate the responsibility for constructing the client error to the response object, + // if possible. + SEL clientErrorWithServerErrorMessageSelector = + @selector(clientErrorWithShortErrorMessage:detailErrorMessage:); + if ([response respondsToSelector:clientErrorWithServerErrorMessageSelector]) { + NSError *error = [response clientErrorWithShortErrorMessage:shortErrorMessage + detailErrorMessage:serverDetailErrorMessage]; + if (error) { + return error; + } + } + + if ([shortErrorMessage isEqualToString:kUserNotFoundErrorMessage]) { + return [FIRAuthErrorUtils userNotFoundErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kUserDeletedErrorMessage]) { + return [FIRAuthErrorUtils userNotFoundErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kInvalidLocalIDErrorMessage]) { + // This case shouldn't be necessary but it is for now: b/27908364 . + return [FIRAuthErrorUtils userNotFoundErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kUserTokenExpiredErrorMessage]) { + return [FIRAuthErrorUtils userTokenExpiredErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kTooManyRequestsErrorMessage]) { + return [FIRAuthErrorUtils tooManyRequestsErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kInvalidCustomTokenErrorMessage]) { + return [FIRAuthErrorUtils invalidCustomTokenErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kCustomTokenMismatch]) { + return [FIRAuthErrorUtils customTokenMistmatchErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kInvalidCredentialErrorMessage]) { + return [FIRAuthErrorUtils invalidCredentialErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kUserDisabledErrorMessage]) { + return [FIRAuthErrorUtils userDisabledErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kOperationNotAllowedErrorMessage]) { + return [FIRAuthErrorUtils operationNotAllowedErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kPasswordLoginDisabledErrorMessage]) { + return [FIRAuthErrorUtils operationNotAllowedErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kEmailAlreadyInUseErrorMessage]) { + return [FIRAuthErrorUtils emailAlreadyInUseErrorWithEmail:nil]; + } + + if ([shortErrorMessage isEqualToString:kInvalidEmailErrorMessage]) { + return [FIRAuthErrorUtils invalidEmailErrorWithMessage:serverDetailErrorMessage]; + } + + // "INVALID_IDENTIFIER" can be returned by createAuthURI RPC. Considering email addresses are + // currently the only identifiers, we surface the FIRAuthErrorCodeInvalidEmail error code in this + // case. + if ([shortErrorMessage isEqualToString:kInvalidIdentifierErrorMessage]) { + return [FIRAuthErrorUtils invalidEmailErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kWrongPasswordErrorMessage]) { + return [FIRAuthErrorUtils wrongPasswordErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kCredentialTooOldErrorMessage]) { + return [FIRAuthErrorUtils requiresRecentLoginErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kInvalidUserTokenErrorMessage]) { + return [FIRAuthErrorUtils invalidUserTokenErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kFederatedUserIDAlreadyLinkedMessage]) { + return [FIRAuthErrorUtils credentialAlreadyInUseErrorWithMessage:serverDetailErrorMessage + credential:nil]; + } + + if ([shortErrorMessage isEqualToString:kWeakPasswordErrorMessagePrefix]) { + return [FIRAuthErrorUtils weakPasswordErrorWithServerResponseReason:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kExpiredActionCodeErrorMessage]) { + return [FIRAuthErrorUtils expiredActionCodeErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kInvalidActionCodeErrorMessage]) { + return [FIRAuthErrorUtils invalidActionCodeErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kInvalidSenderEmailErrorMessage]) { + return [FIRAuthErrorUtils invalidSenderErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kInvalidMessagePayloadErrorMessage]) { + return [FIRAuthErrorUtils invalidMessagePayloadErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kInvalidRecipientEmailErrorMessage]) { + return [FIRAuthErrorUtils invalidRecipientEmailErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kInvalidPhoneNumberErrorMessage]) { + return [FIRAuthErrorUtils invalidPhoneNumberErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kInvalidSessionInfoErrorMessage]) { + return [FIRAuthErrorUtils invalidVerificationIDErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kInvalidVerificationCodeErrorMessage]) { + return [FIRAuthErrorUtils invalidVerificationCodeErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kSessionExpiredErrorMessage]) { + return [FIRAuthErrorUtils sessionExpiredErrorWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kMissingAppCredentialErrorMessage]) { + return [FIRAuthErrorUtils missingAppCredentialWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kInvalidAppCredentialErrorMessage]) { + return [FIRAuthErrorUtils invalidAppCredentialWithMessage:serverDetailErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kQuoutaExceededErrorMessage]) { + return [FIRAuthErrorUtils quotaExceededErrorWithMessage:serverErrorMessage]; + } + + if ([shortErrorMessage isEqualToString:kAppNotVerifiedErrorMessage]) { + return [FIRAuthErrorUtils appNotVerifiedErrorWithMessage:serverErrorMessage]; + } + + // In this case we handle an error that might be specified in the underlying errors dictionary, + // the error message in determined based on the @c reason key in the dictionary. + if (errorDictionary[kErrorsKey]) { + // Check for underlying error with reason = keyInvalid; + id underlyingErrors = errorDictionary[kErrorsKey]; + if ([underlyingErrors isKindOfClass:[NSArray class]]) { + NSArray *underlyingErrorsArray = (NSArray *)underlyingErrors; + for (id underlyingError in underlyingErrorsArray) { + if ([underlyingError isKindOfClass:[NSDictionary class]]) { + NSDictionary *underlyingErrorDictionary = (NSDictionary *)underlyingError; + NSString *reason = underlyingErrorDictionary[kReasonKey]; + if ([reason hasPrefix:kInvalidKeyReasonValue]) { + return [FIRAuthErrorUtils invalidAPIKeyError]; + } + if ([reason isEqualToString:kAppNotAuthorizedReasonValue]) { + return [FIRAuthErrorUtils appNotAuthorizedError]; + } + } + } + } + } + return nil; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRAuthRPCRequest.h b/Firebase/Auth/Source/RPCs/FIRAuthRPCRequest.h new file mode 100644 index 0000000..ddad3cb --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRAuthRPCRequest.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** @protocol FIRAuthRPCRequest + @brief The generic interface for an RPC request needed by @c FIRAuthBackend. + */ +@protocol FIRAuthRPCRequest <NSObject> + +/** @fn requestURL + @brief Gets the request's full URL. + */ +- (NSURL *)requestURL; + +/** @fn UnencodedHTTPRequestBodyWithError: + @brief Creates unencoded HTTP body representing the request. + @param error An out field for an error which occurred constructing the request. + @return The HTTP body data representing the request before any encoding, or nil for error. + */ +- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRAuthRPCResponse.h b/Firebase/Auth/Source/RPCs/FIRAuthRPCResponse.h new file mode 100644 index 0000000..c130f3f --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRAuthRPCResponse.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** @protocol FIRAuthRPCResponse + @brief The generic interface for an RPC response needed by @c FIRAuthBackend. + */ +@protocol FIRAuthRPCResponse <NSObject> + +/** @fn setFieldsWithDictionary:error: + @brief Sets the response instance from the decoded JSON response. + @param dictionary The dictionary decoded from HTTP JSON response. + @param error An out field for an error which occurred constructing the request. + @return Whether the operation was successful or not. + */ +- (BOOL)setWithDictionary:(NSDictionary *)dictionary + error:(NSError *_Nullable *_Nullable)error; + +@optional + +/** @fn clientErrorWithshortErrorMessage:detailErrorMessage + @brief This optional method allows response classes to create client errors given a short error + message and a detail error message from the server. + @param shortErrorMessage The short error message from the server. + @param detailErrorMessage The detailed error message from the server. + @return A client error, if any. + */ +- (nullable NSError *)clientErrorWithShortErrorMessage:(NSString *)shortErrorMessage + detailErrorMessage:(NSString *)detailErrorMessage; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIRequest.h b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIRequest.h new file mode 100644 index 0000000..bb28826 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIRequest.h @@ -0,0 +1,86 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCRequest.h" +#import "FIRIdentityToolkitRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRCreateAuthURIRequest + @brief Represents the parameters for the createAuthUri endpoint. + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/createAuthUri + */ +@interface FIRCreateAuthURIRequest : FIRIdentityToolkitRequest <FIRAuthRPCRequest> + +/** @property identifier + @brief The email or federated ID of the user. + */ +@property(nonatomic, copy) NSString *identifier; + +/** @property continueURI + @brief The URI to which the IDP redirects the user after the federated login flow. + */ +@property(nonatomic, copy) NSString *continueURI; + +/** @property openIDRealm + @brief Optional realm for OpenID protocol. The sub string "scheme://domain:port" of the param + "continueUri" is used if this is not set. + */ +@property(nonatomic, copy, nullable) NSString *openIDRealm; + +/** @property providerID + @brief The IdP ID. For white listed IdPs it's a short domain name e.g. google.com, aol.com, + live.net and yahoo.com. For other OpenID IdPs it's the OP identifier. + */ +@property(nonatomic, copy, nullable) NSString *providerID; + +/** @property clientID + @brief The relying party OAuth client ID. + */ +@property(nonatomic, copy, nullable) NSString *clientID; + +/** @property context + @brief The opaque value used by the client to maintain context info between the authentication + request and the IDP callback. + */ +@property(nonatomic, copy, nullable) NSString *context; + +/** @property appID + @brief The iOS client application's bundle identifier. + */ +@property(nonatomic, copy, nullable) NSString *appID; + +/** @fn initWithEndpoint:APIKey: + @brief Please use initWithIdentifier:continueURI:APIKey: + */ +- (nullable instancetype)initWithEndpoint:(NSString *)endpoint + APIKey:(NSString *)APIKey NS_UNAVAILABLE; + +/** @fn initWithIdentifier:continueURI:APIKey: + @brief Designated initializer. + @param identifier The email or federated ID of the user. + @param continueURI The URI to which the IDP redirects the user after the federated login flow. + @param APIKey The client's API Key. + */ +- (nullable instancetype)initWithIdentifier:(NSString *)identifier + continueURI:(NSString *)continueURI + APIKey:(NSString *)APIKey NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIRequest.m b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIRequest.m new file mode 100644 index 0000000..6d2b9e9 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIRequest.m @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#import "FIRCreateAuthURIRequest.h" + +/** @var kCreateAuthURIEndpoint + @brief The "createAuthUri" endpoint. + */ +static NSString *const kCreateAuthURIEndpoint = @"createAuthUri"; + +/** @var kProviderIDKey + @brief The key for the "providerId" value in the request. + */ +static NSString *const kProviderIDKey = @"providerId"; + +/** @var kIdentifierKey + @brief The key for the "identifier" value in the request. + */ +static NSString *const kIdentifierKey = @"identifier"; + +/** @var kContinueURIKey + @brief The key for the "continueUri" value in the request. + */ +static NSString *const kContinueURIKey = @"continueUri"; + +/** @var kOpenIDRealmKey + @brief The key for the "openidRealm" value in the request. + */ +static NSString *const kOpenIDRealmKey = @"openidRealm"; + +/** @var kClientIDKey + @brief The key for the "clientId" value in the request. + */ +static NSString *const kClientIDKey = @"clientId"; + +/** @var kContextKey + @brief The key for the "context" value in the request. + */ +static NSString *const kContextKey = @"context"; + +/** @var kAppIDKey + @brief The key for the "appId" value in the request. + */ +static NSString *const kAppIDKey = @"appId"; + +@implementation FIRCreateAuthURIRequest + +- (nullable instancetype)initWithIdentifier:(NSString *)identifier + continueURI:(NSString *)continueURI + APIKey:(NSString *)APIKey { + self = [super initWithEndpoint:kCreateAuthURIEndpoint APIKey:APIKey]; + if (self) { + _identifier = [identifier copy]; + _continueURI = [continueURI copy]; + } + return self; +} + +- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)error { + NSMutableDictionary *postBody = [@{ + kIdentifierKey : _identifier, + kContinueURIKey : _continueURI + } mutableCopy]; + if (_providerID) { + postBody[kProviderIDKey] = _providerID; + } + if (_openIDRealm) { + postBody[kOpenIDRealmKey] = _openIDRealm; + } + if (_clientID) { + postBody[kClientIDKey] = _clientID; + } + if (_context) { + postBody[kContextKey] = _context; + } + if (_appID) { + postBody[kAppIDKey] = _appID; + } + return postBody; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.h b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.h new file mode 100644 index 0000000..9f6cbae --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRCreateAuthURIResponse + @brief Represents the parameters for the createAuthUri endpoint. + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/createAuthUri + */ +@interface FIRCreateAuthURIResponse : NSObject <FIRAuthRPCResponse> + +/** @property authUri + @brief The URI used by the IDP to authenticate the user. + */ +@property(nonatomic, strong, readonly, nullable) NSString *authURI; + +/** @property registered + @brief Whether the user is registered if the identifier is an email. + */ +@property(nonatomic, assign, readonly) BOOL registered; + +/** @property providerId + @brief The provider ID of the auth URI. + */ +@property(nonatomic, strong, readonly, nullable) NSString *providerID; + +/** @property forExistingProvider + @brief True if the authUri is for user's existing provider. + */ +@property(nonatomic, assign, readonly) BOOL forExistingProvider; + +/** @property allProviders + @brief A list of provider IDs the passed @c identifier could use to sign in with. + */ +@property(nonatomic, copy, readonly, nullable) NSArray<NSString *> *allProviders; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.m b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.m new file mode 100644 index 0000000..7a38cca --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRCreateAuthURIResponse.m @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#import "FIRCreateAuthURIResponse.h" + +#import "../Private/FIRAuthErrorUtils.h" + +@implementation FIRCreateAuthURIResponse + +- (BOOL)setWithDictionary:(NSDictionary *)dictionary + error:(NSError *_Nullable *_Nullable)error { + _providerID = [dictionary[@"providerId"] copy]; + _authURI = [dictionary[@"authUri"] copy]; + _registered = [dictionary[@"registered"] boolValue]; + _forExistingProvider = [dictionary[@"forExistingProvider"] boolValue]; + _allProviders = [dictionary[@"allProviders"] copy]; + return YES; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRDeleteAccountRequest.h b/Firebase/Auth/Source/RPCs/FIRDeleteAccountRequest.h new file mode 100644 index 0000000..1751e54 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRDeleteAccountRequest.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCRequest.h" +#import "FIRIdentityToolkitRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRDeleteAccountRequest + @brief Represents the parameters for the deleteAccount endpoint. + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/deleteAccount + */ +@interface FIRDeleteAccountRequest : FIRIdentityToolkitRequest<FIRAuthRPCRequest> + +/** @fn initWithEndpoint:APIKey: + @brief Please use initWithAPIKey: + */ +- (nullable instancetype)initWithEndpoint:(NSString *)endpoint + APIKey:(NSString *)APIKey NS_UNAVAILABLE; + +/** @fn initWithAPIKey: + @brief Designated initializer. + @param APIKey The client's API Key. + @param localID The local ID. + @param accessToken The access token. + */ +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey + localID:(NSString *)localID + accessToken:(NSString *)accessToken NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRDeleteAccountRequest.m b/Firebase/Auth/Source/RPCs/FIRDeleteAccountRequest.m new file mode 100644 index 0000000..9105ba0 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRDeleteAccountRequest.m @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#import "FIRDeleteAccountRequest.h" + +/** @var kCreateAuthURIEndpoint + @brief The "deleteAccount" endpoint. + */ +static NSString *const kDeleteAccountEndpoint = @"deleteAccount"; + +/** @var kIDTokenKey + @brief The key for the "idToken" value in the request. This is actually the STS Access Token, + despite it's confusing (backwards compatiable) parameter name. + */ +static NSString *const kIDTokenKey = @"idToken"; + +/** @var kLocalIDKey + @brief The key for the "localID" value in the request. + */ +static NSString *const kLocalIDKey = @"localId"; + +@implementation FIRDeleteAccountRequest { + /** @var _accessToken + @brief The STS Access Token of the authenticated user. + */ + NSString *_accessToken; + + /** @var _localID + @brief The localID of the user. + */ + NSString *_localID; +} + +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey + localID:(nonnull NSString *)localID + accessToken:(nonnull NSString *)accessToken { + self = [super initWithEndpoint:kDeleteAccountEndpoint APIKey:APIKey]; + if (self) { + _localID = [localID copy]; + _accessToken = [accessToken copy]; + } + return self; +} + +- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)error { + NSMutableDictionary *postBody = [NSMutableDictionary dictionary]; + postBody[kIDTokenKey] = _accessToken; + postBody[kLocalIDKey] = _localID; + return postBody; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRDeleteAccountResponse.h b/Firebase/Auth/Source/RPCs/FIRDeleteAccountResponse.h new file mode 100644 index 0000000..59226d6 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRDeleteAccountResponse.h @@ -0,0 +1,26 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCResponse.h" + +/** @class FIRDeleteAccountResponse + @brief Represents the response from the deleteAccount endpoint. + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/deleteAccount + */ +@interface FIRDeleteAccountResponse : NSObject<FIRAuthRPCResponse> +@end diff --git a/Firebase/Auth/Source/RPCs/FIRDeleteAccountResponse.m b/Firebase/Auth/Source/RPCs/FIRDeleteAccountResponse.m new file mode 100644 index 0000000..01eb0a5 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRDeleteAccountResponse.m @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#import "FIRDeleteAccountResponse.h" + +#import "../Private/FIRAuthErrorUtils.h" + +@implementation FIRDeleteAccountResponse + +- (BOOL)setWithDictionary:(NSDictionary *)dictionary + error:(NSError *_Nullable *_Nullable)error { + return YES; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRGetAccountInfoRequest.h b/Firebase/Auth/Source/RPCs/FIRGetAccountInfoRequest.h new file mode 100644 index 0000000..b45b933 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRGetAccountInfoRequest.h @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCRequest.h" +#import "FIRIdentityToolkitRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRGetAccountInfoRequest + @brief Represents the parameters for the getAccountInfo endpoint. + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/getAccountInfo + */ +@interface FIRGetAccountInfoRequest : FIRIdentityToolkitRequest <FIRAuthRPCRequest> + +/** @property accessToken + @brief The STS Access Token for the authenticated user. + */ +@property(nonatomic, copy) NSString *accessToken; + +/** @fn initWithEndpoint:APIKey: + @brief Please use initWithAPIKey:IDToken: + */ +- (nullable instancetype)initWithEndpoint:(NSString *)endpoint + APIKey:(NSString *)APIKey NS_UNAVAILABLE; + +/** @fn initWithAPIKey:accessToken: + @brief Designated initializer. + @param APIKey The client's API Key. + @param accessToken The Access Token of the authenticated user. + */ +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey + accessToken:(NSString *)accessToken NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRGetAccountInfoRequest.m b/Firebase/Auth/Source/RPCs/FIRGetAccountInfoRequest.m new file mode 100644 index 0000000..5c73086 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRGetAccountInfoRequest.m @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#import "FIRGetAccountInfoRequest.h" + +/** @var kGetAccountInfoEndpoint + @brief The "getAccountInfo" endpoint. + */ +static NSString *const kGetAccountInfoEndpoint = @"getAccountInfo"; + +/** @var kIDTokenKey + @brief The key for the "idToken" value in the request. This is actually the STS Access Token, + despite it's confusing (backwards compatiable) parameter name. + */ +static NSString *const kIDTokenKey = @"idToken"; + +@implementation FIRGetAccountInfoRequest + +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey + accessToken:(NSString *)accessToken { + self = [super initWithEndpoint:kGetAccountInfoEndpoint APIKey:APIKey]; + if (self) { + _accessToken = [accessToken copy]; + } + return self; +} + +- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)error { + return @{ + kIDTokenKey : _accessToken + }; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRGetAccountInfoResponse.h b/Firebase/Auth/Source/RPCs/FIRGetAccountInfoResponse.h new file mode 100644 index 0000000..009f3c1 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRGetAccountInfoResponse.h @@ -0,0 +1,146 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRGetAccountInfoResponseProviderUserInfo + @brief Represents the provider user info part of the response from the getAccountInfo endpoint. + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/getAccountInfo + */ +@interface FIRGetAccountInfoResponseProviderUserInfo : NSObject + +/** @property providerID + @brief The ID of the identity provider. + */ +@property(nonatomic, strong, readonly, nullable) NSString *providerID; + +/** @property displayName + @brief The user's display name at the identity provider. + */ +@property(nonatomic, strong, readonly, nullable) NSString *displayName; + +/** @property photoURL + @brief The user's photo URL at the identity provider. + */ +@property(nonatomic, strong, readonly, nullable) NSURL *photoURL; + +/** @property federatedID + @brief The user's identifier at the identity provider. + */ +@property(nonatomic, strong, readonly, nullable) NSString *federatedID; + +/** @property email + @brief The user's email at the identity provider. + */ +@property(nonatomic, strong, readonly, nullable) NSString *email; + +/** @property phoneNumber + @brief A phone number associated with the user. + */ +@property(nonatomic, readonly, nullable) NSString *phoneNumber; + +/** @fn init + @brief Please use initWithDictionary: + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @fn initWithAPIKey: + @brief Designated initializer. + @param dictionary The provider user info data from endpoint. + */ +- (instancetype)initWithDictionary:(NSDictionary *)dictionary NS_DESIGNATED_INITIALIZER; + +@end + +/** @class FIRGetAccountInfoResponseUser + @brief Represents the firebase user info part of the response from the getAccountInfo endpoint. + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/getAccountInfo + */ +@interface FIRGetAccountInfoResponseUser : NSObject + +/** @property localID + @brief The ID of the user. + */ +@property(nonatomic, strong, readonly, nullable) NSString *localID; + +/** @property email + @brief The email or the user. + */ +@property(nonatomic, strong, readonly, nullable) NSString *email; + +/** @property emailVerified + @brief Whether the email has been verified. + */ +@property(nonatomic, assign, readonly) BOOL emailVerified; + +/** @property displayName + @brief The display name of the user. + */ +@property(nonatomic, strong, readonly, nullable) NSString *displayName; + +/** @property photoURL + @brief The user's photo URL. + */ +@property(nonatomic, strong, readonly, nullable) NSURL *photoURL; + +/** @property providerUserInfo + @brief The user's profiles at the associated identity providers. + */ +@property(nonatomic, strong, readonly, nullable) + NSArray<FIRGetAccountInfoResponseProviderUserInfo *> *providerUserInfo; + +/** @property passwordHash + @brief Information about user's password. + @remarks This is not necessarily the hash of user's actual password. + */ +@property(nonatomic, strong, readonly, nullable) NSString *passwordHash; + +/** @property phoneNumber + @brief A phone number associated with the user. + */ +@property(nonatomic, readonly, nullable) NSString *phoneNumber; + +/** @fn init + @brief Please use initWithDictionary: + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @fn initWithAPIKey: + @brief Designated initializer. + @param dictionary The provider user info data from endpoint. + */ +- (instancetype)initWithDictionary:(NSDictionary *)dictionary NS_DESIGNATED_INITIALIZER; + +@end + +/** @class FIRGetAccountInfoResponse + @brief Represents the response from the setAccountInfo endpoint. + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/getAccountInfo + */ +@interface FIRGetAccountInfoResponse : NSObject <FIRAuthRPCResponse> + +/** @property providerUserInfo + @brief The requested users' profiles. + */ +@property(nonatomic, strong, readonly, nullable) NSArray<FIRGetAccountInfoResponseUser *> *users; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRGetAccountInfoResponse.m b/Firebase/Auth/Source/RPCs/FIRGetAccountInfoResponse.m new file mode 100644 index 0000000..ba10746 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRGetAccountInfoResponse.m @@ -0,0 +1,94 @@ +/* + * 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. + */ + +#import "FIRGetAccountInfoResponse.h" + +#import "../Private/FIRAuthErrorUtils.h" + +/** @var kErrorKey + @brief The key for the "error" value in JSON responses from the server. + */ +static NSString *const kErrorKey = @"error"; + +@implementation FIRGetAccountInfoResponseProviderUserInfo + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary { + self = [super init]; + if (self) { + _providerID = [dictionary[@"providerId"] copy]; + _displayName = [dictionary[@"displayName"] copy]; + NSString *photoURL = dictionary[@"photoUrl"]; + if (photoURL) { + _photoURL = [NSURL URLWithString:photoURL]; + } + _federatedID = [dictionary[@"federatedId"] copy]; + _email = [dictionary[@"email"] copy]; + _phoneNumber = [dictionary[@"phoneNumber"] copy]; + } + return self; +} + +@end + +@implementation FIRGetAccountInfoResponseUser + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary { + self = [super init]; + if (self) { + NSArray<NSDictionary *> *providerUserInfoData = dictionary[@"providerUserInfo"]; + if (providerUserInfoData) { + NSMutableArray<FIRGetAccountInfoResponseProviderUserInfo *> *providerUserInfoArray = + [NSMutableArray arrayWithCapacity:providerUserInfoData.count]; + for (NSDictionary *dictionary in providerUserInfoData) { + [providerUserInfoArray addObject: + [[FIRGetAccountInfoResponseProviderUserInfo alloc] initWithDictionary:dictionary]]; + } + _providerUserInfo = [providerUserInfoArray copy]; + } + _localID = [dictionary[@"localId"] copy]; + _displayName = [dictionary[@"displayName"] copy]; + _email = [dictionary[@"email"] copy]; + NSString *photoURL = dictionary[@"photoUrl"]; + if (photoURL) { + _photoURL = [NSURL URLWithString:photoURL]; + } + _emailVerified = [dictionary[@"emailVerified"] boolValue]; + _passwordHash = [dictionary[@"passwordHash"] copy]; + _phoneNumber = [dictionary[@"phoneNumber"] copy]; + } + return self; +} + +@end + +@implementation FIRGetAccountInfoResponse + +- (BOOL)setWithDictionary:(NSDictionary *)dictionary + error:(NSError *_Nullable *_Nullable)error { + NSArray<NSDictionary *> *usersData = dictionary[@"users"]; + // The client side never sends a getAccountInfo request with multiple localID, so only one user + // data is expected in the response. + if (![usersData isKindOfClass:[NSArray class]] || usersData.count != 1) { + if (error) { + *error = [FIRAuthErrorUtils unexpectedResponseWithDeserializedResponse:dictionary]; + } + return NO; + } + _users = @[ [[FIRGetAccountInfoResponseUser alloc] initWithDictionary:usersData.firstObject] ]; + return YES; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h new file mode 100644 index 0000000..08ab495 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.h @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCRequest.h" +#import "FIRIdentityToolkitRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @enum FIRGetOOBConfirmationCodeRequestType + @brief Types of OOB Confirmation Code requests. + */ +typedef NS_ENUM(NSInteger, FIRGetOOBConfirmationCodeRequestType) { + /** @var FIRGetOOBConfirmationCodeRequestTypePasswordReset + @brief Requests a password reset code. + */ + FIRGetOOBConfirmationCodeRequestTypePasswordReset, + + /** @var FIRGetOOBConfirmationCodeRequestTypeVerifyEmail + @brief Requests an email verification code. + */ + FIRGetOOBConfirmationCodeRequestTypeVerifyEmail, +}; + +/** @enum FIRGetOOBConfirmationCodeRequest + @brief Represents the parameters for the getOOBConfirmationCode endpoint. + */ +@interface FIRGetOOBConfirmationCodeRequest : FIRIdentityToolkitRequest <FIRAuthRPCRequest> + +/** @property requestType + @brief The types of OOB Confirmation Code to request. + */ +@property(nonatomic, assign, readonly) FIRGetOOBConfirmationCodeRequestType requestType; + +/** @property email + @brief The email of the user. + @remarks For password reset. + */ +@property(nonatomic, copy, nullable, readonly) NSString *email; + +/** @property accessToken + @brief The STS Access Token of the authenticated user. + @remarks For email change. + */ +@property(nonatomic, copy, nullable, readonly) NSString *accessToken; + +/** @fn passwordResetRequestWithEmail:APIKey: + @brief Creates a password reset request. + @param email The user's email address. + @param APIKey The client's API Key. + @return A password reset request. + */ ++ (nullable FIRGetOOBConfirmationCodeRequest *)passwordResetRequestWithEmail:(NSString *)email + APIKey:(NSString *)APIKey; + +/** @fn verifyEmailRequestWithAccessToken:APIKey: + @brief Creates a password reset request. + @param accessToken The user's STS Access Token. + @param APIKey The client's API Key. + @return A password reset request. + */ ++ (nullable FIRGetOOBConfirmationCodeRequest *) + verifyEmailRequestWithAccessToken:(NSString *)accessToken APIKey:(NSString *)APIKey; + +/** @fn init + @brief Please use a factory method. + */ +- (nullable instancetype)initWithEndpoint:(NSString *)endpoint + APIKey:(NSString *)APIKey NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.m b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.m new file mode 100644 index 0000000..b0523e4 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeRequest.m @@ -0,0 +1,135 @@ +/* + * 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. + */ + +#import "FIRGetOOBConfirmationCodeRequest.h" + +#import "../Private/FIRAuthErrorUtils.h" +#import "../Private/FIRAuth_Internal.h" + +/** @var kEndpoint + @brief The getOobConfirmationCode endpoint name. + */ +static NSString *const kEndpoint = @"getOobConfirmationCode"; + +/** @var kRequestTypeKey + @brief The name of the required "requestType" property in the request. + */ +static NSString *const kRequestTypeKey = @"requestType"; + +/** @var kEmailKey + @brief The name of the "email" property in the request. + */ +static NSString *const kEmailKey = @"email"; + +/** @var kIDTokenKey + @brief The key for the "idToken" value in the request. This is actually the STS Access Token, + despite it's confusing (backwards compatiable) parameter name. + */ +static NSString *const kIDTokenKey = @"idToken"; + +/** @var kPasswordResetRequestTypeValue + @brief The value for the "PASSWORD_RESET" request type. + */ +static NSString *const kPasswordResetRequestTypeValue = @"PASSWORD_RESET"; + +/** @var kVerifyEmailRequestTypeValue + @brief The value for the "VERIFY_EMAIL" request type. + */ +static NSString *const kVerifyEmailRequestTypeValue = @"VERIFY_EMAIL"; + +@interface FIRGetOOBConfirmationCodeRequest () + +/** @fn initWithRequestType:email:APIKey: + @brief Designated initializer. + @param requestType The types of OOB Confirmation Code to request. + @param email The email of the user. + @param accessToken The STS Access Token of the currently signed in user. + @param APIKey The client's API Key. + */ +- (nullable instancetype)initWithRequestType:(FIRGetOOBConfirmationCodeRequestType)requestType + email:(nullable NSString *)email + accessToken:(nullable NSString *)accessToken + APIKey:(nullable NSString *)APIKey + NS_DESIGNATED_INITIALIZER; + +@end + +@implementation FIRGetOOBConfirmationCodeRequest + +/** @var requestTypeStringValueForRequestType: + @brief Returns the string equivilent for an @c FIRGetOOBConfirmationCodeRequestType value. + */ ++ (NSString *)requestTypeStringValueForRequestType: + (FIRGetOOBConfirmationCodeRequestType)requestType { + switch (requestType) { + case FIRGetOOBConfirmationCodeRequestTypePasswordReset: + return kPasswordResetRequestTypeValue; + case FIRGetOOBConfirmationCodeRequestTypeVerifyEmail: + return kVerifyEmailRequestTypeValue; + // No default case so that we get a compiler warning if a new value was added to the enum. + } +} + ++ (FIRGetOOBConfirmationCodeRequest *)passwordResetRequestWithEmail:(NSString *)email + APIKey:(NSString *)APIKey { + return [[self alloc] initWithRequestType:FIRGetOOBConfirmationCodeRequestTypePasswordReset + email:email + accessToken:nil + APIKey:APIKey]; +} + ++ (FIRGetOOBConfirmationCodeRequest *) + verifyEmailRequestWithAccessToken:(NSString *)accessToken APIKey:(NSString *)APIKey { + return [[self alloc] initWithRequestType:FIRGetOOBConfirmationCodeRequestTypeVerifyEmail + email:nil + accessToken:accessToken + APIKey:APIKey]; +} + +- (nullable instancetype)initWithRequestType:(FIRGetOOBConfirmationCodeRequestType)requestType + email:(nullable NSString *)email + accessToken:(nullable NSString *)accessToken + APIKey:(nullable NSString *)APIKey { + self = [super initWithEndpoint:kEndpoint APIKey:APIKey]; + if (self) { + _requestType = requestType; + _email = email; + _accessToken = accessToken; + } + return self; +} + +- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)error { + NSMutableDictionary *body = [@{ + kRequestTypeKey : [[self class] requestTypeStringValueForRequestType:_requestType] + } mutableCopy]; + + // For password reset requests, we only need an email address in addition to the already required + // fields. + if (_requestType == FIRGetOOBConfirmationCodeRequestTypePasswordReset) { + body[kEmailKey] = _email; + } + + // For verify email requests, we only need an STS Access Token in addition to the already required + // fields. + if (_requestType == FIRGetOOBConfirmationCodeRequestTypeVerifyEmail) { + body[kIDTokenKey] = _accessToken; + } + + return body; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeResponse.h b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeResponse.h new file mode 100644 index 0000000..69aa458 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeResponse.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRGetOOBConfirmationCodeResponse + @brief Represents the response from the getOobConfirmationCode endpoint. + */ +@interface FIRGetOOBConfirmationCodeResponse : NSObject <FIRAuthRPCResponse> + +/** @property OOBCode + @brief The OOB code returned by the server in some cases. + */ +@property(nonatomic, copy, readonly, nullable) NSString *OOBCode; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeResponse.m b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeResponse.m new file mode 100644 index 0000000..d5fc1dd --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRGetOOBConfirmationCodeResponse.m @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#import "FIRGetOOBConfirmationCodeResponse.h" + +#import "../Private/FIRAuthErrorUtils.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @var kOOBCodeKey + @brief The name of the field in the response JSON for the OOB code. + */ +static NSString *const kOOBCodeKey = @"oobCode"; + +@implementation FIRGetOOBConfirmationCodeResponse + +- (BOOL)setWithDictionary:(NSDictionary *)dictionary + error:(NSError *_Nullable *_Nullable)error { + _OOBCode = [dictionary[kOOBCodeKey] copy]; + return YES; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRIdentityToolkitRequest.h b/Firebase/Auth/Source/RPCs/FIRIdentityToolkitRequest.h new file mode 100644 index 0000000..873788d --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRIdentityToolkitRequest.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRIdentityToolkitRequest + @brief Represents a request to an identity toolkit endpoint. + */ +@interface FIRIdentityToolkitRequest : NSObject + +/** @property endpoint + @brief Gets the RPC's endpoint. + */ +@property(nonatomic, copy, readonly) NSString *endpoint; + +/** @property APIKey + @brief Gets the client's API key used for the request. + */ +@property(nonatomic, copy, readonly) NSString *APIKey; + +/** @fn init + @brief Please use initWithEndpoint:APIKey: + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @fn initWithEndpoint:APIKey: + @brief Designated initializer. + @param endpoint The endpoint name. + @param APIKey The client's API Key. + */ +- (nullable instancetype)initWithEndpoint:(NSString *)endpoint + APIKey:(NSString *)APIKey + NS_DESIGNATED_INITIALIZER; + +/** @fn requestURL + @brief Gets the request's full URL. + */ +- (NSURL *)requestURL; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRIdentityToolkitRequest.m b/Firebase/Auth/Source/RPCs/FIRIdentityToolkitRequest.m new file mode 100644 index 0000000..fb51a82 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRIdentityToolkitRequest.m @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#import "FIRIdentityToolkitRequest.h" + +/** @var kAPIURLFormat + @brief URL format for server API calls. + */ +static NSString *const kAPIURLFormat = @"https://%@/identitytoolkit/v3/relyingparty/%@?key=%@"; + +/** @var gAPIHost + @brief Host for server API calls. + */ +static NSString *gAPIHost = @"www.googleapis.com"; + +@implementation FIRIdentityToolkitRequest + +- (nullable instancetype)initWithEndpoint:(NSString *)endpoint + APIKey:(NSString *)APIKey { + self = [super init]; + if (self) { + _endpoint = [endpoint copy]; + _APIKey = [APIKey copy]; + } + return self; +} + +- (NSURL *)requestURL { + NSString *URLString = [NSString stringWithFormat:kAPIURLFormat, gAPIHost, _endpoint, _APIKey]; + NSURL *URL = [NSURL URLWithString:URLString]; + return URL; +} + +#pragma mark - Internal API for development + ++ (NSString *)host { + return gAPIHost; +} + ++ (void)setHost:(NSString *)host { + gAPIHost = host; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRResetPasswordRequest.h b/Firebase/Auth/Source/RPCs/FIRResetPasswordRequest.h new file mode 100644 index 0000000..66b03ad --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRResetPasswordRequest.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCRequest.h" +#import "FIRIdentityToolkitRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRResetPasswordRequest : FIRIdentityToolkitRequest <FIRAuthRPCRequest> + +/** @property oobCode + @brief The oobCode sent in the request. + */ +@property(nonatomic, copy, readonly) NSString *oobCode; + +/** @property updatedPassword + @brief The new password sent in the request. + */ +@property(nonatomic, copy, readonly) NSString *updatedPassword; + +/** @fn initWithEndpoint:APIKey: + @brief Please use initWithOOBCode:oobCode: instead. + */ +- (nullable instancetype)initWithEndpoint:(NSString *)endpoint + APIKey:(NSString *)APIKey NS_UNAVAILABLE; + +/** @fn initWithAPIKey:oobCode:currentPassword: + @brief Designated initializer. + @param APIKey The client's API Key. + @param oobCode The OOB Code. + @param newPassword The new password. + */ +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey + oobCode:(NSString *)oobCode + newPassword:(nullable NSString *)newPassword NS_DESIGNATED_INITIALIZER; +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRResetPasswordRequest.m b/Firebase/Auth/Source/RPCs/FIRResetPasswordRequest.m new file mode 100644 index 0000000..603aa00 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRResetPasswordRequest.m @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#import "FIRResetPasswordRequest.h" + +/** @var kResetPasswordEndpoint + @brief The "resetPassword" endpoint. + */ +static NSString *const kResetPasswordEndpoint = @"resetPassword"; + +/** @var kOOBCodeKey + @brief The "resetPassword" key. + */ +static NSString *const kOOBCodeKey = @"oobCode"; + +/** @var kCurrentPasswordKey + @brief The "newPassword" key. + */ +static NSString *const kCurrentPasswordKey = @"newPassword"; + +@implementation FIRResetPasswordRequest + +- (instancetype)initWithAPIKey:(NSString *)APIKey + oobCode:(NSString *)oobCode + newPassword:(NSString *)newPassword { + self = [super initWithEndpoint:kResetPasswordEndpoint APIKey:APIKey]; + if (self) { + _oobCode = oobCode; + _updatedPassword = newPassword; + } + return self; +} + +- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)error { + NSMutableDictionary *postBody = [NSMutableDictionary dictionary]; + postBody[kOOBCodeKey] = _oobCode; + if (_updatedPassword) { + postBody[kCurrentPasswordKey] = _updatedPassword; + } + return postBody; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRResetPasswordResponse.h b/Firebase/Auth/Source/RPCs/FIRResetPasswordResponse.h new file mode 100644 index 0000000..28eb5f4 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRResetPasswordResponse.h @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRAuthResetPasswordResponse + @brief Represents the response from the resetPassword endpoint. + @remarks Possible error codes: + - FIRAuthErrorCodeWeakPassword + - FIRAuthErrorCodeUserDisabled + - FIRAuthErrorCodeOperationNotAllowed + - FIRAuthErrorCodeExpiredActionCode + - FIRAuthErrorCodeInvalidActionCode + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/resetPassword + */ +@interface FIRResetPasswordResponse : NSObject<FIRAuthRPCResponse> + +/** @property email + @brief The email address corresponding to the reset password request. + */ +@property(nonatomic, strong, readonly) NSString *email; + +/** @property verifiedEmail + @brief The verified email returned from the backend. + */ +@property(nonatomic, strong, readonly) NSString *verifiedEmail; + +/** @property requestType + @brief The tpye of request as returned by the backend. + */ +@property(nonatomic, strong, readonly) NSString *requestType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRResetPasswordResponse.m b/Firebase/Auth/Source/RPCs/FIRResetPasswordResponse.m new file mode 100644 index 0000000..d306f6c --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRResetPasswordResponse.m @@ -0,0 +1,31 @@ +/* + * 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. + */ + +#import "FIRResetPasswordResponse.h" + +#import "../Private/FIRAuthErrorUtils.h" + +@implementation FIRResetPasswordResponse + +- (BOOL)setWithDictionary:(NSDictionary *)dictionary + error:(NSError *_Nullable *_Nullable)error { + _email = [dictionary[@"email"] copy]; + _requestType = [dictionary[@"requestType"] copy]; + _verifiedEmail = [dictionary[@"newEmail"] copy]; + return YES; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRSecureTokenRequest.h b/Firebase/Auth/Source/RPCs/FIRSecureTokenRequest.h new file mode 100644 index 0000000..44c16b1 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSecureTokenRequest.h @@ -0,0 +1,109 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @enum FIRSecureTokenRequestGrantType + @brief Represents the possible grant types for a token request. + */ +typedef NS_ENUM(NSUInteger, FIRSecureTokenRequestGrantType) { + /** @var FIRSecureTokenRequestGrantTypeAuthorizationCode + @brief Indicates an authorization code request. + @remarks Exchanges a Gitkit "ID Token" for an STS Access Token and Refresh Token. + */ + FIRSecureTokenRequestGrantTypeAuthorizationCode, + + /** @var FIRSecureTokenRequestGrantTypeRefreshToken + @brief Indicates an refresh token request. + @remarks Uses an existing Refresh Token to create a new Access Token. + */ + FIRSecureTokenRequestGrantTypeRefreshToken, +}; + +/** @class FIRSecureTokenRequest + @brief Represents the parameters for the token endpoint. + */ +@interface FIRSecureTokenRequest : NSObject <FIRAuthRPCRequest> + +/** @property grantType + @brief The type of grant requested. + @see FIRSecureTokenRequestGrantType + */ +@property(nonatomic, assign, readonly) FIRSecureTokenRequestGrantType grantType; + +/** @property scope + @brief The scopes requested (a comma-delimited list of scope strings.) + */ +@property(nonatomic, copy, readonly, nullable) NSString *scope; + +/** @property refreshToken + @brief The client's refresh token. + */ +@property(nonatomic, copy, readonly, nullable) NSString *refreshToken; + +/** @property code + @brief The client's authorization code (legacy Gitkit "ID Token"). + */ +@property(nonatomic, copy, readonly, nullable) NSString *code; + +/** @property APIKey + @brief The client's API Key. + */ +@property(nonatomic, copy, readonly) NSString *APIKey; + +/** @fn authCodeRequestWithCode: + @brief Creates an authorization code request with the given code (legacy Gitkit "ID Token"). + @param code The authorization code (legacy Gitkit "ID Token"). + @param APIKey The client's API Key. + @return An authorization request. + */ ++ (FIRSecureTokenRequest *)authCodeRequestWithCode:(NSString *)code APIKey:(NSString *)APIKey; + +/** @fn refreshRequestWithCode: + @brief Creates a refresh request with the given refresh token. + @param refreshToken The refresh token. + @param APIKey The client's API Key. + @return A refresh request. + */ ++ (FIRSecureTokenRequest *)refreshRequestWithRefreshToken:(NSString *)refreshToken + APIKey:(NSString *)APIKey; + +/** @fn init + @brief Please use initWithGrantType:scope:refreshToken:code: + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @fn initWithGrantType:scope:refreshToken:code:APIKey: + @brief Designated initializer. + @param grantType The type of request. + @param scope The scopes requested. + @param refreshToken The client's refresh token (for refresh requests.) + @param code The client's authorization code (Gitkit ID Token) (for authorization code requests.) + @param APIKey The client's API Key. + */ +- (nullable instancetype)initWithGrantType:(FIRSecureTokenRequestGrantType)grantType + scope:(nullable NSString *)scope + refreshToken:(nullable NSString *)refreshToken + code:(nullable NSString *)code + APIKey:(NSString *)APIKey NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRSecureTokenRequest.m b/Firebase/Auth/Source/RPCs/FIRSecureTokenRequest.m new file mode 100644 index 0000000..1983542 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSecureTokenRequest.m @@ -0,0 +1,141 @@ +/* + * 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. + */ + +#import "FIRSecureTokenRequest.h" + +/** @var kFIRSecureTokenServiceGetTokenURLFormat + @brief The format of the secure token service URLs. Requires string format substitution with + the client's API Key. + */ +static NSString *const kFIRSecureTokenServiceGetTokenURLFormat = @"https://%@/v1/token?key=%@"; + +/** @var kFIRSecureTokenServiceGrantTypeRefreshToken + @brief The string value of the @c FIRSecureTokenRequestGrantTypeRefreshToken request type. + */ +static NSString *const kFIRSecureTokenServiceGrantTypeRefreshToken = @"refresh_token"; + +/** @var kFIRSecureTokenServiceGrantTypeAuthorizationCode + @brief The string value of the @c FIRSecureTokenRequestGrantTypeAuthorizationCode request type. + */ +static NSString *const kFIRSecureTokenServiceGrantTypeAuthorizationCode = @"authorization_code"; + +/** @var kGrantTypeKey + @brief The key for the "grantType" parameter in the request. + */ +static NSString *const kGrantTypeKey = @"grantType"; + +/** @var kScopeKey + @brief The key for the "scope" parameter in the request. + */ +static NSString *const kScopeKey = @"scope"; + +/** @var kRefreshTokenKey + @brief The key for the "refreshToken" parameter in the request. + */ +static NSString *const kRefreshTokenKey = @"refreshToken"; + +/** @var kCodeKey + @brief The key for the "code" parameter in the request. + */ +static NSString *const kCodeKey = @"code"; + +/** @var gAPIHost + @brief Host for server API calls. + */ +static NSString *gAPIHost = @"securetoken.googleapis.com"; + +@implementation FIRSecureTokenRequest + ++ (FIRSecureTokenRequest *)authCodeRequestWithCode:(NSString *)code APIKey:(NSString *)APIKey { + return [[self alloc] initWithGrantType:FIRSecureTokenRequestGrantTypeAuthorizationCode + scope:nil + refreshToken:nil + code:code + APIKey:APIKey]; +} + ++ (FIRSecureTokenRequest *)refreshRequestWithRefreshToken:(NSString *)refreshToken + APIKey:(NSString *)APIKey { + return [[self alloc] initWithGrantType:FIRSecureTokenRequestGrantTypeRefreshToken + scope:nil + refreshToken:refreshToken + code:nil + APIKey:APIKey]; +} + +/** @fn grantTypeStringWithGrantType: + @brief Converts a @c FIRSecureTokenRequestGrantType to it's @c NSString equivilent. + */ ++ (NSString *)grantTypeStringWithGrantType:(FIRSecureTokenRequestGrantType)grantType { + switch (grantType) { + case FIRSecureTokenRequestGrantTypeAuthorizationCode: + return kFIRSecureTokenServiceGrantTypeAuthorizationCode; + case FIRSecureTokenRequestGrantTypeRefreshToken: + return kFIRSecureTokenServiceGrantTypeRefreshToken; + // No Default case so we will notice if new grant types are added to the enum. + } +} + +- (nullable instancetype)initWithGrantType:(FIRSecureTokenRequestGrantType)grantType + scope:(nullable NSString *)scope + refreshToken:(nullable NSString *)refreshToken + code:(nullable NSString *)code + APIKey:(NSString *)APIKey { + self = [super init]; + if (self) { + _grantType = grantType; + _scope = [scope copy]; + _refreshToken = [refreshToken copy]; + _code = [code copy]; + _APIKey = [APIKey copy]; + } + return self; +} + +- (NSURL *)requestURL { + NSString *URLString = + [NSString stringWithFormat:kFIRSecureTokenServiceGetTokenURLFormat, gAPIHost, _APIKey]; + NSURL *URL = [NSURL URLWithString:URLString]; + return URL; +} + +- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)error { + NSMutableDictionary *postBody = [@{ + kGrantTypeKey : [[self class] grantTypeStringWithGrantType:_grantType] + } mutableCopy]; + if (_scope) { + postBody[kScopeKey] = _scope; + } + if (_refreshToken) { + postBody[kRefreshTokenKey] = _refreshToken; + } + if (_code) { + postBody[kCodeKey] = _code; + } + return postBody; +} + +#pragma mark - Internal API for development + ++ (NSString *)host { + return gAPIHost; +} + ++ (void)setHost:(NSString *)host { + gAPIHost = host; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRSecureTokenResponse.h b/Firebase/Auth/Source/RPCs/FIRSecureTokenResponse.h new file mode 100644 index 0000000..0dd4a20 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSecureTokenResponse.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRSecureTokenResponse + @brief Represents the response from the token endpoint. + */ +@interface FIRSecureTokenResponse : NSObject <FIRAuthRPCResponse> + +/** @property approximateExpirationDate + @brief The approximate expiration date of the access token. + */ +@property(nonatomic, copy, readonly, nullable) NSDate *approximateExpirationDate; + +/** @property refreshToken + @brief The refresh token. (Possibly an updated one for refresh requests.) + */ +@property(nonatomic, copy, readonly, nullable) NSString *refreshToken; + +/** @property accessToken + @brief The new access token. + */ +@property(nonatomic, copy, readonly, nullable) NSString *accessToken; + +/** @property IDToken + @brief The new ID Token. + */ +@property(nonatomic, copy, readonly, nullable) NSString *IDToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRSecureTokenResponse.m b/Firebase/Auth/Source/RPCs/FIRSecureTokenResponse.m new file mode 100644 index 0000000..8ff3dde --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSecureTokenResponse.m @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#import "FIRSecureTokenResponse.h" + +#import "../Private/FIRAuthErrorUtils.h" + +/** @var kExpiresInKey + @brief The key for the number of seconds till the access token expires. + */ +static NSString *const kExpiresInKey = @"expires_in"; + +/** @var kRefreshTokenKey + @brief The key for the refresh token. + */ +static NSString *const kRefreshTokenKey = @"refresh_token"; + +/** @var kAccessTokenKey + @brief The key for the access token. + */ +static NSString *const kAccessTokenKey = @"access_token"; + +/** @var kIDTokenKey + @brief The key for the "id_token" value in the response. + */ +static NSString *const kIDTokenKey = @"id_token"; + +@implementation FIRSecureTokenResponse + +- (nullable NSString *)expectedKind { + return nil; +} + +- (BOOL)setWithDictionary:(NSDictionary *)dictionary + error:(NSError *_Nullable *_Nullable)error { + _refreshToken = dictionary[kRefreshTokenKey]; + _accessToken = dictionary[kAccessTokenKey]; + _IDToken = dictionary[kIDTokenKey]; + if (!_accessToken.length) { + if (error) { + *error = [FIRAuthErrorUtils unexpectedResponseWithDeserializedResponse:dictionary]; + } + return NO; + } + id expiresIn = dictionary[kExpiresInKey]; + if (![expiresIn isKindOfClass:[NSString class]]) { + if (error) { + *error = [FIRAuthErrorUtils unexpectedResponseWithDeserializedResponse:dictionary]; + } + return NO; + } + + _approximateExpirationDate = [NSDate dateWithTimeIntervalSinceNow:[expiresIn doubleValue]]; + return YES; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeRequest.h b/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeRequest.h new file mode 100644 index 0000000..596fb8c --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeRequest.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#import "FIRIdentityToolkitRequest.h" + +#import "FIRAuthRPCRequest.h" +#import "FIRIdentityToolkitRequest.h" + +@class FIRAuthAppCredential; + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRSendVerificationCodeRequest : FIRIdentityToolkitRequest <FIRAuthRPCRequest> + +/** @property phoneNumber + @brief The phone number to which the verification code should be sent. + */ +@property(nonatomic, strong, readonly) NSString *phoneNumber; + +/** @property appCredential + @brief The credential to prove the identity of the app in order to send the verification code. + */ +@property(nonatomic, strong, readonly) FIRAuthAppCredential *appCredential; + +/** @fn initWithEndpoint:APIKey: + @brief Please use initWithPhoneNumber:APIKey: + */ +- (nullable instancetype)initWithEndpoint:(NSString *)endpoint + APIKey:(NSString *)APIKey NS_UNAVAILABLE; + +/** @fn initWithPhoneNumber:APIKey: + @brief Designated initializer. + @param phoneNumber The phone number to which the verification code is to be sent. + @param appCredential The credential that proves the identity of the app. + @param APIKey The client's API Key. + */ +- (nullable instancetype)initWithPhoneNumber:(NSString *)phoneNumber + appCredential:(FIRAuthAppCredential *)appCredential + APIKey:(NSString *)APIKey NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeRequest.m b/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeRequest.m new file mode 100644 index 0000000..f2212ad --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeRequest.m @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#import "FIRSendVerificationCodeRequest.h" + +#import "../Private/FIRAuthAppCredential.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @var kSendVerificationCodeEndPoint + @brief The "sendVerificationCodeEnd" endpoint. + */ +static NSString *const kSendVerificationCodeEndPoint = @"sendVerificationCode"; + +/** @var kPhoneNumberKey + @brief The key for the Phone Number parameter in the request. + */ +static NSString *const kPhoneNumberKey = @"phoneNumber"; + +/** @var kReceiptKey + @brief The key for the receipt parameter in the request. + */ +static NSString *const kReceiptKey = @"iosReceipt"; + +/** @var kSecretKey + @brief The key for the Secret parameter in the request. + */ +static NSString *const kSecretKey = @"iosSecret"; + +@implementation FIRSendVerificationCodeRequest { +} + +- (nullable instancetype)initWithPhoneNumber:(NSString *)phoneNumber + appCredential:(FIRAuthAppCredential *)appCredential + APIKey:(NSString *)APIKey { + self = [super initWithEndpoint:kSendVerificationCodeEndPoint APIKey:APIKey]; + if (self) { + _phoneNumber = [phoneNumber copy]; + _appCredential = appCredential; + } + return self; +} + +- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)error { + NSMutableDictionary *postBody = [NSMutableDictionary dictionary]; + if (_phoneNumber) { + postBody[kPhoneNumberKey] = _phoneNumber; + } + if (_appCredential.receipt) { + postBody[kReceiptKey] = _appCredential.receipt; + } + if (_appCredential.secret) { + postBody[kSecretKey] = _appCredential.secret; + } + return postBody; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeResponse.h b/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeResponse.h new file mode 100644 index 0000000..1a49ec2 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeResponse.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRSendVerificationCodeResponse : NSObject <FIRAuthRPCResponse> + +/** @property verificationID + @brief Encrypted session information returned by the backend. + */ +@property(nonatomic, readonly) NSString *verificationID; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeResponse.m b/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeResponse.m new file mode 100644 index 0000000..9e47b6e --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSendVerificationCodeResponse.m @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#import "FIRSendVerificationCodeResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation FIRSendVerificationCodeResponse + +// TODO: remove when resolving b/37169084 . +- (nullable NSString *)expectedKind { + return nil; +} + +- (BOOL)setWithDictionary:(NSDictionary *)dictionary + error:(NSError *_Nullable *_Nullable)error { + _verificationID = [dictionary[@"sessionInfo"] copy]; + return YES; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRSetAccountInfoRequest.h b/Firebase/Auth/Source/RPCs/FIRSetAccountInfoRequest.h new file mode 100644 index 0000000..4816474 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSetAccountInfoRequest.h @@ -0,0 +1,149 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCRequest.h" +#import "FIRIdentityToolkitRequest.h" + +@class FIRGetAccountInfoResponse; + +NS_ASSUME_NONNULL_BEGIN + +/** @var FIRSetAccountInfoUserAttributeEmail + @brief Constant for email attribute used in "deleteAttributes". + */ +extern NSString *const FIRSetAccountInfoUserAttributeEmail; + +/** @var FIRSetAccountInfoUserAttributeDisplayName + @brief Constant for displayName attribute used in "deleteAttributes". + */ +extern NSString *const FIRSetAccountInfoUserAttributeDisplayName; + +/** @var FIRSetAccountInfoUserAttributeProvider + @brief Constant for provider attribute used in "deleteAttributes". + */ +extern NSString *const FIRSetAccountInfoUserAttributeProvider; + +/** @var FIRSetAccountInfoUserAttributePhotoURL + @brief Constant for photoURL attribute used in "deleteAttributes". + */ +extern NSString *const FIRSetAccountInfoUserAttributePhotoURL; + +/** @var FIRSetAccountInfoUserAttributePassword + @brief Constant for password attribute used in "deleteAttributes". + */ +extern NSString *const FIRSetAccountInfoUserAttributePassword; + +/** @class FIRSetAccountInfoRequest + @brief Represents the parameters for the setAccountInfo endpoint. + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/setAccountInfo + */ +@interface FIRSetAccountInfoRequest : FIRIdentityToolkitRequest <FIRAuthRPCRequest> + +/** @property accessToken + @brief The STS Access Token of the authenticated user. + */ +@property(nonatomic, copy, nullable) NSString *accessToken; + +/** @property displayName + @brief The name of the user. + */ +@property(nonatomic, copy, nullable) NSString *displayName; + +/** @property localID + @brief The local ID of the user. + */ +@property(nonatomic, copy, nullable) NSString *localID; + +/** @property email + @brief The email of the user. + */ +@property(nonatomic, copy, nullable) NSString *email; + +/** @property photoURL + @brief The photoURL of the user. + */ +@property(nonatomic, copy, nullable) NSURL *photoURL; + +/** @property password + @brief The new password of the user. + */ +@property(nonatomic, copy, nullable) NSString *password; + +/** @property providers + @brief The associated identity providers of the user. + */ +@property(nonatomic, copy, nullable) NSArray<NSString *> *providers; + +/** @property OOBCode + @brief The out-of-band code of the change email request. + */ +@property(nonatomic, copy, nullable) NSString *OOBCode; + +/** @property emailVerified + @brief Whether to mark the email as verified or not. + */ +@property(nonatomic, assign) BOOL emailVerified; + +/** @property upgradeToFederatedLogin + @brief Whether to mark the user to upgrade to federated login. + */ +@property(nonatomic, assign) BOOL upgradeToFederatedLogin; + +/** @property captchaChallenge + @brief The captcha challenge. + */ +@property(nonatomic, copy, nullable) NSString *captchaChallenge; + +/** @property captchaResponse + @brief Response to the captcha. + */ +@property(nonatomic, copy, nullable) NSString *captchaResponse; + +/** @property deleteAttributes + @brief The list of user attributes to delete. + @remarks Every element of the list must be one of the predefined constant starts with + "FIRSetAccountInfoUserAttribute". + */ +@property(nonatomic, copy, nullable) NSArray<NSString *> *deleteAttributes; + +/** @property deleteProviders + @brief The list of identity providers to delete. + */ +@property(nonatomic, copy, nullable) NSArray<NSString *> *deleteProviders; + +/** @property returnSecureToken + @brief Whether the response should return access token and refresh token directly. + @remarks The default value is @c YES . + */ +@property(nonatomic, assign) BOOL returnSecureToken; + +/** @fn initWithEndpoint:APIKey: + @brief Please use initWithAPIKey: + */ +- (nullable instancetype)initWithEndpoint:(NSString *)endpoint + APIKey:(NSString *)APIKey NS_UNAVAILABLE; + +/** @fn initWithAPIKey: + @brief Designated initializer. + @param APIKey The client's API Key. + */ +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRSetAccountInfoRequest.m b/Firebase/Auth/Source/RPCs/FIRSetAccountInfoRequest.m new file mode 100644 index 0000000..b18d0dd --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSetAccountInfoRequest.m @@ -0,0 +1,174 @@ +/* + * 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. + */ + +#import "FIRSetAccountInfoRequest.h" + +#import "../Private/FIRAuthErrorUtils.h" +#import "../Private/FIRAuth_Internal.h" +#import "FIRGetAccountInfoResponse.h" + +NSString *const FIRSetAccountInfoUserAttributeEmail = @"EMAIL"; + +NSString *const FIRSetAccountInfoUserAttributeDisplayName = @"DISPLAY_NAME"; + +NSString *const FIRSetAccountInfoUserAttributeProvider = @"PROVIDER"; + +NSString *const FIRSetAccountInfoUserAttributePhotoURL = @"PHOTO_URL"; + +NSString *const FIRSetAccountInfoUserAttributePassword = @"PASSWORD"; + +/** @var kCreateAuthURIEndpoint + @brief The "setAccountInfo" endpoint. + */ +static NSString *const kSetAccountInfoEndpoint = @"setAccountInfo"; + +/** @var kIDTokenKey + @brief The key for the "idToken" value in the request. This is actually the STS Access Token, + despite it's confusing (backwards compatiable) parameter name. + */ +static NSString *const kIDTokenKey = @"idToken"; + +/** @var kDisplayNameKey + @brief The key for the "displayName" value in the request. + */ +static NSString *const kDisplayNameKey = @"displayName"; + +/** @var kLocalIDKey + @brief The key for the "localID" value in the request. + */ +static NSString *const kLocalIDKey = @"localId"; + +/** @var kEmailKey + @brief The key for the "email" value in the request. + */ +static NSString *const kEmailKey = @"email"; + +/** @var kPasswordKey + @brief The key for the "password" value in the request. + */ +static NSString *const kPasswordKey = @"password"; + +/** @var kPhotoURLKey + @brief The key for the "photoURL" value in the request. + */ +static NSString *const kPhotoURLKey = @"photoUrl"; + +/** @var kProvidersKey + @brief The key for the "providers" value in the request. + */ +static NSString *const kProvidersKey = @"provider"; + +/** @var kOOBCodeKey + @brief The key for the "OOBCode" value in the request. + */ +static NSString *const kOOBCodeKey = @"oobCode"; + +/** @var kEmailVerifiedKey + @brief The key for the "emailVerified" value in the request. + */ +static NSString *const kEmailVerifiedKey = @"emailVerified"; + +/** @var kUpgradeToFederatedLoginKey + @brief The key for the "upgradeToFederatedLogin" value in the request. + */ +static NSString *const kUpgradeToFederatedLoginKey = @"upgradeToFederatedLogin"; + +/** @var kCaptchaChallengeKey + @brief The key for the "captchaChallenge" value in the request. + */ +static NSString *const kCaptchaChallengeKey = @"captchaChallenge"; + +/** @var kCaptchaResponseKey + @brief The key for the "captchaResponse" value in the request. + */ +static NSString *const kCaptchaResponseKey = @"captchaResponse"; + +/** @var kDeleteAttributesKey + @brief The key for the "deleteAttribute" value in the request. + */ +static NSString *const kDeleteAttributesKey = @"deleteAttribute"; + +/** @var kDeleteProvidersKey + @brief The key for the "deleteProvider" value in the request. + */ +static NSString *const kDeleteProvidersKey = @"deleteProvider"; + +/** @var kReturnSecureTokenKey + @brief The key for the "returnSecureToken" value in the request. + */ +static NSString *const kReturnSecureTokenKey = @"returnSecureToken"; + +@implementation FIRSetAccountInfoRequest + +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey { + self = [super initWithEndpoint:kSetAccountInfoEndpoint APIKey:APIKey]; + if (self) { + _returnSecureToken = YES; + } + return self; +} + +- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)error { + NSMutableDictionary *postBody = [NSMutableDictionary dictionary]; + if (_accessToken) { + postBody[kIDTokenKey] = _accessToken; + } + if (_displayName) { + postBody[kDisplayNameKey] = _displayName; + } + if (_localID) { + postBody[kLocalIDKey] = _localID; + } + if (_email) { + postBody[kEmailKey] = _email; + } + if (_password) { + postBody[kPasswordKey] = _password; + } + if (_photoURL) { + postBody[kPhotoURLKey] = _photoURL.absoluteString; + } + if (_providers) { + postBody[kProvidersKey] = _providers; + } + if (_OOBCode) { + postBody[kOOBCodeKey] = _OOBCode; + } + if (_emailVerified) { + postBody[kEmailVerifiedKey] = @YES; + } + if (_upgradeToFederatedLogin) { + postBody[kUpgradeToFederatedLoginKey] = @YES; + } + if (_captchaChallenge) { + postBody[kCaptchaChallengeKey] = _captchaChallenge; + } + if (_captchaResponse) { + postBody[kCaptchaResponseKey] = _captchaResponse; + } + if (_deleteAttributes) { + postBody[kDeleteAttributesKey] = _deleteAttributes; + } + if (_deleteProviders) { + postBody[kDeleteProvidersKey] = _deleteProviders; + } + if (_returnSecureToken) { + postBody[kReturnSecureTokenKey] = @YES; + } + return postBody; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRSetAccountInfoResponse.h b/Firebase/Auth/Source/RPCs/FIRSetAccountInfoResponse.h new file mode 100644 index 0000000..92895c0 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSetAccountInfoResponse.h @@ -0,0 +1,98 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRSetAccountInfoResponseProviderUserInfo + @brief Represents the provider user info part of the response from the setAccountInfo endpoint. + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/setAccountInfo + */ +@interface FIRSetAccountInfoResponseProviderUserInfo : NSObject + +/** @property providerID + @brief The ID of the identity provider. + */ +@property(nonatomic, strong, readonly, nullable) NSString *providerID; + +/** @property displayName + @brief The user's display name at the identity provider. + */ +@property(nonatomic, strong, readonly, nullable) NSString *displayName; + +/** @property photoURL + @brief The user's photo URL at the identity provider. + */ +@property(nonatomic, strong, readonly, nullable) NSURL *photoURL; + +/** @fn init + @brief Please use initWithDictionary: + */ +- (instancetype)init NS_UNAVAILABLE; + +/** @fn initWithAPIKey: + @brief Designated initializer. + @param dictionary The provider user info data from endpoint. + */ +- (instancetype)initWithDictionary:(NSDictionary *)dictionary NS_DESIGNATED_INITIALIZER; + +@end + +/** @class FIRSetAccountInfoResponse + @brief Represents the response from the setAccountInfo endpoint. + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/setAccountInfo + */ +@interface FIRSetAccountInfoResponse : NSObject <FIRAuthRPCResponse> + +/** @property email + @brief The email or the user. + */ +@property(nonatomic, strong, readonly, nullable) NSString *email; + +/** @property displayName + @brief The display name of the user. + */ +@property(nonatomic, strong, readonly, nullable) NSString *displayName; + +/** @property providerUserInfo + @brief The user's profiles at the associated identity providers. + */ +@property(nonatomic, strong, readonly, nullable) + NSArray<FIRSetAccountInfoResponseProviderUserInfo *> *providerUserInfo; + +/** @property IDToken + @brief Either an authorization code suitable for performing an STS token exchange, or the + access token from Secure Token Service, depending on whether @c returnSecureToken is set + on the request. + */ +@property(nonatomic, strong, readonly, nullable) NSString *IDToken; + +/** @property approximateExpirationDate + @brief The approximate expiration date of the access token. + */ +@property(nonatomic, copy, readonly, nullable) NSDate *approximateExpirationDate; + +/** @property refreshToken + @brief The refresh token from Secure Token Service. + */ +@property(nonatomic, strong, readonly, nullable) NSString *refreshToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRSetAccountInfoResponse.m b/Firebase/Auth/Source/RPCs/FIRSetAccountInfoResponse.m new file mode 100644 index 0000000..140ba25 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSetAccountInfoResponse.m @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#import "FIRSetAccountInfoResponse.h" + +#import "../Private/FIRAuthErrorUtils.h" + +@implementation FIRSetAccountInfoResponseProviderUserInfo + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary { + self = [super init]; + if (self) { + _providerID = [dictionary[@"providerId"] copy]; + _displayName = [dictionary[@"displayName"] copy]; + NSString *photoURL = dictionary[@"photoUrl"]; + if (photoURL) { + _photoURL = [NSURL URLWithString:photoURL]; + } + } + return self; +} + +@end + +@implementation FIRSetAccountInfoResponse + +- (BOOL)setWithDictionary:(NSDictionary *)dictionary + error:(NSError *_Nullable *_Nullable)error { + _email = [dictionary[@"email"] copy]; + _displayName = [dictionary[@"displayName"] copy]; + _IDToken = [dictionary[@"idToken"] copy]; + _approximateExpirationDate = [dictionary[@"expiresIn"] isKindOfClass:[NSString class]] ? + [NSDate dateWithTimeIntervalSinceNow:[dictionary[@"expiresIn"] doubleValue]] : nil; + _refreshToken = [dictionary[@"refreshToken"] copy]; + NSArray<NSDictionary *> *providerUserInfoData = dictionary[@"providerUserInfo"]; + if (providerUserInfoData) { + NSMutableArray<FIRSetAccountInfoResponseProviderUserInfo *> *providerUserInfoArray = + [NSMutableArray arrayWithCapacity:providerUserInfoData.count]; + for (NSDictionary *dictionary in providerUserInfoData) { + [providerUserInfoArray addObject: + [[FIRSetAccountInfoResponseProviderUserInfo alloc] initWithDictionary:dictionary]]; + } + _providerUserInfo = [providerUserInfoArray copy]; + } + return YES; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRSignUpNewUserRequest.h b/Firebase/Auth/Source/RPCs/FIRSignUpNewUserRequest.h new file mode 100644 index 0000000..46b47d5 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSignUpNewUserRequest.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCRequest.h" +#import "FIRIdentityToolkitRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRSignUpNewUserRequest : FIRIdentityToolkitRequest <FIRAuthRPCRequest> + +/** @property email + @brief The email of the user. + */ +@property(nonatomic, copy, nullable) NSString *email; + +/** @property password + @brief The password inputed by the user. + */ +@property(nonatomic, copy, nullable) NSString *password; + +/** @property displayName + @brief The password inputed by the user. + */ +@property(nonatomic, copy, nullable) NSString *displayName; + +/** @property returnSecureToken + @brief Whether the response should return access token and refresh token directly. + @remarks The default value is @c YES . + */ +@property(nonatomic, assign) BOOL returnSecureToken; + +/** @fn initWithEndpoint:APIKey: + @brief Please use initWithEmail:password:APIKey: + */ +- (nullable instancetype)initWithEndpoint:(NSString *)endpoint + APIKey:(NSString *)APIKey NS_UNAVAILABLE; +/** @fn initWithAPIKey: + @brief initializer for anonymous sign-in. + */ +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey; + +/** @fn initWithAPIKey:email:password: + @brief Designated initializer. + */ +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey + email:(nullable NSString *)email + password:(nullable NSString *)password + displayName:(nullable NSString *)displayName NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRSignUpNewUserRequest.m b/Firebase/Auth/Source/RPCs/FIRSignUpNewUserRequest.m new file mode 100644 index 0000000..af60b11 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSignUpNewUserRequest.m @@ -0,0 +1,82 @@ +/* + * 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. + */ + +#import "FIRSignUpNewUserRequest.h" + +/** @var kSignupNewUserEndpoint + @brief The "SingupNewUserEndpoint" endpoint. + */ +static NSString *const kSignupNewUserEndpoint = @"signupNewUser"; + +/** @var kEmailKey + @brief The key for the "email" value in the request. + */ +static NSString *const kEmailKey = @"email"; + +/** @var kPasswordKey + @brief The key for the "password" value in the request. + */ +static NSString *const kPasswordKey = @"password"; + +/** @var kDisplayNameKey + @brief The key for the "kDisplayName" value in the request. + */ +static NSString *const kDisplayNameKey = @"displayName"; + +/** @var kReturnSecureTokenKey + @brief The key for the "returnSecureToken" value in the request. + */ +static NSString *const kReturnSecureTokenKey = @"returnSecureToken"; + +@implementation FIRSignUpNewUserRequest + +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey + email:(NSString *)email + password:(NSString *)password + displayName:(NSString *)displayName { + self = [super initWithEndpoint:kSignupNewUserEndpoint APIKey:APIKey]; + if (self) { + _email = [email copy]; + _password = [password copy]; + _displayName = [displayName copy]; + _returnSecureToken = YES; + } + return self; +} + +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey{ + self = [self initWithAPIKey:APIKey email:nil password:nil displayName:nil]; + return self; +} + +- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)error { + NSMutableDictionary *postBody = [NSMutableDictionary dictionary]; + if (_email) { + postBody[kEmailKey] = _email; + } + if (_password) { + postBody[kPasswordKey] = _password; + } + if (_displayName) { + postBody[kDisplayNameKey] = _displayName; + } + if (_returnSecureToken) { + postBody[kReturnSecureTokenKey] = @YES; + } + return postBody; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRSignUpNewUserResponse.h b/Firebase/Auth/Source/RPCs/FIRSignUpNewUserResponse.h new file mode 100644 index 0000000..0d55939 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSignUpNewUserResponse.h @@ -0,0 +1,44 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRSignUpNewUserResponse : NSObject<FIRAuthRPCResponse> + +/** @property IDToken + @brief Either an authorization code suitable for performing an STS token exchange, or the + access token from Secure Token Service, depending on whether @c returnSecureToken is set + on the request. + */ +@property(nonatomic, strong, readonly, nullable) NSString *IDToken; + +/** @property approximateExpirationDate + @brief The approximate expiration date of the access token. + */ +@property(nonatomic, copy, readonly, nullable) NSDate *approximateExpirationDate; + +/** @property refreshToken + @brief The refresh token from Secure Token Service. + */ +@property(nonatomic, strong, readonly, nullable) NSString *refreshToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRSignUpNewUserResponse.m b/Firebase/Auth/Source/RPCs/FIRSignUpNewUserResponse.m new file mode 100644 index 0000000..4196d21 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRSignUpNewUserResponse.m @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#import "FIRSignUpNewUserResponse.h" + +#import "../Private/FIRAuthErrorUtils.h" + +@implementation FIRSignUpNewUserResponse + +- (BOOL)setWithDictionary:(NSDictionary *)dictionary + error:(NSError *_Nullable *_Nullable)error { + _IDToken = [dictionary[@"idToken"] copy]; + _approximateExpirationDate = [dictionary[@"expiresIn"] isKindOfClass:[NSString class]] ? + [NSDate dateWithTimeIntervalSinceNow:[dictionary[@"expiresIn"] doubleValue]] : nil; + _refreshToken = [dictionary[@"refreshToken"] copy]; + return YES; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyAssertionRequest.h b/Firebase/Auth/Source/RPCs/FIRVerifyAssertionRequest.h new file mode 100644 index 0000000..3202b47 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyAssertionRequest.h @@ -0,0 +1,100 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCRequest.h" +#import "FIRIdentityToolkitRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRVerifyAssertionRequest + @brief Represents the parameters for the verifyAssertion endpoint. + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/verifyAssertion + */ +@interface FIRVerifyAssertionRequest : FIRIdentityToolkitRequest <FIRAuthRPCRequest> + +/** @property requestURI + @brief The URI to which the IDP redirects the user back. It may contain federated login result + params added by the IDP. + */ +@property(nonatomic, copy, nullable) NSString *requestURI; + +/** @property pendingIDToken + @brief The Firebase ID Token for the non-trusted IDP pending to be confirmed by the user. + */ +@property(nonatomic, copy, nullable) NSString *pendingIDToken; + +/** @property accessToken + @brief The STS Access Token for the authenticated user, only needed for linking the user. + */ +@property(nonatomic, copy, nullable) NSString *accessToken; + +/** @property returnSecureToken + @brief Whether the response should return access token and refresh token directly. + @remarks The default value is @c YES . + */ +@property(nonatomic, assign) BOOL returnSecureToken; + +#pragma mark - Components of "postBody" + +/** @property providerID + @brief The ID of the IDP whose credentials are being presented to the endpoint. + */ +@property(nonatomic, copy, readonly) NSString *providerID; + +/** @property providerAccessToken + @brief An access token from the IDP. + */ +@property(nonatomic, copy, nullable) NSString *providerAccessToken; + +/** @property providerIDToken + @brief An ID Token from the IDP. + */ +@property(nonatomic, copy, nullable) NSString *providerIDToken; + +/** @property providerOAuthTokenSecret + @brief An OAuth client secret from the IDP. + */ +@property(nonatomic, copy, nullable) NSString *providerOAuthTokenSecret; + +/** @property inputEmail + @brief The originally entered email in the UI. + */ +@property(nonatomic, copy, nullable) NSString *inputEmail; + +/** @property autoCreate + @brief A flag that indicates whether or not the user should be automatically created. + */ +@property(nonatomic, assign) BOOL autoCreate; + +/** @fn initWithEndpoint:APIKey: + @brief Please use initWithAPIKey: + */ +- (nullable instancetype)initWithEndpoint:(NSString *)endpoint + APIKey:(NSString *)APIKey NS_UNAVAILABLE; + +/** @fn initWithAPIKey: + @brief Designated initializer. + @param APIKey The client's API Key. + @param providerID The auth provider's ID. + */ +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey + providerID:(NSString *)providerID NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyAssertionRequest.m b/Firebase/Auth/Source/RPCs/FIRVerifyAssertionRequest.m new file mode 100644 index 0000000..b31ae42 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyAssertionRequest.m @@ -0,0 +1,142 @@ +/* + * 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. + */ + +#import "FIRVerifyAssertionRequest.h" + +#import <GoogleToolboxForMac/GTMNSData+zlib.h> +#import <GoogleToolboxForMac/GTMNSDictionary+URLArguments.h> + +/** @var kVerifyAssertionEndpoint + @brief The "verifyAssertion" endpoint. + */ +static NSString *const kVerifyAssertionEndpoint = @"verifyAssertion"; + +/** @var kProviderIDKey + @brief The key for the "providerId" value in the request. + */ +static NSString *const kProviderIDKey = @"providerId"; + +/** @var kProviderIDTokenKey + @brief The key for the "id_token" value in the request. + */ +static NSString *const kProviderIDTokenKey = @"id_token"; + +/** @var kProviderAccessTokenKey + @brief The key for the "access_token" value in the request. + */ +static NSString *const kProviderAccessTokenKey = @"access_token"; + +/** @var kProviderOAuthTokenSecretKey + @brief The key for the "oauth_token_secret" value in the request. + */ +static NSString *const kProviderOAuthTokenSecretKey = @"oauth_token_secret"; + +/** @var kIdentifierKey + @brief The key for the "identifier" value in the request. + */ +static NSString *const kIdentifierKey = @"identifier"; + +/** @var kRequestURIKey + @brief The key for the "requestUri" value in the request. + */ +static NSString *const kRequestURIKey = @"requestUri"; + +/** @var kPostBodyKey + @brief The key for the "postBody" value in the request. + */ +static NSString *const kPostBodyKey = @"postBody"; + +/** @var kPendingIDTokenKey + @brief The key for the "pendingIdToken" value in the request. + */ +static NSString *const kPendingIDTokenKey = @"pendingIdToken"; + +/** @var kAutoCreateKey + @brief The key for the "autoCreate" value in the request. + */ +static NSString *const kAutoCreateKey = @"autoCreate"; + +/** @var kIDTokenKey + @brief The key for the "idToken" value in the request. This is actually the STS Access Token, + despite it's confusing (backwards compatiable) parameter name. + */ +static NSString *const kIDTokenKey = @"idToken"; + +/** @var kReturnSecureTokenKey + @brief The key for the "returnSecureToken" value in the request. + */ +static NSString *const kReturnSecureTokenKey = @"returnSecureToken"; + +@implementation FIRVerifyAssertionRequest + +- (nullable instancetype)initWithAPIKey:(NSString *)APIKey + providerID:(nonnull NSString *)providerID{ + self = [super initWithEndpoint:kVerifyAssertionEndpoint APIKey:APIKey]; + if (self) { + _providerID = providerID; + _returnSecureToken = YES; + _autoCreate = YES; + } + return self; +} + +- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)error { + NSMutableDictionary *postBody = [@{ + kProviderIDKey : _providerID, + } mutableCopy]; + + if (_providerIDToken) { + postBody[kProviderIDTokenKey] = _providerIDToken; + } + + if (_providerAccessToken) { + postBody[kProviderAccessTokenKey] = _providerAccessToken; + } + + if (!_providerIDToken && !_providerAccessToken) { + [NSException raise:NSInvalidArgumentException + format:@"Either IDToken or accessToken must be supplied."]; + } + + if (_providerOAuthTokenSecret) { + postBody[kProviderOAuthTokenSecretKey] = _providerOAuthTokenSecret; + } + + if (_inputEmail) { + postBody[kIdentifierKey] = _inputEmail; + } + + NSMutableDictionary *body = [@{ + kRequestURIKey : @"http://localhost", // Unused by server, but required + kPostBodyKey : [postBody gtm_httpArgumentsString] + } mutableCopy]; + + if (_pendingIDToken) { + body[kPendingIDTokenKey] = _pendingIDToken; + } + if (_accessToken) { + body[kIDTokenKey] = _accessToken; + } + if (_returnSecureToken) { + body[kReturnSecureTokenKey] = @YES; + } + + body[kAutoCreateKey] = @(_autoCreate); + + return body; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyAssertionResponse.h b/Firebase/Auth/Source/RPCs/FIRVerifyAssertionResponse.h new file mode 100644 index 0000000..ce796ee --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyAssertionResponse.h @@ -0,0 +1,186 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRVerifyAssertionResponse + @brief Represents the response from the verifyAssertion endpoint. + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/verifyAssertion + */ +@interface FIRVerifyAssertionResponse : NSObject <FIRAuthRPCResponse> + +/** @property federatedID + @brief The unique ID identifies the IdP account. + */ +@property(nonatomic, strong, readonly, nullable) NSString *federatedID; + +/** @property providerID + @brief The IdP ID. For white listed IdPs it's a short domain name e.g. google.com, aol.com, + live.net and yahoo.com. If the "providerId" param is set to OpenID OP identifer other than + the whilte listed IdPs the OP identifier is returned. If the "identifier" param is federated + ID in the createAuthUri request. The domain part of the federated ID is returned. + */ +@property(nonatomic, strong, readonly, nullable) NSString *providerID; + +/** @property localID + @brief The RP local ID if it's already been mapped to the IdP account identified by the + federated ID. + */ +@property(nonatomic, strong, readonly, nullable) NSString *localID; + +/** @property email + @brief The email returned by the IdP. NOTE: The federated login user may not own the email. + */ +@property(nonatomic, strong, readonly, nullable) NSString *email; + +/** @property inputEmail + @brief It's the identifier param in the createAuthUri request if the identifier is an email. It + can be used to check whether the user input email is different from the asserted email. + */ +@property(nonatomic, strong, readonly, nullable) NSString *inputEmail; + +/** @property originalEmail + @brief The original email stored in the mapping storage. It's returned when the federated ID is + associated to a different email. + */ +@property(nonatomic, strong, readonly, nullable) NSString *originalEmail; + +/** @property oauthRequestToken + @brief The user approved request token for the OpenID OAuth extension. + */ +@property(nonatomic, strong, readonly, nullable) NSString *oauthRequestToken; + +/** @property oauthScope + @brief The scope for the OpenID OAuth extension. + */ +@property(nonatomic, strong, readonly, nullable) NSString *oauthScope; + +/** @property firstName + @brief The first name of the user. + */ +@property(nonatomic, strong, readonly, nullable) NSString *firstName; + +/** @property lastName + @brief The last name of the user. + */ +@property(nonatomic, strong, readonly, nullable) NSString *lastName; + +/** @property fullName + @brief The full name of the user. + */ +@property(nonatomic, strong, readonly, nullable) NSString *fullName; + +/** @property nickName + @brief The nick name of the user. + */ +@property(nonatomic, strong, readonly, nullable) NSString *nickName; + +/** @property displayName + @brief The display name of the user. + */ +@property(nonatomic, strong, readonly, nullable) NSString *displayName; + +/** @property IDToken + @brief Either an authorization code suitable for performing an STS token exchange, or the + access token from Secure Token Service, depending on whether @c returnSecureToken is set + on the request. + */ +@property(nonatomic, strong, readonly, nullable) NSString *IDToken; + +/** @property approximateExpirationDate + @brief The approximate expiration date of the access token. + */ +@property(nonatomic, copy, readonly, nullable) NSDate *approximateExpirationDate; + +/** @property refreshToken + @brief The refresh token from Secure Token Service. + */ +@property(nonatomic, strong, readonly, nullable) NSString *refreshToken; + +/** @property action + @brief The action code. + */ +@property(nonatomic, strong, readonly, nullable) NSString *action; + +/** @property language + @brief The language preference of the user. + */ +@property(nonatomic, strong, readonly, nullable) NSString *language; + +/** @property timeZone + @brief The timezone of the user. + */ +@property(nonatomic, strong, readonly, nullable) NSString *timeZone; + +/** @property photoURL + @brief The URI of the public accessible profile picture. + */ +@property(nonatomic, strong, readonly, nullable) NSURL *photoURL; + +/** @property dateOfBirth + @brief The birth date of the IdP account. + */ +@property(nonatomic, strong, readonly, nullable) NSString *dateOfBirth; + +/** @property context + @brief The opaque value used by the client to maintain context info between the authentication + request and the IDP callback. + */ +@property(nonatomic, strong, readonly, nullable) NSString *context; + +/** @property verifiedProvider + @brief When action is 'map', contains the idps which can be used for confirmation. + */ +@property(nonatomic, strong, readonly, nullable) NSArray<NSString *> *verifiedProvider; + +/** @property needConfirmation + @brief Whether the assertion is from a non-trusted IDP and need account linking confirmation. + */ +@property(nonatomic, assign) BOOL needConfirmation; + +/** @property emailRecycled + @brief It's true if the email is recycled. + */ +@property(nonatomic, assign) BOOL emailRecycled; + +/** @property emailVerified + @brief The value is true if the IDP is also the email provider. It means the user owns the + email. + */ +@property(nonatomic, assign) BOOL emailVerified; + +/** @property isNewUser + @brief Flag indicating that the user signing in is a new user and not a returning user. + */ +@property(nonatomic, assign) BOOL isNewUser; + +/** @property profile + @brief Dictionary containing the additional IdP specific information. + */ +@property(nonatomic, readonly, nullable) NSDictionary<NSString *, NSObject *> *profile; + +/** @property username + @brief The name of the user. + */ +@property(nonatomic, strong, readonly, nullable) NSString *username; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyAssertionResponse.m b/Firebase/Auth/Source/RPCs/FIRVerifyAssertionResponse.m new file mode 100644 index 0000000..6ae33c6 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyAssertionResponse.m @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#import "FIRVerifyAssertionResponse.h" + +#import "../Private/FIRAuthErrorUtils.h" + +@implementation FIRVerifyAssertionResponse + +- (BOOL)setWithDictionary:(NSDictionary *)dictionary + error:(NSError *_Nullable *_Nullable)error { + _federatedID = [dictionary[@"federatedId"] copy]; + _providerID = [dictionary[@"providerId"] copy]; + _localID = [dictionary[@"localId"] copy]; + _emailRecycled = [dictionary[@"emailRecycled"] boolValue]; + _emailVerified = [dictionary[@"emailVerified"] boolValue]; + _email = [dictionary[@"email"] copy]; + _inputEmail = [dictionary[@"inputEmail"] copy]; + _originalEmail = [dictionary[@"originalEmail"] copy]; + _oauthRequestToken = [dictionary[@"oauthRequestToken"] copy]; + _oauthScope = [dictionary[@"oauthScope"] copy]; + _firstName = [dictionary[@"firstName"] copy]; + _lastName = [dictionary[@"lastName"] copy]; + _fullName = [dictionary[@"fullName"] copy]; + _nickName = [dictionary[@"nickName"] copy]; + _displayName = [dictionary[@"displayName"] copy]; + _IDToken = [dictionary[@"idToken"] copy]; + _approximateExpirationDate = [dictionary[@"expiresIn"] isKindOfClass:[NSString class]] ? + [NSDate dateWithTimeIntervalSinceNow:[dictionary[@"expiresIn"] doubleValue]] : nil; + _refreshToken = [dictionary[@"refreshToken"] copy]; + _isNewUser = [dictionary[@"isNewUser"] boolValue]; + id rawUserInfo = dictionary[@"rawUserInfo"]; + if ([rawUserInfo isKindOfClass:[NSString class]]) { + NSData *data = [rawUserInfo dataUsingEncoding:NSUTF8StringEncoding]; + rawUserInfo = [NSJSONSerialization JSONObjectWithData:data + options:NSJSONReadingMutableLeaves + error:nil]; + } + if ([rawUserInfo isKindOfClass:[NSDictionary class]]) { + _profile = [[NSDictionary alloc] initWithDictionary:rawUserInfo + copyItems:YES]; + } + _username = [dictionary[@"username"] copy]; + _action = [dictionary[@"action"] copy]; + _language = [dictionary[@"language"] copy]; + _timeZone = [dictionary[@"timeZone"] copy]; + _photoURL = dictionary[@"photoUrl"] ? [NSURL URLWithString:dictionary[@"photoUrl"]] : nil; + _dateOfBirth = [dictionary[@"dateOfBirth"] copy]; + _context = [dictionary[@"context"] copy]; + _needConfirmation = [dictionary[@"needConfirmation"] boolValue]; + id verifiedProvider = dictionary[@"verifiedProvider"]; + if ([verifiedProvider isKindOfClass:[NSString class]]) { + NSData *data = [verifiedProvider dataUsingEncoding:NSUTF8StringEncoding]; + verifiedProvider = [NSJSONSerialization JSONObjectWithData:data + options:NSJSONReadingMutableLeaves + error:nil]; + } + if ([verifiedProvider isKindOfClass:[NSArray class]]) { + _verifiedProvider = [[NSArray alloc] initWithArray:verifiedProvider + copyItems:YES]; + } + return YES; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyClientRequest.h b/Firebase/Auth/Source/RPCs/FIRVerifyClientRequest.h new file mode 100644 index 0000000..b5da6b8 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyClientRequest.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#import "FIRIdentityToolkitRequest.h" + +#import "FIRAuthRPCRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRVerifyClientRequest : FIRIdentityToolkitRequest <FIRAuthRPCRequest> + +/** @property appToken + @brief The APNS device token. + */ +@property(nonatomic, readonly, nullable) NSString *appToken; + +/** @property isSandbox + @brief The flag that denotes if the appToken pertains to Sandbox or Production. + */ +@property(nonatomic, assign, readonly) BOOL isSandbox; + +/** @fn initWithEndpoint:APIKey: + @brief Please use initWithAppToken:isSandbox: instead. + */ +- (nullable instancetype)initWithEndpoint:(NSString *)endpoint + APIKey:(NSString *)APIKey NS_UNAVAILABLE; + +/** @fn initWithAppToken:isSandbox: + @brief Designated initializer. + @param appToken The APNS device token. + @param isSandbox The flag indicating whether or not the app token provided is for Sandbox or + Production. + */ +- (nullable instancetype)initWithAppToken:(NSString *)appToken + isSandbox:(BOOL)isSandbox + APIKey:(NSString *)APIKey NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyClientRequest.m b/Firebase/Auth/Source/RPCs/FIRVerifyClientRequest.m new file mode 100644 index 0000000..7b4b469 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyClientRequest.m @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#import "FIRVerifyClientRequest.h" + + +NS_ASSUME_NONNULL_BEGIN + +/** @var kVerifyClientEndpoint + @brief The endpoint for the verifyClient request. + */ +static NSString *const kVerifyClientEndpoint = @"verifyClient"; + +/** @var kAppTokenKey + @brief The key for the appToken request paramenter. + */ +static NSString *const kAPPTokenKey = @"appToken"; + +/** @var kIsSandboxKey + @brief The key for the isSandbox request parameter + */ +static NSString *const kIsSandboxKey = @"isSandbox"; + +@implementation FIRVerifyClientRequest + +- (nullable instancetype)initWithAppToken:(NSString *)appToken + isSandbox:(BOOL)isSandbox + APIKey:(NSString *)APIKey { + self = [super initWithEndpoint:kVerifyClientEndpoint APIKey:APIKey]; + if (self) { + _appToken = appToken; + _isSandbox = isSandbox; + } + return self; +} + +- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *__autoreleasing _Nullable *)error { + NSMutableDictionary *postBody = [NSMutableDictionary dictionary]; + if (_appToken) { + postBody[kAPPTokenKey] = _appToken; + } + if (_isSandbox) { + postBody[kIsSandboxKey] = @YES; + } + return postBody; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyClientResponse.h b/Firebase/Auth/Source/RPCs/FIRVerifyClientResponse.h new file mode 100644 index 0000000..794256a --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyClientResponse.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRVerifyClientResponse : NSObject <FIRAuthRPCResponse> + +/** @property receipt + @brief Receipt that the APNS token was successfully validated with APNS. + */ +@property(nonatomic, copy, readonly, nullable) NSString *receipt; + +/** @property suggestedTimeOut + @brief The date after which delivery of the silent push notification is considered to have + failed. + */ +@property(nonatomic, copy, readonly, nullable) NSDate *suggestedTimeOutDate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyClientResponse.m b/Firebase/Auth/Source/RPCs/FIRVerifyClientResponse.m new file mode 100644 index 0000000..c2477d2 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyClientResponse.m @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#import "FIRVerifyClientResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation FIRVerifyClientResponse + +- (BOOL)setWithDictionary:(NSDictionary *)dictionary + error:(NSError *_Nullable *_Nullable)error { + _receipt = dictionary[@"receipt"]; + _suggestedTimeOutDate = [dictionary[@"suggestedTimeout"] isKindOfClass:[NSString class]] ? + [NSDate dateWithTimeIntervalSinceNow:[dictionary[@"suggestedTimeout"] doubleValue]] : nil; + return YES; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenRequest.h b/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenRequest.h new file mode 100644 index 0000000..20f3f4d --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenRequest.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCRequest.h" +#import "FIRIdentityToolkitRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRVerifyCustomTokenRequest + @brief Represents the parameters for the verifyCustomToken endpoint. + */ +@interface FIRVerifyCustomTokenRequest : FIRIdentityToolkitRequest <FIRAuthRPCRequest> + +/** @property token + @brief The self-signed token from the client's BYOAuth server. + */ +@property(nonatomic, copy, readonly) NSString *token; + +/** @property returnSecureToken + @brief Whether the response should return access token and refresh token directly. + @remarks The default value is @c YES . + */ +@property(nonatomic, assign) BOOL returnSecureToken; + +/** @fn initWithEndpoint:APIKey: + @brief Please use initWithToken:APIKey: + */ +- (nullable instancetype)initWithEndpoint:(NSString *)endpoint + APIKey:(NSString *)APIKey NS_UNAVAILABLE; + +/** @fn initWithToken:APIKey: + @brief Designated initializer. + @param token The self-signed token from the client's BYOAuth server. + @param APIKey The client's API Key. + */ +- (nullable instancetype)initWithToken:(NSString *)token + APIKey:(NSString *)APIKey NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenRequest.m b/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenRequest.m new file mode 100644 index 0000000..63d72d1 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenRequest.m @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#import "FIRVerifyCustomTokenRequest.h" + +/** @var kVerifyCustomTokenEndpoint + @brief The "verifyPassword" endpoint. + */ +static NSString *const kVerifyCustomTokenEndpoint = @"verifyCustomToken"; + +/** @var kTokenKey + @brief The key for the "token" value in the request. + */ +static NSString *const kTokenKey = @"token"; + +/** @var kReturnSecureTokenKey + @brief The key for the "returnSecureToken" value in the request. + */ +static NSString *const kReturnSecureTokenKey = @"returnSecureToken"; + +@implementation FIRVerifyCustomTokenRequest + +- (nullable instancetype)initWithToken:(NSString *)token + APIKey:(NSString *)APIKey { + self = [super initWithEndpoint:kVerifyCustomTokenEndpoint APIKey:APIKey]; + if (self) { + _token = [token copy]; + _returnSecureToken = YES; + } + return self; +} + +- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)error { + NSMutableDictionary *body = [@{ + kTokenKey : _token + } mutableCopy]; + if (_returnSecureToken) { + body[kReturnSecureTokenKey] = @YES; + } + return body; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenResponse.h b/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenResponse.h new file mode 100644 index 0000000..b8c215c --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenResponse.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRVerifyCustomTokenResponse + @brief Represents the response from the verifyCustomToken endpoint. + */ +@interface FIRVerifyCustomTokenResponse : NSObject <FIRAuthRPCResponse> + +/** @property IDToken + @brief Either an authorization code suitable for performing an STS token exchange, or the + access token from Secure Token Service, depending on whether @c returnSecureToken is set + on the request. + */ +@property(nonatomic, strong, readonly, nullable) NSString *IDToken; + +/** @property approximateExpirationDate + @brief The approximate expiration date of the access token. + */ +@property(nonatomic, copy, readonly, nullable) NSDate *approximateExpirationDate; + +/** @property refreshToken + @brief The refresh token from Secure Token Service. + */ +@property(nonatomic, strong, readonly, nullable) NSString *refreshToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenResponse.m b/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenResponse.m new file mode 100644 index 0000000..f86d94b --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyCustomTokenResponse.m @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#import "FIRVerifyCustomTokenResponse.h" + +#import "../Private/FIRAuthErrorUtils.h" + +@implementation FIRVerifyCustomTokenResponse + +- (BOOL)setWithDictionary:(NSDictionary *)dictionary + error:(NSError *_Nullable *_Nullable)error { + _IDToken = [dictionary[@"idToken"] copy]; + _approximateExpirationDate = [dictionary[@"expiresIn"] isKindOfClass:[NSString class]] ? + [NSDate dateWithTimeIntervalSinceNow:[dictionary[@"expiresIn"] doubleValue]] : nil; + _refreshToken = [dictionary[@"refreshToken"] copy]; + return YES; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPasswordRequest.h b/Firebase/Auth/Source/RPCs/FIRVerifyPasswordRequest.h new file mode 100644 index 0000000..ba54bce --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyPasswordRequest.h @@ -0,0 +1,79 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCRequest.h" +#import "FIRIdentityToolkitRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRVerifyPasswordRequest + @brief Represents the parameters for the verifyPassword endpoint. + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/verifyPassword + */ +@interface FIRVerifyPasswordRequest : FIRIdentityToolkitRequest <FIRAuthRPCRequest> + +/** @property email + @brief The email of the user. + */ +@property(nonatomic, copy) NSString *email; + +/** @property password + @brief The password inputed by the user. + */ +@property(nonatomic, copy) NSString *password; + +/** @property pendingIDToken + @brief The GITKit token for the non-trusted IDP, which is to be confirmed by the user. + */ +@property(nonatomic, copy, nullable) NSString *pendingIDToken; + +/** @property captchaChallenge + @brief The captcha challenge. + */ +@property(nonatomic, copy, nullable) NSString *captchaChallenge; + +/** @property captchaResponse + @brief Response to the captcha. + */ +@property(nonatomic, copy, nullable) NSString *captchaResponse; + +/** @property returnSecureToken + @brief Whether the response should return access token and refresh token directly. + @remarks The default value is @c YES . + */ +@property(nonatomic, assign) BOOL returnSecureToken; + +/** @fn initWithEndpoint:APIKey: + @brief Please use initWithEmail:password:APIKey: + */ +- (nullable instancetype)initWithEndpoint:(NSString *)endpoint + APIKey:(NSString *)APIKey NS_UNAVAILABLE; + +/** @fn initWithEmail:password:APIKey: + @brief Designated initializer. + @param email The email of the user. + @param password The password inputed by the user. + @param APIKey The client's API Key. + */ +- (nullable instancetype)initWithEmail:(NSString *)email + password:(NSString *)password + APIKey:(NSString *)APIKey NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPasswordRequest.m b/Firebase/Auth/Source/RPCs/FIRVerifyPasswordRequest.m new file mode 100644 index 0000000..7a9da8b --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyPasswordRequest.m @@ -0,0 +1,91 @@ +/* + * 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. + */ + +#import "FIRVerifyPasswordRequest.h" + +/** @var kVerifyPasswordEndpoint + @brief The "verifyPassword" endpoint. + */ +static NSString *const kVerifyPasswordEndpoint = @"verifyPassword"; + +/** @var kEmailKey + @brief The key for the "email" value in the request. + */ +static NSString *const kEmailKey = @"email"; + +/** @var kPasswordKey + @brief The key for the "password" value in the request. + */ +static NSString *const kPasswordKey = @"password"; + +/** @var kPendingIDTokenKey + @brief The key for the "pendingIdToken" value in the request. + */ +static NSString *const kPendingIDTokenKey = @"pendingIdToken"; + +/** @var kCaptchaChallengeKey + @brief The key for the "captchaChallenge" value in the request. + */ +static NSString *const kCaptchaChallengeKey = @"captchaChallenge"; + +/** @var kCaptchaResponseKey + @brief The key for the "captchaResponse" value in the request. + */ +static NSString *const kCaptchaResponseKey = @"captchaResponse"; + +/** @var kReturnSecureTokenKey + @brief The key for the "returnSecureToken" value in the request. + */ +static NSString *const kReturnSecureTokenKey = @"returnSecureToken"; + +@implementation FIRVerifyPasswordRequest + +- (nullable instancetype)initWithEmail:(NSString *)email + password:(NSString *)password + APIKey:(NSString *)APIKey { + self = [super initWithEndpoint:kVerifyPasswordEndpoint APIKey:APIKey]; + if (self) { + _email = [email copy]; + _password = [password copy]; + _returnSecureToken = YES; + } + return self; +} + +- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *_Nullable *_Nullable)error { + NSMutableDictionary *postBody = [NSMutableDictionary dictionary]; + if (_email) { + postBody[kEmailKey] = _email; + } + if (_password) { + postBody[kPasswordKey] = _password; + } + if (_pendingIDToken) { + postBody[kPendingIDTokenKey] = _pendingIDToken; + } + if (_captchaChallenge) { + postBody[kCaptchaChallengeKey] = _captchaChallenge; + } + if (_captchaResponse) { + postBody[kCaptchaResponseKey] = _captchaResponse; + } + if (_returnSecureToken) { + postBody[kReturnSecureTokenKey] = @YES; + } + return postBody; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPasswordResponse.h b/Firebase/Auth/Source/RPCs/FIRVerifyPasswordResponse.h new file mode 100644 index 0000000..bed13be --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyPasswordResponse.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @class FIRVerifyPasswordResponse + @brief Represents the response from the verifyPassword endpoint. + @remarks Possible error codes: + - FIRAuthInternalErrorCodeUserDisabled + - FIRAuthInternalErrorCodeEmailNotFound + @see https://developers.google.com/identity/toolkit/web/reference/relyingparty/verifyPassword + */ +@interface FIRVerifyPasswordResponse : NSObject <FIRAuthRPCResponse> + +/** @property localID + @brief The RP local ID if it's already been mapped to the IdP account identified by the + federated ID. + */ +@property(nonatomic, strong, readonly, nullable) NSString *localID; + +/** @property email + @brief The email returned by the IdP. NOTE: The federated login user may not own the email. + */ +@property(nonatomic, strong, readonly, nullable) NSString *email; + +/** @property displayName + @brief The display name of the user. + */ +@property(nonatomic, strong, readonly, nullable) NSString *displayName; + +/** @property IDToken + @brief Either an authorization code suitable for performing an STS token exchange, or the + access token from Secure Token Service, depending on whether @c returnSecureToken is set + on the request. + */ +@property(nonatomic, strong, readonly, nullable) NSString *IDToken; + +/** @property approximateExpirationDate + @brief The approximate expiration date of the access token. + */ +@property(nonatomic, copy, readonly, nullable) NSDate *approximateExpirationDate; + +/** @property refreshToken + @brief The refresh token from Secure Token Service. + */ +@property(nonatomic, strong, readonly, nullable) NSString *refreshToken; + +/** @property photoURL + @brief The URI of the public accessible profile picture. + */ +@property(nonatomic, strong, readonly, nullable) NSURL *photoURL; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPasswordResponse.m b/Firebase/Auth/Source/RPCs/FIRVerifyPasswordResponse.m new file mode 100644 index 0000000..4d0ca10 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyPasswordResponse.m @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#import "FIRVerifyPasswordResponse.h" + +#import "../Private/FIRAuthErrorUtils.h" + +@implementation FIRVerifyPasswordResponse + +- (BOOL)setWithDictionary:(NSDictionary *)dictionary + error:(NSError *_Nullable *_Nullable)error { + _localID = [dictionary[@"localId"] copy]; + _email = [dictionary[@"email"] copy]; + _displayName = [dictionary[@"displayName"] copy]; + _IDToken = [dictionary[@"idToken"] copy]; + _approximateExpirationDate = [dictionary[@"expiresIn"] isKindOfClass:[NSString class]] ? + [NSDate dateWithTimeIntervalSinceNow:[dictionary[@"expiresIn"] doubleValue]] : nil; + _refreshToken = [dictionary[@"refreshToken"] copy]; + _photoURL = dictionary[@"photoUrl"] ? [NSURL URLWithString:dictionary[@"photoUrl"]] : nil; + return YES; +} + +@end diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberRequest.h b/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberRequest.h new file mode 100644 index 0000000..06039b9 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberRequest.h @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#import "FIRIdentityToolkitRequest.h" + +#import "FIRAuthRPCRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRVerifyPhoneNumberRequest : FIRIdentityToolkitRequest <FIRAuthRPCRequest> + +/** @property verificationID + @brief The verification ID obtained from the response of @c sendVerificationCode. +*/ +@property(nonatomic, readonly, nullable) NSString *verificationID; + +/** @property verificationCode + @brief The verification code provided by the user. +*/ +@property(nonatomic, readonly, nullable) NSString *verificationCode; + +/** @property accessToken + @brief The STS Access Token for the authenticated user. + */ +@property(nonatomic, copy, nullable) NSString *accessToken; + +/** @var temporaryProof + @brief The a temporary proof code pertaining to this credentil, returned from the backend. + */ +@property(nonatomic, readonly, nonnull) NSString *temporaryProof; + +/** @var phoneNumber + @brief The a phone number pertaining to this credential, returned from the backend. + */ +@property(nonatomic, readonly, nonnull) NSString *phoneNumber; + +/** @fn initWithEndpoint:APIKey: + @brief Please use initWithPhoneNumber:APIKey: + */ +- (nullable instancetype)initWithEndpoint:(NSString *)endpoint + APIKey:(NSString *)APIKey NS_UNAVAILABLE; + +/** @fn initWithTemporaryProof:phoneNumberAPIKey + @brief Designated initializer. + @param temporaryProof The temporary proof sent by the backed. + @param phoneNumber The phone number associated with the credential to be signed in. + @param APIKey The client's API Key. + */ +- (nullable instancetype)initWithTemporaryProof:(NSString *)temporaryProof + phoneNumber:(NSString *)phoneNumber + APIKey:(NSString *)APIKey NS_DESIGNATED_INITIALIZER; + +/** @fn initWithVerificationID:verificationCode:APIKey + @brief Designated initializer. + @param verificationID The verification ID obtained from the response of @c sendVerificationCode. + @param verificationCode The verification code provided by the user. + @param APIKey The client's API Key. + */ +- (nullable instancetype)initWithVerificationID:(NSString *)verificationID + verificationCode:(NSString *)verificationCode + APIKey:(NSString *)APIKey NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberRequest.m b/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberRequest.m new file mode 100644 index 0000000..b3d1054 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberRequest.m @@ -0,0 +1,97 @@ +/* + * 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. + */ + +#import "FIRVerifyPhoneNumberRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** @var kVerifyPhoneNumberEndPoint + @brief The "verifyPhoneNumber" endpoint. + */ +static NSString *const kVerifyPhoneNumberEndPoint = @"verifyPhoneNumber"; + +/** @var kVerificationIDKey + @brief The key for the verification ID parameter in the request. + */ +static NSString *const kVerificationIDKey = @"sessionInfo"; + +/** @var kVerificationCodeKey + @brief The key for the verification code parameter in the request. + */ +static NSString *const kVerificationCodeKey = @"code"; + +/** @var kIDTokenKey + @brief The key for the "ID Token" value in the request. + */ +static NSString *const kIDTokenKey = @"idToken"; + +/** @var kTemporaryProofKey + @brief The key for the temporary proof value in the request. + */ +static NSString *const kTemporaryProofKey = @"temporaryProof"; + +/** @var kPhoneNumberKey + @brief The key for the phone number value in the request. + */ +static NSString *const kPhoneNumberKey = @"phoneNumber"; + +@implementation FIRVerifyPhoneNumberRequest + +- (nullable instancetype)initWithTemporaryProof:(NSString *)temporaryProof + phoneNumber:(NSString *)phoneNumber + APIKey:(NSString *)APIKey { + self = [super initWithEndpoint:kVerifyPhoneNumberEndPoint APIKey:APIKey]; + if (self) { + _temporaryProof = [temporaryProof copy]; + _phoneNumber = [phoneNumber copy]; + } + return self; +} + +- (nullable instancetype)initWithVerificationID:(NSString *)verificationID + verificationCode:(NSString *)verificationCode + APIKey:(NSString *)APIKey { + self = [super initWithEndpoint:kVerifyPhoneNumberEndPoint APIKey:APIKey]; + if (self) { + _verificationID = verificationID; + _verificationCode = verificationCode; + } + return self; +} + +- (nullable id)unencodedHTTPRequestBodyWithError:(NSError *__autoreleasing _Nullable *)error { + NSMutableDictionary *postBody = [NSMutableDictionary dictionary]; + if (_verificationID) { + postBody[kVerificationIDKey] = _verificationID; + } + if (_verificationCode) { + postBody[kVerificationCodeKey] = _verificationCode; + } + if (_accessToken) { + postBody[kIDTokenKey] = _accessToken; + } + if (_temporaryProof) { + postBody[kTemporaryProofKey] = _temporaryProof; + } + if (_phoneNumber) { + postBody[kPhoneNumberKey] = _phoneNumber; + } + return postBody; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberResponse.h b/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberResponse.h new file mode 100644 index 0000000..f8f4ba2 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberResponse.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#import <Foundation/Foundation.h> + +#import "FIRAuthRPCResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRVerifyPhoneNumberResponse : NSObject <FIRAuthRPCResponse> + +/** @property IDToken + @brief Either an authorization code suitable for performing an STS token exchange, or the + access token from Secure Token Service, depending on whether @c returnSecureToken is set + on the request. + */ +@property(nonatomic, strong, readonly, nullable) NSString *IDToken; + +/** @property refreshToken + @brief The refresh token from Secure Token Service. + */ +@property(nonatomic, strong, readonly, nullable) NSString *refreshToken; + +/** @property localID + @brief The Firebear user ID. + */ +@property(nonatomic, strong, readonly, nullable) NSString *localID; + +/** @property phoneNumber + @brief The verified phone number. + */ +@property(nonatomic, strong, readonly, nullable) NSString *phoneNumber; + +/** @property temporaryProof + @brief The temporary proof code returned by the backend. + */ +@property(nonatomic, strong, readonly, nullable) NSString *temporaryProof; + +/** @property isNewUser + @brief Flag indicating that the user signing in is a new user and not a returning user. + */ +@property(nonatomic, assign) BOOL isNewUser; + +/** @property approximateExpirationDate + @brief The approximate expiration date of the access token. + */ +@property(nonatomic, copy, readonly, nullable) NSDate *approximateExpirationDate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberResponse.m b/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberResponse.m new file mode 100644 index 0000000..acba2c2 --- /dev/null +++ b/Firebase/Auth/Source/RPCs/FIRVerifyPhoneNumberResponse.m @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#import "FIRVerifyPhoneNumberResponse.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation FIRVerifyPhoneNumberResponse + +- (nullable NSString *)expectedKind { + return nil; +} + +- (BOOL)setWithDictionary:(NSDictionary *)dictionary + error:(NSError *_Nullable *_Nullable)error { + _IDToken = [dictionary[@"idToken"] copy]; + _refreshToken = [dictionary[@"refreshToken"] copy]; + _isNewUser = [dictionary[@"isNewUser"] boolValue]; + _localID = [dictionary[@"localId"] copy]; + _phoneNumber = [dictionary[@"phoneNumber"] copy]; + _temporaryProof = [dictionary[@"temporaryProof"] copy]; + _approximateExpirationDate = [dictionary[@"expiresIn"] isKindOfClass:[NSString class]] ? + [NSDate dateWithTimeIntervalSinceNow:[dictionary[@"expiresIn"] doubleValue]] : nil; + return YES; +} + +@end + +NS_ASSUME_NONNULL_END |