[auth-swift] ActionCode classes to Swift (#11181)

This commit is contained in:
Paul Beusterien committed 2023-07-11 16:22:00 -07:00
1 parent a41b7f5c3f
commit 53e5bd6cae
6 files changed
+203 -286

No files matched your search

-183
View File
@@ -82,195 +82,12 @@ static NSString *const kInvalidEmailSignInLinkExceptionMessage =
@"The link provided is not valid for email/link sign-in. Please check the link by calling "
"isSignInWithEmailLink:link: on Auth before attempting to use it for email/link sign-in.";
/** @var kPasswordResetRequestType
@brief The action code type value for resetting password in the check action code response.
*/
static NSString *const kPasswordResetRequestType = @"PASSWORD_RESET";
/** @var kVerifyEmailRequestType
@brief The action code type value for verifying email in the check action code response.
*/
static NSString *const kVerifyEmailRequestType = @"VERIFY_EMAIL";
/** @var kRecoverEmailRequestType
@brief The action code type value for recovering email in the check action code response.
*/
static NSString *const kRecoverEmailRequestType = @"RECOVER_EMAIL";
/** @var kEmailLinkSignInRequestType
@brief The action code type value for an email sign-in link in the check action code response.
*/
static NSString *const kEmailLinkSignInRequestType = @"EMAIL_SIGNIN";
/** @var kVerifyAndChangeEmailRequestType
@brief The action code type value for verifying and changing email in the check action code
response.
*/
static NSString *const kVerifyAndChangeEmailRequestType = @"VERIFY_AND_CHANGE_EMAIL";
/** @var kRevertSecondFactorAdditionRequestType
@brief The action code type value for reverting second factor addition in the check action code
response.
*/
static NSString *const kRevertSecondFactorAdditionRequestType = @"REVERT_SECOND_FACTOR_ADDITION";
/** @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";
#pragma mark - FIRActionCodeInfo
@interface FIRActionCodeInfo ()
/**
@brief The operation being performed.
*/
@property(nonatomic, readwrite) FIRActionCodeOperation operation;
/** @property email
@brief The email address to which the code was sent. The new email address in the case of
FIRActionCodeOperationRecoverEmail.
*/
@property(nonatomic, nullable, readwrite, copy) NSString *email;
/** @property previousEmail
@brief The current email address in the case of FIRActionCodeOperationRecoverEmail.
*/
@property(nonatomic, nullable, readwrite, copy) NSString *previousEmail;
#if TARGET_OS_IOS
/** @property multiFactorInfo
@brief The MultiFactorInfo object of the second factor to be reverted in case of
FIRActionCodeMultiFactorInfoKey.
*/
@property(nonatomic, nullable, readwrite) FIRMultiFactorInfo *multiFactorInfo;
#endif
@end
@implementation FIRActionCodeInfo
- (instancetype)initWithOperation:(FIRActionCodeOperation)operation
email:(NSString *)email
newEmail:(nullable NSString *)newEmail {
self = [super init];
if (self) {
_operation = operation;
if (newEmail) {
_email = [newEmail copy];
_previousEmail = [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;
}
if ([requestType isEqualToString:kRecoverEmailRequestType]) {
return FIRActionCodeOperationRecoverEmail;
}
if ([requestType isEqualToString:kEmailLinkSignInRequestType]) {
return FIRActionCodeOperationEmailLink;
}
if ([requestType isEqualToString:kVerifyAndChangeEmailRequestType]) {
return FIRActionCodeOperationVerifyAndChangeEmail;
}
if ([requestType isEqualToString:kRevertSecondFactorAdditionRequestType]) {
return FIRActionCodeOperationRevertSecondFactorAddition;
}
return FIRActionCodeOperationUnknown;
}
@end
#pragma mark - FIRActionCodeURL
@implementation FIRActionCodeURL
/** @fn FIRAuthParseURL:NSString
@brief Parses an incoming URL into all available query items.
@param urlString The url to be parsed.
@return A dictionary of available query items in the target URL.
*/
+ (NSDictionary<NSString *, NSString *> *)parseURL:(NSString *)urlString {
NSString *linkURL = [NSURLComponents componentsWithString:urlString].query;
if (!linkURL) {
return @{};
}
NSArray<NSString *> *URLComponents = [linkURL componentsSeparatedByString:@"&"];
NSMutableDictionary<NSString *, NSString *> *queryItems =
[[NSMutableDictionary alloc] initWithCapacity:URLComponents.count];
for (NSString *component in URLComponents) {
NSRange equalRange = [component rangeOfString:@"="];
if (equalRange.location != NSNotFound) {
NSString *queryItemKey =
[[component substringToIndex:equalRange.location] stringByRemovingPercentEncoding];
NSString *queryItemValue =
[[component substringFromIndex:equalRange.location + 1] stringByRemovingPercentEncoding];
if (queryItemKey && queryItemValue) {
queryItems[queryItemKey] = queryItemValue;
}
}
}
return queryItems;
}
+ (nullable instancetype)actionCodeURLWithLink:(NSString *)link {
NSDictionary<NSString *, NSString *> *queryItems = [FIRActionCodeURL parseURL:link];
if (!queryItems.count) {
NSURLComponents *urlComponents = [NSURLComponents componentsWithString:link];
queryItems = [FIRActionCodeURL parseURL:urlComponents.query];
}
if (!queryItems.count) {
return nil;
}
NSString *APIKey = queryItems[@"apiKey"];
NSString *actionCode = queryItems[@"oobCode"];
NSString *continueURLString = queryItems[@"continueUrl"];
NSString *languageCode = queryItems[@"languageCode"];
NSString *mode = queryItems[@"mode"];
NSString *tenantID = queryItems[@"tenantID"];
return [[FIRActionCodeURL alloc] initWithAPIKey:APIKey
actionCode:actionCode
continueURLString:continueURLString
languageCode:languageCode
mode:mode
tenantID:tenantID];
}
- (nullable instancetype)initWithAPIKey:(NSString *)APIKey
actionCode:(NSString *)actionCode
continueURLString:(NSString *)continueURLString
languageCode:(NSString *)languageCode
mode:(NSString *)mode
tenantID:(NSString *)tenantID {
self = [super init];
if (self) {
_APIKey = APIKey;
_operation = [FIRActionCodeInfo actionCodeOperationForRequestType:mode];
_code = actionCode;
_continueURL = [NSURL URLWithString:continueURLString];
_languageCode = languageCode;
}
return self;
}
@end
#pragma mark - FIRAuth
#if TARGET_OS_IOS
@@ -36,6 +36,8 @@
@protocol FIRAuthStorage;
@class FIRAuthAPNSTokenManager;
@class FIRAuthAppCredentialManager;
@class FIRActionCodeInfo;
@class FIRActionCodeURL;
NS_ASSUME_NONNULL_BEGIN
@@ -176,109 +178,6 @@ typedef void (^FIRApplyActionCodeCallback)(NSError *_Nullable error)
typedef void (^FIRAuthVoidErrorCallback)(NSError *_Nullable)
NS_SWIFT_UNAVAILABLE("Use Swift's closure syntax instead.");
/** @class FIRActionCodeInfo
@brief Manages information regarding action codes.
*/
NS_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,
/** Action code for recover email operation. */
FIRActionCodeOperationRecoverEmail = 3,
/** Action code for email link operation. */
FIRActionCodeOperationEmailLink = 4,
/** Action code for verifying and changing email */
FIRActionCodeOperationVerifyAndChangeEmail = 5,
/** Action code for reverting second factor addition */
FIRActionCodeOperationRevertSecondFactorAddition = 6,
} NS_SWIFT_NAME(ActionCodeOperation);
/**
@brief The operation being performed.
*/
@property(nonatomic, readonly) FIRActionCodeOperation operation;
/** @property email
@brief The email address to which the code was sent. The new email address in the case of
`ActionCodeOperationRecoverEmail`.
*/
@property(nonatomic, nullable, readonly, copy) NSString *email;
/** @property previousEmail
@brief The email that is being recovered in the case of `ActionCodeOperationRecoverEmail`.
*/
@property(nonatomic, nullable, readonly, copy) NSString *previousEmail;
/** @fn init
@brief please use initWithOperation: instead.
*/
- (instancetype)init NS_UNAVAILABLE;
@end
/** @class FIRActionCodeURL
@brief This class will allow developers to easily extract information about out of band links.
*/
NS_SWIFT_NAME(ActionCodeURL)
@interface FIRActionCodeURL : NSObject
/** @property APIKey
@brief Returns the API key from the link. nil, if not provided.
*/
@property(nonatomic, nullable, copy, readonly) NSString *APIKey;
/** @property operation
@brief Returns the mode of oob action. The property will be of `FIRActionCodeOperation` type.
It will return `FIRActionCodeOperationUnknown` if no oob action is provided.
*/
@property(nonatomic, readonly) FIRActionCodeOperation operation;
/** @property code
@brief Returns the email action code from the link. nil, if not provided.
*/
@property(nonatomic, nullable, copy, readonly) NSString *code;
/** @property continueURL
@brief Returns the continue URL from the link. nil, if not provided.
*/
@property(nonatomic, nullable, copy, readonly) NSURL *continueURL;
/** @property languageCode
@brief Returns the language code from the link. nil, if not provided.
*/
@property(nonatomic, nullable, copy, readonly) NSString *languageCode;
/** @fn actionCodeURLWithLink:
@brief Construct an `ActionCodeURL` from an out of band link (e.g. email link).
@param link The oob link string used to construct the action code URL.
@return The `ActionCodeURL` object constructed based on the oob link provided.
*/
+ (nullable instancetype)actionCodeURLWithLink:(NSString *)link;
/** @fn init
@brief Please use `init(link:)` in Swift or `actionCodeURLWithLink:` in Objective-C
instead.
*/
- (instancetype)init NS_UNAVAILABLE;
@end
/** @typedef FIRCheckActionCodeCallBack
@brief The type of block invoked when performing a check action code operation.
@@ -0,0 +1,68 @@
// Copyright 2023 Google LLC
//
// 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
/** @class ActionCodeInfo
@brief Manages information regarding action codes.
*/
@objc(FIRActionCodeInfo) public class ActionCodeInfo: NSObject {
/**
@brief The operation being performed.
*/
@objc public let operation: ActionCodeOperation
/** @property email
@brief The email address to which the code was sent. The new email address in the case of
`ActionCodeOperationRecoverEmail`.
*/
@objc public let email: String
/** @property previousEmail
@brief The email that is being recovered in the case of `ActionCodeOperationRecoverEmail`.
*/
@objc public let previousEmail: String?
// TODO: Below here change to internal.
@objc public init(withOperation operation: ActionCodeOperation, email: String,
newEmail: String?) {
self.operation = operation
if let newEmail {
self.email = newEmail
previousEmail = email
} else {
self.email = email
previousEmail = nil
}
}
/** @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 ActionCodeOperation for the supplied request type.
*/
@objc public
class func actionCodeOperation(forRequestType requestType: String?) -> ActionCodeOperation {
switch requestType {
case "PASSWORD_RESET": return .passwordReset
case "VERIFY_EMAIL": return .verifyEmail
case "RECOVER_EMAIL": return .recoverEmail
case "EMAIL_SIGNIN": return .emailLink
case "VERIFY_AND_CHANGE_EMAIL": return .verifyAndChangeEmail
case "REVERT_SECOND_FACTOR_ADDITION": return .revertSecondFactorAddition
default: return .unknown
}
}
}
@@ -0,0 +1,42 @@
// Copyright 2023 Google LLC
//
// 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
/**
@brief Operations which can be performed with action codes.
*/
@objc(FIRActionCodeOperation) public enum ActionCodeOperation : Int, @unchecked Sendable {
/** Action code for unknown operation. */
case unknown = 0
/** Action code for password reset operation. */
case passwordReset = 1
/** Action code for verify email operation. */
case verifyEmail = 2
/** Action code for recover email operation. */
case recoverEmail = 3
/** Action code for email link operation. */
case emailLink = 4
/** Action code for verifying and changing email */
case verifyAndChangeEmail = 5
/** Action code for reverting second factor addition */
case revertSecondFactorAddition = 6
}
@@ -0,0 +1,91 @@
// Copyright 2023 Google LLC
//
// 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
/** @class FIRActionCodeURL
@brief This class will allow developers to easily extract information about out of band links.
*/
@objc(FIRActionCodeURL) public class ActionCodeURL: NSObject {
/** @property APIKey
@brief Returns the API key from the link. nil, if not provided.
*/
@objc public let apiKey: String?
/** @property operation
@brief Returns the mode of oob action. The property will be of `FIRActionCodeOperation` type.
It will return `FIRActionCodeOperationUnknown` if no oob action is provided.
*/
@objc public let operation: ActionCodeOperation
/** @property code
@brief Returns the email action code from the link. nil, if not provided.
*/
@objc public let code: String?
/** @property continueURL
@brief Returns the continue URL from the link. nil, if not provided.
*/
@objc public let continueURL: URL?
/** @property languageCode
@brief Returns the language code from the link. nil, if not provided.
*/
@objc public let languageCode: String?
/** @fn actionCodeURLWithLink:
@brief Construct an `ActionCodeURL` from an out of band link (e.g. email link).
@param link The oob link string used to construct the action code URL.
@return The `ActionCodeURL` object constructed based on the oob link provided.
*/
@objc(actionCodeURLWithLink:) public init?(link: String) {
var queryItems = ActionCodeURL.parseURL(link)
if queryItems.count == 0 {
let urlComponents = URLComponents(string: link)
if let query = urlComponents?.query {
queryItems = ActionCodeURL.parseURL(query)
}
}
guard queryItems.count > 0 else {
return nil
}
apiKey = queryItems["apiKey"]
operation = ActionCodeInfo.actionCodeOperation(forRequestType: queryItems["mode"])
code = queryItems["oobCode"]
if let continueURL = queryItems["continueUrl"] {
self.continueURL = URL(string: continueURL)
} else {
continueURL = nil
}
languageCode = queryItems["languageCode"]
}
// TODO: internal below here.
@objc public class func parseURL(_ urlString: String) -> [String: String] {
guard let linkURL = URLComponents(string: urlString)?.query else {
return [:]
}
var queryItems: [String: String] = [:]
let urlComponents = linkURL.components(separatedBy: "&")
for component in urlComponents {
let splitArray = component.split(separator: "=")
if let queryItemKey = String(splitArray[0]).removingPercentEncoding,
let queryItemValue = String(splitArray[1]).removingPercentEncoding {
queryItems[queryItemKey] = queryItemValue
}
}
return queryItems
}
}