@hawcx/react-native-sdk 1.0.8 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -1
- package/HawcxReactNative.podspec +2 -2
- package/README.md +325 -109
- package/android/build.gradle +2 -2
- package/android/src/main/java/com/hawcx/reactnative/HawcxEventDispatcher.kt +4 -0
- package/android/src/main/java/com/hawcx/reactnative/HawcxReactNativeModule.kt +324 -1
- package/android/src/main/java/com/hawcx/reactnative/v6/HawcxV6Bridge.kt +402 -0
- package/ios/Frameworks/HawcxFramework.xcframework/Info.plist +5 -5
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/HawcxFramework +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Info.plist +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.abi.json +22145 -2
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.private.swiftinterface +628 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.swiftinterface +628 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.swiftmodule +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/HawcxFramework +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Info.plist +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.abi.json +22145 -2
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +628 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.swiftinterface +628 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.swiftmodule +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.abi.json +22145 -2
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +628 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +628 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.swiftmodule +0 -0
- package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/_CodeSignature/CodeResources +21 -21
- package/ios/HawcxReactNative.m +56 -0
- package/ios/HawcxReactNative.swift +380 -1
- package/ios/HawcxV6BridgeSupport.swift +468 -0
- package/lib/commonjs/index.js +326 -3
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/v6Normalization.js +325 -0
- package/lib/commonjs/v6Normalization.js.map +1 -0
- package/lib/commonjs/v6State.js +186 -0
- package/lib/commonjs/v6State.js.map +1 -0
- package/lib/commonjs/v6Types.js +2 -0
- package/lib/commonjs/v6Types.js.map +1 -0
- package/lib/commonjs/v6WebLogin.js +101 -0
- package/lib/commonjs/v6WebLogin.js.map +1 -0
- package/lib/module/index.js +287 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/v6Normalization.js +318 -0
- package/lib/module/v6Normalization.js.map +1 -0
- package/lib/module/v6State.js +173 -0
- package/lib/module/v6State.js.map +1 -0
- package/lib/module/v6Types.js +2 -0
- package/lib/module/v6Types.js.map +1 -0
- package/lib/module/v6WebLogin.js +92 -0
- package/lib/module/v6WebLogin.js.map +1 -0
- package/lib/typescript/index.d.ts +83 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/v6Normalization.d.ts +3 -0
- package/lib/typescript/v6Normalization.d.ts.map +1 -0
- package/lib/typescript/v6State.d.ts +13 -0
- package/lib/typescript/v6State.d.ts.map +1 -0
- package/lib/typescript/v6Types.d.ts +157 -0
- package/lib/typescript/v6Types.d.ts.map +1 -0
- package/lib/typescript/v6WebLogin.d.ts +32 -0
- package/lib/typescript/v6WebLogin.d.ts.map +1 -0
- package/package.json +21 -9
- package/src/index.ts +477 -0
- package/src/v6Normalization.ts +356 -0
- package/src/v6State.ts +238 -0
- package/src/v6Types.ts +194 -0
- package/src/v6WebLogin.ts +154 -0
- package/android/.settings/org.eclipse.buildship.core.prefs +0 -2
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +0 -6
- package/android/gradlew +0 -185
- package/android/gradlew.bat +0 -89
- package/android/libs/hawcx-5.1.4.aar +0 -0
- package/docs/RELEASE.md +0 -129
- package/example/README.md +0 -59
- package/example/android/app/build.gradle +0 -126
- package/example/android/app/debug.keystore +0 -0
- package/example/android/app/proguard-rules.pro +0 -10
- package/example/android/app/src/debug/AndroidManifest.xml +0 -9
- package/example/android/app/src/main/AndroidManifest.xml +0 -27
- package/example/android/app/src/main/java/com/hawcx/example/MainActivity.kt +0 -22
- package/example/android/app/src/main/java/com/hawcx/example/MainApplication.kt +0 -45
- package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +0 -36
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/values/strings.xml +0 -3
- package/example/android/app/src/main/res/values/styles.xml +0 -9
- package/example/android/build.gradle +0 -35
- package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/example/android/gradle/wrapper/gradle-wrapper.properties +0 -7
- package/example/android/gradle.properties +0 -41
- package/example/android/gradlew +0 -249
- package/example/android/gradlew.bat +0 -92
- package/example/android/local.properties +0 -2
- package/example/android/settings.gradle +0 -38
- package/example/app.json +0 -4
- package/example/babel.config.js +0 -3
- package/example/e2e/README.md +0 -17
- package/example/e2e/hawcx-login.yaml +0 -14
- package/example/index.js +0 -5
- package/example/ios/.xcode.env +0 -11
- package/example/ios/HawcxExampleApp/AppDelegate.h +0 -6
- package/example/ios/HawcxExampleApp/AppDelegate.mm +0 -31
- package/example/ios/HawcxExampleApp/Images.xcassets/AppIcon.appiconset/Contents.json +0 -53
- package/example/ios/HawcxExampleApp/Images.xcassets/Contents.json +0 -6
- package/example/ios/HawcxExampleApp/Info.plist +0 -55
- package/example/ios/HawcxExampleApp/LaunchScreen.storyboard +0 -47
- package/example/ios/HawcxExampleApp/PrivacyInfo.xcprivacy +0 -37
- package/example/ios/HawcxExampleApp/main.m +0 -10
- package/example/ios/HawcxExampleApp.xcodeproj/project.pbxproj +0 -704
- package/example/ios/HawcxExampleApp.xcodeproj/project.xcworkspace/xcuserdata/agambhullar.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/example/ios/HawcxExampleApp.xcodeproj/xcshareddata/xcschemes/HawcxExampleApp.xcscheme +0 -90
- package/example/ios/HawcxExampleApp.xcodeproj/xcuserdata/agambhullar.xcuserdatad/xcschemes/xcschememanagement.plist +0 -16
- package/example/ios/HawcxExampleApp.xcworkspace/contents.xcworkspacedata +0 -10
- package/example/ios/HawcxExampleAppTests/HawcxExampleAppTests.m +0 -66
- package/example/ios/HawcxExampleAppTests/Info.plist +0 -24
- package/example/ios/Podfile +0 -79
- package/example/ios/Podfile.lock +0 -1290
- package/example/metro.config.js +0 -16
- package/example/package-lock.json +0 -13220
- package/example/package.json +0 -30
- package/example/src/App.tsx +0 -755
- package/example/src/hawcx.config.ts +0 -25
- package/example/tsconfig.json +0 -8
- package/ios/Frameworks/.keep +0 -0
- package/lib/typescript/__tests__/index.test.d.ts +0 -2
- package/lib/typescript/__tests__/index.test.d.ts.map +0 -1
- package/react_mobile_sdk_plan.md +0 -242
- package/src/__tests__/index.test.ts +0 -206
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import HawcxFramework
|
|
3
|
+
|
|
4
|
+
#if canImport(React)
|
|
5
|
+
|
|
6
|
+
internal let HAWCX_V6_FLOW_EVENT_NAME = "hawcx.v6.flow.event"
|
|
7
|
+
|
|
8
|
+
internal struct HawcxV6StartOptions {
|
|
9
|
+
let flowType: HawcxV1FlowType
|
|
10
|
+
let identifier: String
|
|
11
|
+
let startToken: String?
|
|
12
|
+
let inviteCode: String?
|
|
13
|
+
let codeChallenge: String?
|
|
14
|
+
|
|
15
|
+
static func from(options: NSDictionary) throws -> HawcxV6StartOptions {
|
|
16
|
+
let identifier = (options["identifier"] as? String)?
|
|
17
|
+
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
18
|
+
.nilIfEmpty
|
|
19
|
+
guard let identifier else {
|
|
20
|
+
throw HawcxV6BridgeError.invalidInput("identifier is required")
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let flowTypeRaw = (options["flowType"] as? String)?
|
|
24
|
+
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
25
|
+
.nilIfEmpty ?? HawcxV1FlowType.signin.rawValue
|
|
26
|
+
guard let flowType = HawcxV1FlowType(rawValue: flowTypeRaw) else {
|
|
27
|
+
throw HawcxV6BridgeError.invalidInput("flowType must be one of signin, signup, or account_manage")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return HawcxV6StartOptions(
|
|
31
|
+
flowType: flowType,
|
|
32
|
+
identifier: identifier,
|
|
33
|
+
startToken: (options["startToken"] as? String)?
|
|
34
|
+
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
35
|
+
.nilIfEmpty,
|
|
36
|
+
inviteCode: (options["inviteCode"] as? String)?
|
|
37
|
+
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
38
|
+
.nilIfEmpty,
|
|
39
|
+
codeChallenge: (options["codeChallenge"] as? String)?
|
|
40
|
+
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
41
|
+
.nilIfEmpty
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
internal struct HawcxV6InitializeOptions {
|
|
47
|
+
let relyingParty: String?
|
|
48
|
+
let autoPollApprovals: Bool
|
|
49
|
+
|
|
50
|
+
static func from(config: NSDictionary) -> HawcxV6InitializeOptions {
|
|
51
|
+
let relyingParty = (config["relyingParty"] as? String)?
|
|
52
|
+
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
53
|
+
.nilIfEmpty
|
|
54
|
+
let autoPollApprovals = (config["autoPollApprovals"] as? NSNumber)?.boolValue ?? true
|
|
55
|
+
return HawcxV6InitializeOptions(
|
|
56
|
+
relyingParty: relyingParty,
|
|
57
|
+
autoPollApprovals: autoPollApprovals
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
internal final class HawcxV6Bridge {
|
|
63
|
+
private weak var emitter: HawcxReactNative?
|
|
64
|
+
private(set) var sdk: HawcxV1SDK?
|
|
65
|
+
|
|
66
|
+
init(emitter: HawcxReactNative) {
|
|
67
|
+
self.emitter = emitter
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
func configure(configId: String, baseURL: URL, options: HawcxV6InitializeOptions) {
|
|
71
|
+
dispose(resetFlow: true)
|
|
72
|
+
|
|
73
|
+
let sdk = HawcxV1SDK(
|
|
74
|
+
configId: configId,
|
|
75
|
+
baseURL: baseURL,
|
|
76
|
+
relyingParty: options.relyingParty,
|
|
77
|
+
autoPollApprovals: options.autoPollApprovals
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
sdk.flow.onUpdate = { [weak self] update in
|
|
81
|
+
self?.emitter?.emitV6FlowEvent(HawcxV6FlowEventCodec.encode(update: update))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
self.sdk = sdk
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
func start(options: HawcxV6StartOptions) throws {
|
|
88
|
+
guard let sdk else {
|
|
89
|
+
throw HawcxV6BridgeError.notInitialized
|
|
90
|
+
}
|
|
91
|
+
sdk.start(
|
|
92
|
+
flowType: options.flowType,
|
|
93
|
+
identifier: options.identifier,
|
|
94
|
+
startToken: options.startToken,
|
|
95
|
+
inviteCode: options.inviteCode,
|
|
96
|
+
codeChallenge: options.codeChallenge
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
func selectMethod(_ methodId: String) throws {
|
|
101
|
+
guard let sdk else {
|
|
102
|
+
throw HawcxV6BridgeError.notInitialized
|
|
103
|
+
}
|
|
104
|
+
sdk.flow.selectMethod(methodId)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
func submitCode(_ code: String) throws {
|
|
108
|
+
guard let sdk else {
|
|
109
|
+
throw HawcxV6BridgeError.notInitialized
|
|
110
|
+
}
|
|
111
|
+
sdk.flow.submitCode(code)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
func submitTotp(_ code: String) throws {
|
|
115
|
+
guard let sdk else {
|
|
116
|
+
throw HawcxV6BridgeError.notInitialized
|
|
117
|
+
}
|
|
118
|
+
sdk.flow.submitTotp(code)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
func submitPhone(_ phone: String) throws {
|
|
122
|
+
guard let sdk else {
|
|
123
|
+
throw HawcxV6BridgeError.notInitialized
|
|
124
|
+
}
|
|
125
|
+
sdk.flow.submitPhone(phone)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@discardableResult
|
|
129
|
+
func resend() throws -> Bool {
|
|
130
|
+
guard let sdk else {
|
|
131
|
+
throw HawcxV6BridgeError.notInitialized
|
|
132
|
+
}
|
|
133
|
+
return sdk.flow.resend()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
func poll() throws {
|
|
137
|
+
guard let sdk else {
|
|
138
|
+
throw HawcxV6BridgeError.notInitialized
|
|
139
|
+
}
|
|
140
|
+
sdk.flow.poll()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
func cancel() throws {
|
|
144
|
+
guard let sdk else {
|
|
145
|
+
throw HawcxV6BridgeError.notInitialized
|
|
146
|
+
}
|
|
147
|
+
sdk.flow.cancel()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
func approveQr(
|
|
151
|
+
rawPayload: String,
|
|
152
|
+
identifier: String,
|
|
153
|
+
rememberDevice: Bool,
|
|
154
|
+
completion: @escaping (Result<HawcxV6QrApprovalResult, HawcxV1QrApprovalError>) -> Void
|
|
155
|
+
) throws {
|
|
156
|
+
guard let sdk else {
|
|
157
|
+
throw HawcxV6BridgeError.notInitialized
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
guard let payload = HawcxV1QrPayloadParser.parse(rawPayload) else {
|
|
161
|
+
throw HawcxV6BridgeError.invalidInput("rawPayload must be a valid Hawcx QR payload")
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let trimmedIdentifier = identifier.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
165
|
+
guard !trimmedIdentifier.isEmpty else {
|
|
166
|
+
throw HawcxV6BridgeError.invalidInput("identifier is required")
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
let service = HawcxV1QrApprovalService(
|
|
170
|
+
config: sdk.client.config,
|
|
171
|
+
signer: sdk.crypto
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
service.approve(payload: payload, identifier: trimmedIdentifier, rememberDevice: rememberDevice) { result in
|
|
175
|
+
switch result {
|
|
176
|
+
case .success(.approved):
|
|
177
|
+
completion(.success(.approved(payloadType: payload.type.rawValue)))
|
|
178
|
+
case .success(.bound(let userid)):
|
|
179
|
+
completion(.success(.bound(payloadType: payload.type.rawValue, userId: userid)))
|
|
180
|
+
case .failure(let error):
|
|
181
|
+
completion(.failure(error))
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
func handleRedirect(urlString: String) throws {
|
|
187
|
+
guard let sdk else {
|
|
188
|
+
throw HawcxV6BridgeError.notInitialized
|
|
189
|
+
}
|
|
190
|
+
guard let url = URL(string: urlString),
|
|
191
|
+
let callback = HawcxV1OAuthCallbackParser.parse(url) else {
|
|
192
|
+
throw HawcxV6BridgeError.invalidInput("url must be a valid V6 OAuth callback URL")
|
|
193
|
+
}
|
|
194
|
+
sdk.flow.oauthCallback(code: callback.code, state: callback.state)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
func dispose(resetFlow: Bool) {
|
|
198
|
+
guard let sdk else { return }
|
|
199
|
+
sdk.flow.onUpdate = nil
|
|
200
|
+
if resetFlow {
|
|
201
|
+
sdk.reset()
|
|
202
|
+
}
|
|
203
|
+
self.sdk = nil
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
internal enum HawcxV6QrApprovalResult {
|
|
208
|
+
case approved(payloadType: String)
|
|
209
|
+
case bound(payloadType: String, userId: String?)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
internal enum HawcxV6FlowEventCodec {
|
|
213
|
+
static func encode(update: HawcxV1FlowUpdate) -> [String: Any] {
|
|
214
|
+
switch update {
|
|
215
|
+
case .idle:
|
|
216
|
+
return ["type": "idle"]
|
|
217
|
+
case .loading(let session):
|
|
218
|
+
var payload: [String: Any] = [:]
|
|
219
|
+
if let session, !session.isEmpty {
|
|
220
|
+
payload["session"] = session
|
|
221
|
+
}
|
|
222
|
+
return [
|
|
223
|
+
"type": "loading",
|
|
224
|
+
"payload": payload
|
|
225
|
+
]
|
|
226
|
+
case .prompt(let context, let prompt):
|
|
227
|
+
var payload = encodePromptContext(context)
|
|
228
|
+
payload["prompt"] = encodePrompt(prompt)
|
|
229
|
+
return [
|
|
230
|
+
"type": "prompt",
|
|
231
|
+
"payload": payload
|
|
232
|
+
]
|
|
233
|
+
case .completed(let session, let authCode, let expiresAt, let codeVerifier, let meta):
|
|
234
|
+
var payload: [String: Any] = [
|
|
235
|
+
"session": session,
|
|
236
|
+
"authCode": authCode,
|
|
237
|
+
"expiresAt": expiresAt,
|
|
238
|
+
"traceId": meta.traceId
|
|
239
|
+
]
|
|
240
|
+
if let codeVerifier, !codeVerifier.isEmpty {
|
|
241
|
+
payload["codeVerifier"] = codeVerifier
|
|
242
|
+
}
|
|
243
|
+
return [
|
|
244
|
+
"type": "completed",
|
|
245
|
+
"payload": payload
|
|
246
|
+
]
|
|
247
|
+
case .error(let session, let code, let action, let message, let retryable, let details, let meta):
|
|
248
|
+
var payload: [String: Any] = [
|
|
249
|
+
"code": code,
|
|
250
|
+
"message": message,
|
|
251
|
+
"retryable": retryable
|
|
252
|
+
]
|
|
253
|
+
if let session, !session.isEmpty {
|
|
254
|
+
payload["session"] = session
|
|
255
|
+
}
|
|
256
|
+
if let actionRawValue = actionRawValue(action) {
|
|
257
|
+
payload["action"] = actionRawValue
|
|
258
|
+
}
|
|
259
|
+
if let traceId = meta?.traceId, !traceId.isEmpty {
|
|
260
|
+
payload["traceId"] = traceId
|
|
261
|
+
}
|
|
262
|
+
if let details {
|
|
263
|
+
payload["details"] = encodeErrorDetails(details)
|
|
264
|
+
}
|
|
265
|
+
return [
|
|
266
|
+
"type": "error",
|
|
267
|
+
"payload": payload
|
|
268
|
+
]
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private static func encodePromptContext(_ context: HawcxV1PromptContext) -> [String: Any] {
|
|
273
|
+
var payload: [String: Any] = [
|
|
274
|
+
"session": context.session,
|
|
275
|
+
"traceId": context.meta.traceId,
|
|
276
|
+
"expiresAt": context.meta.expiresAt
|
|
277
|
+
]
|
|
278
|
+
if let stepInfo = context.stepInfo {
|
|
279
|
+
payload["step"] = encodeStepInfo(stepInfo)
|
|
280
|
+
}
|
|
281
|
+
if let risk = context.risk {
|
|
282
|
+
payload["risk"] = encodeRiskInfo(risk)
|
|
283
|
+
}
|
|
284
|
+
if let codeChannel = context.codeChannel, !codeChannel.isEmpty {
|
|
285
|
+
payload["codeChannel"] = codeChannel
|
|
286
|
+
}
|
|
287
|
+
return payload
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private static func encodePrompt(_ prompt: HawcxV1UserPrompt) -> [String: Any] {
|
|
291
|
+
switch prompt {
|
|
292
|
+
case let .selectMethod(methods, phase):
|
|
293
|
+
var payload: [String: Any] = [
|
|
294
|
+
"type": "select_method",
|
|
295
|
+
"methods": methods.map(encodeMethod)
|
|
296
|
+
]
|
|
297
|
+
if let phase, !phase.isEmpty {
|
|
298
|
+
payload["phase"] = phase
|
|
299
|
+
}
|
|
300
|
+
return payload
|
|
301
|
+
case let .enterCode(destination, codeLength, codeFormat, codeExpiresAt, resendAt):
|
|
302
|
+
var payload: [String: Any] = [
|
|
303
|
+
"type": "enter_code",
|
|
304
|
+
"destination": destination
|
|
305
|
+
]
|
|
306
|
+
if let codeLength {
|
|
307
|
+
payload["codeLength"] = codeLength
|
|
308
|
+
}
|
|
309
|
+
if let codeFormat, !codeFormat.isEmpty {
|
|
310
|
+
payload["codeFormat"] = codeFormat
|
|
311
|
+
}
|
|
312
|
+
if let codeExpiresAt, !codeExpiresAt.isEmpty {
|
|
313
|
+
payload["codeExpiresAt"] = codeExpiresAt
|
|
314
|
+
}
|
|
315
|
+
if let resendAt, !resendAt.isEmpty {
|
|
316
|
+
payload["resendAt"] = resendAt
|
|
317
|
+
}
|
|
318
|
+
return payload
|
|
319
|
+
case .enterTotp:
|
|
320
|
+
return ["type": "enter_totp"]
|
|
321
|
+
case let .setupTotp(secret, otpauthUrl, period):
|
|
322
|
+
var payload: [String: Any] = [
|
|
323
|
+
"type": "setup_totp",
|
|
324
|
+
"secret": secret,
|
|
325
|
+
"otpauthUrl": otpauthUrl
|
|
326
|
+
]
|
|
327
|
+
if let period {
|
|
328
|
+
payload["period"] = period
|
|
329
|
+
}
|
|
330
|
+
return payload
|
|
331
|
+
case let .setupSms(existingPhone):
|
|
332
|
+
var payload: [String: Any] = [
|
|
333
|
+
"type": "setup_sms"
|
|
334
|
+
]
|
|
335
|
+
if let existingPhone, !existingPhone.isEmpty {
|
|
336
|
+
payload["existingPhone"] = existingPhone
|
|
337
|
+
}
|
|
338
|
+
return payload
|
|
339
|
+
case let .redirect(url, returnScheme):
|
|
340
|
+
var payload: [String: Any] = [
|
|
341
|
+
"type": "redirect",
|
|
342
|
+
"url": url
|
|
343
|
+
]
|
|
344
|
+
if let returnScheme, !returnScheme.isEmpty {
|
|
345
|
+
payload["returnScheme"] = returnScheme
|
|
346
|
+
}
|
|
347
|
+
return payload
|
|
348
|
+
case let .awaitApproval(qrData, expiresAt, pollInterval):
|
|
349
|
+
var payload: [String: Any] = [
|
|
350
|
+
"type": "await_approval",
|
|
351
|
+
"expiresAt": expiresAt,
|
|
352
|
+
"pollInterval": pollInterval
|
|
353
|
+
]
|
|
354
|
+
if let qrData, !qrData.isEmpty {
|
|
355
|
+
payload["qrData"] = qrData
|
|
356
|
+
}
|
|
357
|
+
return payload
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private static func encodeMethod(_ method: HawcxV1Method) -> [String: Any] {
|
|
362
|
+
var payload: [String: Any] = [
|
|
363
|
+
"id": method.id,
|
|
364
|
+
"label": method.label
|
|
365
|
+
]
|
|
366
|
+
if let icon = method.icon, !icon.isEmpty {
|
|
367
|
+
payload["icon"] = icon
|
|
368
|
+
}
|
|
369
|
+
return payload
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
private static func encodeStepInfo(_ step: HawcxV1StepInfo) -> [String: Any] {
|
|
373
|
+
var payload: [String: Any] = ["id": step.id]
|
|
374
|
+
if let label = step.label, !label.isEmpty {
|
|
375
|
+
payload["label"] = label
|
|
376
|
+
}
|
|
377
|
+
return payload
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
private static func encodeRiskInfo(_ risk: HawcxV1RiskInfo) -> [String: Any] {
|
|
381
|
+
var payload: [String: Any] = [
|
|
382
|
+
"detected": risk.detected,
|
|
383
|
+
"reasons": risk.reasons
|
|
384
|
+
]
|
|
385
|
+
if let message = risk.message, !message.isEmpty {
|
|
386
|
+
payload["message"] = message
|
|
387
|
+
}
|
|
388
|
+
if let riskScore = risk.riskScore {
|
|
389
|
+
payload["riskScore"] = riskScore
|
|
390
|
+
}
|
|
391
|
+
if let location = risk.location {
|
|
392
|
+
payload["location"] = encodeRiskLocation(location)
|
|
393
|
+
}
|
|
394
|
+
return payload
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private static func encodeRiskLocation(_ location: HawcxV1RiskLocation) -> [String: Any] {
|
|
398
|
+
var payload: [String: Any] = [:]
|
|
399
|
+
if let city = location.city, !city.isEmpty {
|
|
400
|
+
payload["city"] = city
|
|
401
|
+
}
|
|
402
|
+
if let country = location.country, !country.isEmpty {
|
|
403
|
+
payload["country"] = country
|
|
404
|
+
}
|
|
405
|
+
return payload
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
private static func encodeErrorDetails(_ details: HawcxV1ErrorDetails) -> [String: Any] {
|
|
409
|
+
var payload: [String: Any] = [:]
|
|
410
|
+
if let retryAfterSeconds = details.retryAfterSeconds {
|
|
411
|
+
payload["retryAfterSeconds"] = retryAfterSeconds
|
|
412
|
+
}
|
|
413
|
+
if let retryAt = details.retryAt, !retryAt.isEmpty {
|
|
414
|
+
payload["retryAt"] = retryAt
|
|
415
|
+
}
|
|
416
|
+
if let attemptsRemaining = details.attemptsRemaining {
|
|
417
|
+
payload["attemptsRemaining"] = attemptsRemaining
|
|
418
|
+
}
|
|
419
|
+
if let errors = details.errors, !errors.isEmpty {
|
|
420
|
+
payload["errors"] = errors.map { ["field": $0.field, "message": $0.message] }
|
|
421
|
+
}
|
|
422
|
+
return payload
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
private static func actionRawValue(_ action: HawcxV1ErrorAction?) -> String? {
|
|
426
|
+
guard let action else { return nil }
|
|
427
|
+
switch action {
|
|
428
|
+
case .retryInput:
|
|
429
|
+
return "retry_input"
|
|
430
|
+
case .restartFlow:
|
|
431
|
+
return "restart_flow"
|
|
432
|
+
case .wait:
|
|
433
|
+
return "wait"
|
|
434
|
+
case .retryRequest:
|
|
435
|
+
return "retry_request"
|
|
436
|
+
case .abort:
|
|
437
|
+
return "abort"
|
|
438
|
+
case .resendCode:
|
|
439
|
+
return "resend_code"
|
|
440
|
+
case .selectMethod:
|
|
441
|
+
return "select_method"
|
|
442
|
+
case .unknown(let raw):
|
|
443
|
+
return raw
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
internal enum HawcxV6BridgeError: LocalizedError {
|
|
449
|
+
case notInitialized
|
|
450
|
+
case invalidInput(String)
|
|
451
|
+
|
|
452
|
+
var errorDescription: String? {
|
|
453
|
+
switch self {
|
|
454
|
+
case .notInitialized:
|
|
455
|
+
return "initialize must be called before using V6 bridge methods"
|
|
456
|
+
case let .invalidInput(message):
|
|
457
|
+
return message
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
private extension String {
|
|
463
|
+
var nilIfEmpty: String? {
|
|
464
|
+
isEmpty ? nil : self
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
#endif
|