@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.
Files changed (137) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/HawcxReactNative.podspec +2 -2
  3. package/README.md +325 -109
  4. package/android/build.gradle +2 -2
  5. package/android/src/main/java/com/hawcx/reactnative/HawcxEventDispatcher.kt +4 -0
  6. package/android/src/main/java/com/hawcx/reactnative/HawcxReactNativeModule.kt +324 -1
  7. package/android/src/main/java/com/hawcx/reactnative/v6/HawcxV6Bridge.kt +402 -0
  8. package/ios/Frameworks/HawcxFramework.xcframework/Info.plist +5 -5
  9. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/HawcxFramework +0 -0
  10. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Info.plist +0 -0
  11. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.abi.json +22145 -2
  12. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.private.swiftinterface +628 -0
  13. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  14. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.swiftinterface +628 -0
  15. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.swiftmodule +0 -0
  16. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/HawcxFramework +0 -0
  17. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Info.plist +0 -0
  18. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.abi.json +22145 -2
  19. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +628 -0
  20. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  21. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.swiftinterface +628 -0
  22. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.swiftmodule +0 -0
  23. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.abi.json +22145 -2
  24. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +628 -0
  25. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  26. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +628 -0
  27. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.swiftmodule +0 -0
  28. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/_CodeSignature/CodeResources +21 -21
  29. package/ios/HawcxReactNative.m +56 -0
  30. package/ios/HawcxReactNative.swift +380 -1
  31. package/ios/HawcxV6BridgeSupport.swift +468 -0
  32. package/lib/commonjs/index.js +326 -3
  33. package/lib/commonjs/index.js.map +1 -1
  34. package/lib/commonjs/v6Normalization.js +325 -0
  35. package/lib/commonjs/v6Normalization.js.map +1 -0
  36. package/lib/commonjs/v6State.js +186 -0
  37. package/lib/commonjs/v6State.js.map +1 -0
  38. package/lib/commonjs/v6Types.js +2 -0
  39. package/lib/commonjs/v6Types.js.map +1 -0
  40. package/lib/commonjs/v6WebLogin.js +101 -0
  41. package/lib/commonjs/v6WebLogin.js.map +1 -0
  42. package/lib/module/index.js +287 -1
  43. package/lib/module/index.js.map +1 -1
  44. package/lib/module/v6Normalization.js +318 -0
  45. package/lib/module/v6Normalization.js.map +1 -0
  46. package/lib/module/v6State.js +173 -0
  47. package/lib/module/v6State.js.map +1 -0
  48. package/lib/module/v6Types.js +2 -0
  49. package/lib/module/v6Types.js.map +1 -0
  50. package/lib/module/v6WebLogin.js +92 -0
  51. package/lib/module/v6WebLogin.js.map +1 -0
  52. package/lib/typescript/index.d.ts +83 -0
  53. package/lib/typescript/index.d.ts.map +1 -1
  54. package/lib/typescript/v6Normalization.d.ts +3 -0
  55. package/lib/typescript/v6Normalization.d.ts.map +1 -0
  56. package/lib/typescript/v6State.d.ts +13 -0
  57. package/lib/typescript/v6State.d.ts.map +1 -0
  58. package/lib/typescript/v6Types.d.ts +157 -0
  59. package/lib/typescript/v6Types.d.ts.map +1 -0
  60. package/lib/typescript/v6WebLogin.d.ts +32 -0
  61. package/lib/typescript/v6WebLogin.d.ts.map +1 -0
  62. package/package.json +21 -9
  63. package/src/index.ts +477 -0
  64. package/src/v6Normalization.ts +356 -0
  65. package/src/v6State.ts +238 -0
  66. package/src/v6Types.ts +194 -0
  67. package/src/v6WebLogin.ts +154 -0
  68. package/android/.settings/org.eclipse.buildship.core.prefs +0 -2
  69. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  70. package/android/gradle/wrapper/gradle-wrapper.properties +0 -6
  71. package/android/gradlew +0 -185
  72. package/android/gradlew.bat +0 -89
  73. package/android/libs/hawcx-5.1.4.aar +0 -0
  74. package/docs/RELEASE.md +0 -129
  75. package/example/README.md +0 -59
  76. package/example/android/app/build.gradle +0 -126
  77. package/example/android/app/debug.keystore +0 -0
  78. package/example/android/app/proguard-rules.pro +0 -10
  79. package/example/android/app/src/debug/AndroidManifest.xml +0 -9
  80. package/example/android/app/src/main/AndroidManifest.xml +0 -27
  81. package/example/android/app/src/main/java/com/hawcx/example/MainActivity.kt +0 -22
  82. package/example/android/app/src/main/java/com/hawcx/example/MainApplication.kt +0 -45
  83. package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +0 -36
  84. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  85. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  86. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  87. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  88. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  89. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  90. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  91. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  92. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  93. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  94. package/example/android/app/src/main/res/values/strings.xml +0 -3
  95. package/example/android/app/src/main/res/values/styles.xml +0 -9
  96. package/example/android/build.gradle +0 -35
  97. package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  98. package/example/android/gradle/wrapper/gradle-wrapper.properties +0 -7
  99. package/example/android/gradle.properties +0 -41
  100. package/example/android/gradlew +0 -249
  101. package/example/android/gradlew.bat +0 -92
  102. package/example/android/local.properties +0 -2
  103. package/example/android/settings.gradle +0 -38
  104. package/example/app.json +0 -4
  105. package/example/babel.config.js +0 -3
  106. package/example/e2e/README.md +0 -17
  107. package/example/e2e/hawcx-login.yaml +0 -14
  108. package/example/index.js +0 -5
  109. package/example/ios/.xcode.env +0 -11
  110. package/example/ios/HawcxExampleApp/AppDelegate.h +0 -6
  111. package/example/ios/HawcxExampleApp/AppDelegate.mm +0 -31
  112. package/example/ios/HawcxExampleApp/Images.xcassets/AppIcon.appiconset/Contents.json +0 -53
  113. package/example/ios/HawcxExampleApp/Images.xcassets/Contents.json +0 -6
  114. package/example/ios/HawcxExampleApp/Info.plist +0 -55
  115. package/example/ios/HawcxExampleApp/LaunchScreen.storyboard +0 -47
  116. package/example/ios/HawcxExampleApp/PrivacyInfo.xcprivacy +0 -37
  117. package/example/ios/HawcxExampleApp/main.m +0 -10
  118. package/example/ios/HawcxExampleApp.xcodeproj/project.pbxproj +0 -704
  119. package/example/ios/HawcxExampleApp.xcodeproj/project.xcworkspace/xcuserdata/agambhullar.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  120. package/example/ios/HawcxExampleApp.xcodeproj/xcshareddata/xcschemes/HawcxExampleApp.xcscheme +0 -90
  121. package/example/ios/HawcxExampleApp.xcodeproj/xcuserdata/agambhullar.xcuserdatad/xcschemes/xcschememanagement.plist +0 -16
  122. package/example/ios/HawcxExampleApp.xcworkspace/contents.xcworkspacedata +0 -10
  123. package/example/ios/HawcxExampleAppTests/HawcxExampleAppTests.m +0 -66
  124. package/example/ios/HawcxExampleAppTests/Info.plist +0 -24
  125. package/example/ios/Podfile +0 -79
  126. package/example/ios/Podfile.lock +0 -1290
  127. package/example/metro.config.js +0 -16
  128. package/example/package-lock.json +0 -13220
  129. package/example/package.json +0 -30
  130. package/example/src/App.tsx +0 -755
  131. package/example/src/hawcx.config.ts +0 -25
  132. package/example/tsconfig.json +0 -8
  133. package/ios/Frameworks/.keep +0 -0
  134. package/lib/typescript/__tests__/index.test.d.ts +0 -2
  135. package/lib/typescript/__tests__/index.test.d.ts.map +0 -1
  136. package/react_mobile_sdk_plan.md +0 -242
  137. package/src/__tests__/index.test.ts +0 -206
package/src/index.ts CHANGED
@@ -6,6 +6,55 @@ import {
6
6
  NativeModule,
7
7
  } from 'react-native';
8
8
  import { useCallback, useEffect, useRef, useState } from 'react';
9
+ import { normalizeV6FlowEvent } from './v6Normalization';
10
+ import {
11
+ canHawcxV6Resend,
12
+ createIdentifierHawcxV6AuthState,
13
+ createInitialHawcxV6AuthState,
14
+ getHawcxV6ResendAvailability,
15
+ normalizeHawcxV6FlowType,
16
+ refreshHawcxV6AuthState,
17
+ reduceHawcxV6FlowEvent,
18
+ } from './v6State';
19
+ import type {
20
+ HawcxV6AuthState,
21
+ HawcxV6FlowEvent,
22
+ HawcxV6FlowType,
23
+ HawcxV6StartOptions,
24
+ } from './v6Types';
25
+ import { routeWebLoginScan, type HawcxV6QrApprovalResult } from './v6WebLogin';
26
+
27
+ export type {
28
+ HawcxV6AwaitApprovalPrompt,
29
+ HawcxV6AuthState,
30
+ HawcxV6AuthStatus,
31
+ HawcxV6CompletedPayload,
32
+ HawcxV6EnterCodePrompt,
33
+ HawcxV6EnterTotpPrompt,
34
+ HawcxV6ErrorAction,
35
+ HawcxV6ErrorDetails,
36
+ HawcxV6ErrorPayload,
37
+ HawcxV6FieldError,
38
+ HawcxV6FlowEvent,
39
+ HawcxV6FlowType,
40
+ HawcxV6Method,
41
+ HawcxV6PromptPayload,
42
+ HawcxV6RedirectPrompt,
43
+ HawcxV6ResendAvailability,
44
+ HawcxV6RiskInfo,
45
+ HawcxV6RiskLocation,
46
+ HawcxV6SelectMethodPrompt,
47
+ HawcxV6SetupSmsPrompt,
48
+ HawcxV6SetupTotpPrompt,
49
+ HawcxV6StartOptions,
50
+ HawcxV6StepInfo,
51
+ } from './v6Types';
52
+ export type {
53
+ HawcxV6QrApprovalResult,
54
+ HawcxV6QrPayload,
55
+ HawcxV6QrPayloadType,
56
+ HawcxWebLoginScanRoute,
57
+ } from './v6WebLogin';
9
58
 
10
59
  const LINKING_ERROR = [
11
60
  "The package '@hawcx/react-native-sdk' doesn't seem to be linked.",
@@ -16,6 +65,7 @@ const LINKING_ERROR = [
16
65
  const AUTH_EVENT = 'hawcx.auth.event';
17
66
  const SESSION_EVENT = 'hawcx.session.event';
18
67
  const PUSH_EVENT = 'hawcx.push.event';
68
+ const V6_FLOW_EVENT = 'hawcx.v6.flow.event';
19
69
 
20
70
  export type HawcxInitializeConfig = {
21
71
  projectApiKey: string;
@@ -29,6 +79,8 @@ export type HawcxInitializeConfig = {
29
79
  publicKeyPem: string;
30
80
  tokenEndpoint: string;
31
81
  };
82
+ relyingParty?: string;
83
+ autoPollApprovals?: boolean;
32
84
  /**
33
85
  * @deprecated Use root-level `baseUrl` instead. Kept for backward compatibility during migration.
34
86
  */
@@ -86,15 +138,56 @@ export type PushEvent =
86
138
  | { type: 'push_login_request'; payload: PushLoginPayload }
87
139
  | { type: 'push_error'; payload: AuthErrorPayload };
88
140
 
141
+ export type HawcxV6QrApprovalOptions = {
142
+ rememberDevice?: boolean;
143
+ };
144
+
145
+ export type HawcxV6AuthHookResult = {
146
+ state: HawcxV6AuthState;
147
+ start: (options: HawcxV6StartOptions) => Promise<void>;
148
+ selectMethod: (methodId: string) => Promise<void>;
149
+ submitCode: (code: string) => Promise<void>;
150
+ submitTotp: (code: string) => Promise<void>;
151
+ submitPhone: (phone: string) => Promise<void>;
152
+ resend: () => Promise<boolean>;
153
+ poll: () => Promise<void>;
154
+ cancel: () => Promise<void>;
155
+ changeIdentifier: () => Promise<void>;
156
+ reset: () => Promise<void>;
157
+ handleRedirectUrl: (url: string) => Promise<void>;
158
+ canResend: boolean;
159
+ resendAvailability: HawcxV6AuthState['resend'];
160
+ secondsUntilResend?: number;
161
+ };
162
+
89
163
  type NativeBridge = {
90
164
  initialize(config: HawcxInitializeConfig): Promise<void>;
91
165
  authenticate(userId: string): Promise<void>;
92
166
  submitOtp(otp: string): Promise<void>;
167
+ v6Start(options: HawcxV6StartOptions): Promise<void>;
168
+ v6SelectMethod(methodId: string): Promise<void>;
169
+ v6SubmitCode(code: string): Promise<void>;
170
+ v6SubmitTotp(code: string): Promise<void>;
171
+ v6SubmitPhone(phone: string): Promise<void>;
172
+ v6Resend(): Promise<boolean>;
173
+ v6Poll(): Promise<void>;
174
+ v6Cancel(): Promise<void>;
175
+ v6Reset(): Promise<void>;
176
+ v6ApproveQr(
177
+ rawPayload: string,
178
+ identifier: string,
179
+ rememberDevice: boolean,
180
+ ): Promise<Record<string, unknown>>;
181
+ v6HandleRedirectUrl(url: string): Promise<void>;
93
182
  storeBackendOAuthTokens(
94
183
  userId: string,
95
184
  accessToken: string,
96
185
  refreshToken?: string | null,
97
186
  ): Promise<boolean>;
187
+ getLastLoggedInUser(): Promise<string>;
188
+ clearSessionTokens(userId: string): Promise<void>;
189
+ clearUserKeychainData(userId: string): Promise<void>;
190
+ clearLastLoggedInUser(): Promise<void>;
98
191
  getDeviceDetails(): Promise<void>;
99
192
  webLogin(pin: string): Promise<void>;
100
193
  webApprove(token: string): Promise<void>;
@@ -119,6 +212,7 @@ const HawcxReactNative = HawcxReactNativeModule;
119
212
  const authEventEmitter = new NativeEventEmitter(HawcxReactNativeModule);
120
213
  const sessionEventEmitter = new NativeEventEmitter(HawcxReactNativeModule);
121
214
  const pushEventEmitter = new NativeEventEmitter(HawcxReactNativeModule);
215
+ const v6FlowEventEmitter = new NativeEventEmitter(HawcxReactNativeModule);
122
216
 
123
217
  const isIOS = () => Platform.OS === 'ios';
124
218
  const isAndroid = () => Platform.OS === 'android';
@@ -166,6 +260,119 @@ export function submitOtp(otp: string): Promise<void> {
166
260
  }
167
261
  }
168
262
 
263
+ export function startV6Flow(options: HawcxV6StartOptions): Promise<void> {
264
+ try {
265
+ const identifier = ensureNonEmpty(options.identifier, 'identifier');
266
+ return HawcxReactNative.v6Start({
267
+ ...options,
268
+ identifier,
269
+ flowType: normalizeHawcxV6FlowType(options.flowType),
270
+ startToken: options.startToken?.trim() || undefined,
271
+ inviteCode: options.inviteCode?.trim() || undefined,
272
+ codeChallenge: options.codeChallenge?.trim() || undefined,
273
+ });
274
+ } catch (error) {
275
+ return Promise.reject(error);
276
+ }
277
+ }
278
+
279
+ export function v6SelectMethod(methodId: string): Promise<void> {
280
+ try {
281
+ return HawcxReactNative.v6SelectMethod(ensureNonEmpty(methodId, 'methodId'));
282
+ } catch (error) {
283
+ return Promise.reject(error);
284
+ }
285
+ }
286
+
287
+ export function v6SubmitCode(code: string): Promise<void> {
288
+ try {
289
+ return HawcxReactNative.v6SubmitCode(ensureNonEmpty(code, 'code'));
290
+ } catch (error) {
291
+ return Promise.reject(error);
292
+ }
293
+ }
294
+
295
+ export function v6SubmitTotp(code: string): Promise<void> {
296
+ try {
297
+ return HawcxReactNative.v6SubmitTotp(ensureNonEmpty(code, 'code'));
298
+ } catch (error) {
299
+ return Promise.reject(error);
300
+ }
301
+ }
302
+
303
+ export function v6SubmitPhone(phone: string): Promise<void> {
304
+ try {
305
+ return HawcxReactNative.v6SubmitPhone(ensureNonEmpty(phone, 'phone'));
306
+ } catch (error) {
307
+ return Promise.reject(error);
308
+ }
309
+ }
310
+
311
+ export function v6Resend(): Promise<boolean> {
312
+ return HawcxReactNative.v6Resend();
313
+ }
314
+
315
+ export function v6Poll(): Promise<void> {
316
+ return HawcxReactNative.v6Poll();
317
+ }
318
+
319
+ export function v6Cancel(): Promise<void> {
320
+ return HawcxReactNative.v6Cancel();
321
+ }
322
+
323
+ export function v6Reset(): Promise<void> {
324
+ return HawcxReactNative.v6Reset();
325
+ }
326
+
327
+ const normalizeV6QrApprovalResult = (value: unknown): HawcxV6QrApprovalResult => {
328
+ const record =
329
+ typeof value === 'object' && value !== null && !Array.isArray(value)
330
+ ? (value as Record<string, unknown>)
331
+ : null;
332
+ const outcome = record?.outcome;
333
+ const payloadType = record?.payloadType;
334
+
335
+ if (outcome === 'approved' && (payloadType === 'qr_auth' || payloadType === 'qr_login')) {
336
+ return {
337
+ outcome,
338
+ payloadType,
339
+ };
340
+ }
341
+
342
+ if (outcome === 'bound' && (payloadType === 'qr_auth' || payloadType === 'qr_login')) {
343
+ return {
344
+ outcome,
345
+ payloadType,
346
+ userId: typeof record?.userId === 'string' ? record.userId : undefined,
347
+ };
348
+ }
349
+
350
+ throw new Error('Invalid V6 QR approval response from native bridge');
351
+ };
352
+
353
+ export async function approveV6Qr(
354
+ rawPayload: string,
355
+ identifier: string,
356
+ options: HawcxV6QrApprovalOptions = {},
357
+ ): Promise<HawcxV6QrApprovalResult> {
358
+ const trimmedPayload = ensureNonEmpty(rawPayload, 'rawPayload');
359
+ const trimmedIdentifier = ensureNonEmpty(identifier, 'identifier');
360
+ const result = await HawcxReactNative.v6ApproveQr(
361
+ trimmedPayload,
362
+ trimmedIdentifier,
363
+ options.rememberDevice ?? false,
364
+ );
365
+ return normalizeV6QrApprovalResult(result);
366
+ }
367
+
368
+ export function v6HandleRedirectUrl(url: string): Promise<void> {
369
+ try {
370
+ return HawcxReactNative.v6HandleRedirectUrl(ensureNonEmpty(url, 'url'));
371
+ } catch (error) {
372
+ return Promise.reject(error);
373
+ }
374
+ }
375
+
169
376
  export function storeBackendOAuthTokens(
170
377
  userId: string,
171
378
  accessToken: string,
@@ -272,10 +479,51 @@ export function addAuthListener(handler: (event: AuthEvent) => void): EmitterSub
272
479
  return authEventEmitter.addListener(AUTH_EVENT, handler);
273
480
  }
274
481
 
482
+ export function addV6FlowListener(handler: (event: HawcxV6FlowEvent) => void): EmitterSubscription {
483
+ return v6FlowEventEmitter.addListener(V6_FLOW_EVENT, (rawEvent: unknown) => {
484
+ const normalized = normalizeV6FlowEvent(rawEvent);
485
+ if (normalized) {
486
+ handler(normalized);
487
+ }
488
+ });
489
+ }
490
+
491
+ export const addHawcxV6FlowListener = addV6FlowListener;
492
+ export const startHawcxV6Flow = startV6Flow;
493
+ export const handleHawcxV6RedirectUrl = v6HandleRedirectUrl;
494
+ export { canHawcxV6Resend, getHawcxV6ResendAvailability };
495
+ export { routeWebLoginScan };
496
+
497
+ export async function getLastLoggedInUser(): Promise<string> {
498
+ const userId = await HawcxReactNative.getLastLoggedInUser();
499
+ return typeof userId === 'string' ? userId : '';
500
+ }
501
+
502
+ export function logoutSession(userId: string): Promise<void> {
503
+ try {
504
+ return HawcxReactNative.clearSessionTokens(ensureNonEmpty(userId, 'userId'));
505
+ } catch (error) {
506
+ return Promise.reject(error);
507
+ }
508
+ }
509
+
510
+ export function forgetTrustedDevice(userId: string): Promise<void> {
511
+ try {
512
+ return HawcxReactNative.clearUserKeychainData(ensureNonEmpty(userId, 'userId'));
513
+ } catch (error) {
514
+ return Promise.reject(error);
515
+ }
516
+ }
517
+
518
+ export function clearLastLoggedInUser(): Promise<void> {
519
+ return HawcxReactNative.clearLastLoggedInUser();
520
+ }
521
+
275
522
  export function removeAllListeners(): void {
276
523
  authEventEmitter.removeAllListeners(AUTH_EVENT);
277
524
  sessionEventEmitter.removeAllListeners(SESSION_EVENT);
278
525
  pushEventEmitter.removeAllListeners(PUSH_EVENT);
526
+ v6FlowEventEmitter.removeAllListeners(V6_FLOW_EVENT);
279
527
  }
280
528
 
281
529
  export function addSessionListener(handler: (event: SessionEvent) => void): EmitterSubscription {
@@ -457,10 +705,120 @@ export class HawcxClient {
457
705
  storeBackendOAuthTokens(userId: string, tokens: BackendOAuthTokens): Promise<void> {
458
706
  return storeBackendOAuthTokens(userId, tokens.accessToken, tokens.refreshToken);
459
707
  }
708
+
709
+ getLastLoggedInUser(): Promise<string> {
710
+ return getLastLoggedInUser();
711
+ }
712
+
713
+ logoutSession(userId: string): Promise<void> {
714
+ return logoutSession(userId);
715
+ }
716
+
717
+ forgetTrustedDevice(userId: string): Promise<void> {
718
+ return forgetTrustedDevice(userId);
719
+ }
720
+
721
+ clearLastLoggedInUser(): Promise<void> {
722
+ return clearLastLoggedInUser();
723
+ }
460
724
  }
461
725
 
462
726
  export const hawcxClient = new HawcxClient();
463
727
 
728
+ export type HawcxV6HookOptions = {
729
+ flowType?: HawcxV6FlowType;
730
+ now?: () => number;
731
+ resendTickMs?: number;
732
+ };
733
+
734
+ export class HawcxV6Client {
735
+ initialize(config: HawcxInitializeConfig): Promise<void> {
736
+ return initialize(config);
737
+ }
738
+
739
+ start(options: HawcxV6StartOptions): Promise<void> {
740
+ return startV6Flow(options);
741
+ }
742
+
743
+ selectMethod(methodId: string): Promise<void> {
744
+ return v6SelectMethod(methodId);
745
+ }
746
+
747
+ submitCode(code: string): Promise<void> {
748
+ return v6SubmitCode(code);
749
+ }
750
+
751
+ submitTotp(code: string): Promise<void> {
752
+ return v6SubmitTotp(code);
753
+ }
754
+
755
+ submitPhone(phone: string): Promise<void> {
756
+ return v6SubmitPhone(phone);
757
+ }
758
+
759
+ resend(): Promise<boolean> {
760
+ return v6Resend();
761
+ }
762
+
763
+ poll(): Promise<void> {
764
+ return v6Poll();
765
+ }
766
+
767
+ cancel(): Promise<void> {
768
+ return v6Cancel();
769
+ }
770
+
771
+ reset(): Promise<void> {
772
+ return v6Reset();
773
+ }
774
+
775
+ approveQr(
776
+ rawPayload: string,
777
+ identifier: string,
778
+ options?: HawcxV6QrApprovalOptions,
779
+ ): Promise<HawcxV6QrApprovalResult> {
780
+ return approveV6Qr(rawPayload, identifier, options);
781
+ }
782
+
783
+ changeIdentifier(): Promise<void> {
784
+ return v6Reset();
785
+ }
786
+
787
+ handleRedirectUrl(url: string): Promise<void> {
788
+ return v6HandleRedirectUrl(url);
789
+ }
790
+
791
+ addFlowListener(handler: (event: HawcxV6FlowEvent) => void): EmitterSubscription {
792
+ return addV6FlowListener(handler);
793
+ }
794
+
795
+ storeBackendOAuthTokens(userId: string, tokens: BackendOAuthTokens): Promise<void> {
796
+ return storeBackendOAuthTokens(userId, tokens.accessToken, tokens.refreshToken);
797
+ }
798
+
799
+ notifyUserAuthenticated(): Promise<void> {
800
+ return notifyUserAuthenticated();
801
+ }
802
+
803
+ getLastLoggedInUser(): Promise<string> {
804
+ return getLastLoggedInUser();
805
+ }
806
+
807
+ logoutSession(userId: string): Promise<void> {
808
+ return logoutSession(userId);
809
+ }
810
+
811
+ forgetTrustedDevice(userId: string): Promise<void> {
812
+ return forgetTrustedDevice(userId);
813
+ }
814
+
815
+ clearLastLoggedInUser(): Promise<void> {
816
+ return clearLastLoggedInUser();
817
+ }
818
+ }
819
+
820
+ export const hawcxV6Client = new HawcxV6Client();
821
+
464
822
  export type HawcxAuthHookState =
465
823
  | { status: 'idle' }
466
824
  | { status: 'pending' }
@@ -476,6 +834,123 @@ export type HawcxWebSessionState =
476
834
  | { status: 'success' }
477
835
  | { status: 'error'; error: AuthErrorPayload };
478
836
 
837
+ export function useHawcxV6Auth(
838
+ client: HawcxV6Client = hawcxV6Client,
839
+ options: HawcxV6HookOptions = {},
840
+ ): HawcxV6AuthHookResult {
841
+ const now = options.now ?? Date.now;
842
+ const resendTickMs = Math.max(250, options.resendTickMs ?? 1000);
843
+ const [state, setState] = useState<HawcxV6AuthState>(() =>
844
+ createInitialHawcxV6AuthState(options.flowType),
845
+ );
846
+
847
+ useEffect(() => {
848
+ const subscription = client.addFlowListener((event) => {
849
+ setState((previous) => reduceHawcxV6FlowEvent(event, previous, now()));
850
+ });
851
+
852
+ return () => {
853
+ subscription.remove();
854
+ };
855
+ }, [client, now]);
856
+
857
+ useEffect(() => {
858
+ if (!state.resend.resendAt || state.resend.canResend) {
859
+ return;
860
+ }
861
+
862
+ const intervalId = setInterval(() => {
863
+ setState((previous) => refreshHawcxV6AuthState(previous, now()));
864
+ }, resendTickMs);
865
+
866
+ return () => {
867
+ clearInterval(intervalId);
868
+ };
869
+ }, [now, resendTickMs, state.resend.canResend, state.resend.resendAt]);
870
+
871
+ const start = useCallback(
872
+ (startOptions: HawcxV6StartOptions) => {
873
+ const identifier = ensureNonEmpty(startOptions.identifier, 'identifier');
874
+ const flowType = normalizeHawcxV6FlowType(startOptions.flowType ?? options.flowType);
875
+
876
+ setState((previous) =>
877
+ createIdentifierHawcxV6AuthState({
878
+ previous,
879
+ identifier,
880
+ flowType,
881
+ }),
882
+ );
883
+
884
+ return client.start({
885
+ ...startOptions,
886
+ identifier,
887
+ flowType,
888
+ });
889
+ },
890
+ [client, options.flowType],
891
+ );
892
+
893
+ const selectMethod = useCallback((methodId: string) => client.selectMethod(methodId), [client]);
894
+
895
+ const submitCode = useCallback((code: string) => client.submitCode(code), [client]);
896
+
897
+ const submitTotp = useCallback((code: string) => client.submitTotp(code), [client]);
898
+
899
+ const submitPhone = useCallback((phone: string) => client.submitPhone(phone), [client]);
900
+
901
+ const resend = useCallback(() => client.resend(), [client]);
902
+ const poll = useCallback(() => client.poll(), [client]);
903
+
904
+ const cancel = useCallback(async () => {
905
+ await client.cancel();
906
+ setState((previous) =>
907
+ createIdentifierHawcxV6AuthState({
908
+ previous,
909
+ identifier: previous.identifier,
910
+ }),
911
+ );
912
+ }, [client]);
913
+
914
+ const reset = useCallback(async () => {
915
+ await client.reset();
916
+ setState((previous) =>
917
+ createIdentifierHawcxV6AuthState({
918
+ previous,
919
+ identifier: previous.identifier,
920
+ }),
921
+ );
922
+ }, [client]);
923
+
924
+ const changeIdentifier = useCallback(async () => {
925
+ await client.changeIdentifier();
926
+ setState((previous) =>
927
+ createIdentifierHawcxV6AuthState({
928
+ previous,
929
+ }),
930
+ );
931
+ }, [client]);
932
+
933
+ const handleRedirectUrl = useCallback((url: string) => client.handleRedirectUrl(url), [client]);
934
+
935
+ return {
936
+ state,
937
+ start,
938
+ selectMethod,
939
+ submitCode,
940
+ submitTotp,
941
+ submitPhone,
942
+ resend,
943
+ poll,
944
+ cancel,
945
+ changeIdentifier,
946
+ reset,
947
+ handleRedirectUrl,
948
+ canResend: state.resend.canResend,
949
+ resendAvailability: state.resend,
950
+ secondsUntilResend: state.resend.secondsUntilResend,
951
+ };
952
+ }
953
+
479
954
  export function useHawcxAuth(client: HawcxClient = hawcxClient) {
480
955
  const [state, setState] = useState<HawcxAuthHookState>({ status: 'idle' });
481
956
  const otpAttempts = useRef(0);
@@ -547,9 +1022,11 @@ export const __INTERNAL_EVENTS__ = {
547
1022
  authEmitter: authEventEmitter,
548
1023
  sessionEmitter: sessionEventEmitter,
549
1024
  pushEmitter: pushEventEmitter,
1025
+ v6FlowEmitter: v6FlowEventEmitter,
550
1026
  authEventName: AUTH_EVENT,
551
1027
  sessionEventName: SESSION_EVENT,
552
1028
  pushEventName: PUSH_EVENT,
1029
+ v6FlowEventName: V6_FLOW_EVENT,
553
1030
  };
554
1031
 
555
1032
  export function useHawcxWebLogin(client: HawcxClient = hawcxClient) {