Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AccountMigrationManager with OTP accounts import and export methods #240

Merged
merged 8 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Unreleased
#### Added
- Implement Export and Import functions for OTP accounts in Authenticator module [SDKS-2730]

# Version 4.1.1

## [4.1.1]
Expand Down
61 changes: 60 additions & 1 deletion FRAuthenticator/FRAuthenticator.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
Expand All @@ -18,8 +18,13 @@
1BB34B44299704A300729300 /* AccountError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BB34B43299704A300729300 /* AccountError.swift */; };
1BB34B46299AD30100729300 /* InvalidFakePolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BB34B45299AD30100729300 /* InvalidFakePolicy.swift */; };
1BB34B48299AE08300729300 /* URIType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BB34B47299AE08300729300 /* URIType.swift */; };
A5285C122AD885DE00A8CE01 /* otpauth-migration.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5285C102AD885DE00A8CE01 /* otpauth-migration.pb.swift */; };
A56A46EF2A4A5601000C1055 /* RequestInterceptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A56A46EE2A4A5601000C1055 /* RequestInterceptorTests.swift */; };
A5B7D8362AD703C400972032 /* AccountMigrationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B7D8352AD703C400972032 /* AccountMigrationManager.swift */; };
A5EAD7D228356B5F000637F0 /* BiometricAuthentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5EAD7D128356B5F000637F0 /* BiometricAuthentication.swift */; };
A5FDBA9E2ADEBAF0009679C6 /* AccountMigrationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5FDBA9D2ADEBAF0009679C6 /* AccountMigrationError.swift */; };
A5FDBAA12ADF333C009679C6 /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = A5FDBAA02ADF333C009679C6 /* SwiftProtobuf */; };
A5FDBAA52ADF35F6009679C6 /* AccountMigrationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5FDBAA42ADF35F6009679C6 /* AccountMigrationManagerTests.swift */; };
D5230BE02457868B004AB6E9 /* FRAConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5230BDF2457868B004AB6E9 /* FRAConstants.swift */; };
D525BE01256765190012CE33 /* FRCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D525BDFC2567650B0012CE33 /* FRCore.framework */; };
D525BE02256765190012CE33 /* FRCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D525BDFC2567650B0012CE33 /* FRCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
Expand Down Expand Up @@ -202,8 +207,12 @@
1BB34B43299704A300729300 /* AccountError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountError.swift; sourceTree = "<group>"; };
1BB34B45299AD30100729300 /* InvalidFakePolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvalidFakePolicy.swift; sourceTree = "<group>"; };
1BB34B47299AE08300729300 /* URIType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URIType.swift; sourceTree = "<group>"; };
A5285C102AD885DE00A8CE01 /* otpauth-migration.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "otpauth-migration.pb.swift"; sourceTree = "<group>"; };
A56A46EE2A4A5601000C1055 /* RequestInterceptorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestInterceptorTests.swift; sourceTree = "<group>"; };
A5B7D8352AD703C400972032 /* AccountMigrationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountMigrationManager.swift; sourceTree = "<group>"; };
A5EAD7D128356B5F000637F0 /* BiometricAuthentication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BiometricAuthentication.swift; sourceTree = "<group>"; };
A5FDBA9D2ADEBAF0009679C6 /* AccountMigrationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountMigrationError.swift; sourceTree = "<group>"; };
A5FDBAA42ADF35F6009679C6 /* AccountMigrationManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountMigrationManagerTests.swift; sourceTree = "<group>"; };
D5230BDF2457868B004AB6E9 /* FRAConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FRAConstants.swift; sourceTree = "<group>"; };
D525BDF62567650B0012CE33 /* FRCore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FRCore.xcodeproj; path = ../FRCore/FRCore.xcodeproj; sourceTree = "<group>"; };
D52D5C372412ECA800835035 /* MechanismError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MechanismError.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -326,6 +335,7 @@
buildActionMask = 2147483647;
files = (
D525BE01256765190012CE33 /* FRCore.framework in Frameworks */,
A5FDBAA12ADF333C009679C6 /* SwiftProtobuf in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -375,6 +385,22 @@
path = Biometric;
sourceTree = "<group>";
};
A5FDBAA22ADF3583009679C6 /* OTPAuthMigration */ = {
isa = PBXGroup;
children = (
A5285C102AD885DE00A8CE01 /* otpauth-migration.pb.swift */,
);
path = OTPAuthMigration;
sourceTree = "<group>";
};
A5FDBAA32ADF35D3009679C6 /* Manager */ = {
isa = PBXGroup;
children = (
A5FDBAA42ADF35F6009679C6 /* AccountMigrationManagerTests.swift */,
);
path = Manager;
sourceTree = "<group>";
};
D525BDF72567650B0012CE33 /* Products */ = {
isa = PBXGroup;
children = (
Expand All @@ -401,6 +427,7 @@
D5B55C1A2432870600B49320 /* OathError.swift */,
D5FFA03A245F52E8003680C1 /* PushNotificationError.swift */,
D5FFA045245F5560003680C1 /* CryptoError.swift */,
A5FDBA9D2ADEBAF0009679C6 /* AccountMigrationError.swift */,
);
path = Error;
sourceTree = "<group>";
Expand Down Expand Up @@ -442,6 +469,7 @@
D52D5C65241333D500835035 /* UnitTests */ = {
isa = PBXGroup;
children = (
A5FDBAA32ADF35D3009679C6 /* Manager */,
D5D0A51A24621C0700BA9FE0 /* Crypto */,
D5CC1DC52460C9EC00D0CB94 /* JWT */,
D541AC072432B8090057A060 /* Error */,
Expand Down Expand Up @@ -771,6 +799,7 @@
D5C3549A2412EC0600695A84 /* Model */ = {
isa = PBXGroup;
children = (
A5FDBAA22ADF3583009679C6 /* OTPAuthMigration */,
D5C354A12412EC0600695A84 /* Account */,
D5C3549B2412EC0600695A84 /* Mechanism */,
D5C3549F2412EC0600695A84 /* Notification */,
Expand Down Expand Up @@ -809,6 +838,7 @@
isa = PBXGroup;
children = (
D5C9A1AF24636A73004D5941 /* AuthenticatorManager.swift */,
A5B7D8352AD703C400972032 /* AccountMigrationManager.swift */,
);
path = Manager;
sourceTree = "<group>";
Expand Down Expand Up @@ -930,6 +960,9 @@
D525BE00256765130012CE33 /* PBXTargetDependency */,
);
name = FRAuthenticator;
packageProductDependencies = (
A5FDBAA02ADF333C009679C6 /* SwiftProtobuf */,
);
productName = FRAuthenticator;
productReference = D5C354802412EBF800695A84 /* FRAuthenticator.framework */;
productType = "com.apple.product-type.framework";
Expand Down Expand Up @@ -980,6 +1013,9 @@
Base,
);
mainGroup = D5C354762412EBF800695A84;
packageReferences = (
A5FDBA9F2ADF31A0009679C6 /* XCRemoteSwiftPackageReference "swift-protobuf" */,
);
productRefGroup = D5C354812412EBF800695A84 /* Products */;
projectDirPath = "";
projectReferences = (
Expand Down Expand Up @@ -1080,6 +1116,7 @@
D5E3089F2473942300724502 /* OathTokenCode.swift in Sources */,
D5C9A1B024636A73004D5941 /* AuthenticatorManager.swift in Sources */,
D5B55C1F24329ABA00B49320 /* Base32.swift in Sources */,
A5285C122AD885DE00A8CE01 /* otpauth-migration.pb.swift in Sources */,
D5D9147A242E844600E24ECA /* OathCodeGenerator.swift in Sources */,
D52D5C52241318F000835035 /* PushQRCodeParser.swift in Sources */,
D5FFA05A245FB319003680C1 /* CodableValue.swift in Sources */,
Expand All @@ -1097,7 +1134,9 @@
1BB34B3629957D8F00729300 /* BiometricAvailablePolicy.swift in Sources */,
D5C354A32412EC0600695A84 /* Mechanism.swift in Sources */,
D52D5C5A24131B3700835035 /* TOTPMechanism.swift in Sources */,
A5FDBA9E2ADEBAF0009679C6 /* AccountMigrationError.swift in Sources */,
D542AA3D2416B41900ECBFC1 /* StorageClient.swift in Sources */,
A5B7D8362AD703C400972032 /* AccountMigrationManager.swift in Sources */,
D5E308982473821000724502 /* OathMechanism.swift in Sources */,
D5B55C1B2432870600B49320 /* OathError.swift in Sources */,
D5FFA046245F5560003680C1 /* CryptoError.swift in Sources */,
Expand Down Expand Up @@ -1126,6 +1165,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A5FDBAA52ADF35F6009679C6 /* AccountMigrationManagerTests.swift in Sources */,
D5791C1125F88393004B487A /* FRBaseTestCase.swift in Sources */,
D5791C1225F88393004B487A /* FRTestLogger.swift in Sources */,
D5791C1325F88393004B487A /* FRTestStubResponseParser.swift in Sources */,
Expand Down Expand Up @@ -1457,6 +1497,25 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
A5FDBA9F2ADF31A0009679C6 /* XCRemoteSwiftPackageReference "swift-protobuf" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/apple/swift-protobuf.git";
requirement = {
kind = exactVersion;
version = 1.24.0;
};
};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
A5FDBAA02ADF333C009679C6 /* SwiftProtobuf */ = {
isa = XCSwiftPackageProductDependency;
package = A5FDBA9F2ADF31A0009679C6 /* XCRemoteSwiftPackageReference "swift-protobuf" */;
productName = SwiftProtobuf;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = D5C354772412EBF800695A84 /* Project object */;
}
29 changes: 28 additions & 1 deletion FRAuthenticator/FRAuthenticator/Base32/Base32.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Base32.swift
// FRAuthenticator
//
// Copyright (c) 2020 ForgeRock. All rights reserved.
// Copyright (c) 2020-2023 ForgeRock. All rights reserved.
//
// This software may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.
Expand Down Expand Up @@ -60,3 +60,30 @@ extension String {
return Data.init(bytes: decoded, count: Int(result))
}
}


extension Data {

/// Base32-encodes current Data and returns encoded value in String
func base32Encode() -> String? {
if let encodedData: Data = self.base32Encode() {
return String(bytes: encodedData, encoding: .utf8)
}
return nil
}


/// Base32-encodes current Data and returns encoded value in bytes
func base32Encode() -> Data? {
let encoded = UnsafeMutablePointer<Int8>.allocate(capacity: 4096)

let result = self.withUnsafeBytes({ (ptr : UnsafeRawBufferPointer) in
base32_encode(ptr.baseAddress!, Int32(ptr.count), encoded, 4096)
})
if result < 0 {
return nil
}

return Data.init(bytes: encoded, count: Int(result))
}
}
3 changes: 2 additions & 1 deletion FRAuthenticator/FRAuthenticator/Constants/AuthType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// AuthType.swift
// FRAuthenticator
//
// Copyright (c) 2020 ForgeRock. All rights reserved.
// Copyright (c) 2020-2023 ForgeRock. All rights reserved.
//
// This software may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.
Expand All @@ -15,5 +15,6 @@ enum AuthType: String {
case totp = "totp"
case hotp = "hotp"
case push = "push"
case offline = "offline"
case unknown
}
1 change: 1 addition & 0 deletions FRAuthenticator/FRAuthenticator/Constants/URIType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ enum URIType: String {
case otpauth = "otpauth"
case pushauth = "pushauth"
case mfauth = "mfauth"
case otpauthMigration = "otpauth-migration"
case unknown
}
79 changes: 79 additions & 0 deletions FRAuthenticator/FRAuthenticator/Error/AccountMigrationError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//
// AccountMigrationError.swift
// FRAuthenticator
//
// Copyright (c) 2023 ForgeRock. All rights reserved.
//
// This software may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.
//


import Foundation
import FRCore


/// AccountMigrationError represents an error captured by FRAuthenticator SDK for any operations related to Account Migration
///
/// - invalidScheme: Unsupported scheme
/// - invalidHost: Unsupported host
/// - missingData: Missing `data` parameter
/// - failToDecodeData: Unable to decode data
public enum AccountMigrationError: FRError {
case invalidScheme
case invalidHost
case missingData
case failToDecodeData
}


public extension AccountMigrationError {

/// Unique error code for given error
var code: Int {
return self.parseErrorCode()
}

/// Parses AccountMigrationError value into integer error code
///
/// - Returns: Int value of unique error code
func parseErrorCode() -> Int {
switch self {
case .invalidScheme:
return 1500000
case .invalidHost:
return 1500001
case .missingData:
return 1500002
case .failToDecodeData:
return 1500003
}
}
}


// MARK: - CustomNSError protocols
extension AccountMigrationError: CustomNSError {

/// An error domain for AccountMigrationError
public static var errorDomain: String { return "com.forgerock.ios.frauthenticator.accountmigration" }

/// Error codes for each error enum
public var errorCode: Int {
return self.parseErrorCode()
}

/// Error UserInfo
public var errorUserInfo: [String : Any] {
switch self {
case .invalidScheme:
return [NSLocalizedDescriptionKey: "Invalid scheme in url given for Account Migration"]
case .invalidHost:
return [NSLocalizedDescriptionKey: "Invalid host in url given for Account Migration"]
case .missingData:
return [NSLocalizedDescriptionKey: "Missing data parameter in url given for Account Migration"]
case .failToDecodeData:
return [NSLocalizedDescriptionKey: "failed to decode data parameter avlue in url given for Account Migration"]
}
}
}
Loading
Loading