diff options
Diffstat (limited to 'Firebase/Auth/Source/AuthProviders/Phone')
7 files changed, 547 insertions, 0 deletions
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 |