@hawcx/react-native-sdk 1.0.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 (100) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/HawcxReactNative.podspec +25 -0
  3. package/LICENSE +21 -0
  4. package/README.md +109 -0
  5. package/docs/RELEASE.md +119 -0
  6. package/example/README.md +59 -0
  7. package/example/android/app/build.gradle +126 -0
  8. package/example/android/app/debug.keystore +0 -0
  9. package/example/android/app/proguard-rules.pro +10 -0
  10. package/example/android/app/src/debug/AndroidManifest.xml +9 -0
  11. package/example/android/app/src/main/AndroidManifest.xml +27 -0
  12. package/example/android/app/src/main/java/com/hawcx/example/MainActivity.kt +22 -0
  13. package/example/android/app/src/main/java/com/hawcx/example/MainApplication.kt +45 -0
  14. package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +36 -0
  15. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  16. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  17. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  18. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  19. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  20. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  21. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  22. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  23. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  24. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  25. package/example/android/app/src/main/res/values/strings.xml +3 -0
  26. package/example/android/app/src/main/res/values/styles.xml +9 -0
  27. package/example/android/build.gradle +23 -0
  28. package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  29. package/example/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  30. package/example/android/gradle.properties +41 -0
  31. package/example/android/gradlew +249 -0
  32. package/example/android/gradlew.bat +92 -0
  33. package/example/android/local.properties +2 -0
  34. package/example/android/settings.gradle +4 -0
  35. package/example/app.json +4 -0
  36. package/example/babel.config.js +3 -0
  37. package/example/e2e/README.md +17 -0
  38. package/example/e2e/hawcx-login.yaml +14 -0
  39. package/example/index.js +5 -0
  40. package/example/ios/.xcode.env +11 -0
  41. package/example/ios/HawcxExampleApp/AppDelegate.h +6 -0
  42. package/example/ios/HawcxExampleApp/AppDelegate.mm +31 -0
  43. package/example/ios/HawcxExampleApp/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
  44. package/example/ios/HawcxExampleApp/Images.xcassets/Contents.json +6 -0
  45. package/example/ios/HawcxExampleApp/Info.plist +55 -0
  46. package/example/ios/HawcxExampleApp/LaunchScreen.storyboard +47 -0
  47. package/example/ios/HawcxExampleApp/PrivacyInfo.xcprivacy +37 -0
  48. package/example/ios/HawcxExampleApp/main.m +10 -0
  49. package/example/ios/HawcxExampleApp.xcodeproj/project.pbxproj +704 -0
  50. package/example/ios/HawcxExampleApp.xcodeproj/project.xcworkspace/xcuserdata/agambhullar.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  51. package/example/ios/HawcxExampleApp.xcodeproj/xcshareddata/xcschemes/HawcxExampleApp.xcscheme +90 -0
  52. package/example/ios/HawcxExampleApp.xcodeproj/xcuserdata/agambhullar.xcuserdatad/xcschemes/xcschememanagement.plist +16 -0
  53. package/example/ios/HawcxExampleApp.xcworkspace/contents.xcworkspacedata +10 -0
  54. package/example/ios/HawcxExampleAppTests/HawcxExampleAppTests.m +66 -0
  55. package/example/ios/HawcxExampleAppTests/Info.plist +24 -0
  56. package/example/ios/Podfile +55 -0
  57. package/example/ios/Podfile.lock +1290 -0
  58. package/example/metro.config.js +16 -0
  59. package/example/package-lock.json +13220 -0
  60. package/example/package.json +30 -0
  61. package/example/src/App.tsx +552 -0
  62. package/example/src/hawcx.config.ts +41 -0
  63. package/example/tsconfig.json +8 -0
  64. package/ios/Frameworks/.keep +0 -0
  65. package/ios/Frameworks/HawcxFramework.xcframework/Info.plist +44 -0
  66. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/HawcxFramework +0 -0
  67. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Headers/HawcxFramework.h +16 -0
  68. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Info.plist +0 -0
  69. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.abi.json +9794 -0
  70. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.private.swiftinterface +302 -0
  71. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  72. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.swiftinterface +302 -0
  73. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/module.modulemap +6 -0
  74. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/HawcxFramework +0 -0
  75. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Headers/HawcxFramework.h +16 -0
  76. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Info.plist +0 -0
  77. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.abi.json +9794 -0
  78. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +302 -0
  79. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  80. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.swiftinterface +302 -0
  81. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.abi.json +9794 -0
  82. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +302 -0
  83. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  84. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +302 -0
  85. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/module.modulemap +6 -0
  86. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/_CodeSignature/CodeResources +234 -0
  87. package/ios/HawcxReactNative.m +51 -0
  88. package/ios/HawcxReactNative.swift +311 -0
  89. package/lib/commonjs/index.js +404 -0
  90. package/lib/commonjs/index.js.map +1 -0
  91. package/lib/module/index.js +375 -0
  92. package/lib/module/index.js.map +1 -0
  93. package/lib/typescript/__tests__/index.test.d.ts +2 -0
  94. package/lib/typescript/__tests__/index.test.d.ts.map +1 -0
  95. package/lib/typescript/index.d.ts +151 -0
  96. package/lib/typescript/index.d.ts.map +1 -0
  97. package/package.json +72 -0
  98. package/react_mobile_sdk_plan.md +240 -0
  99. package/src/__tests__/index.test.ts +163 -0
  100. package/src/index.ts +518 -0
@@ -0,0 +1,240 @@
1
+ # Hawcx React Mobile SDK Delivery Plan
2
+
3
+ ## How to Use This Document
4
+ - Read this file end-to-end before touching code. It establishes context from `hawcx_core_auth` and `dev_ios/ios_sdk` that must be mirrored exactly.
5
+ - Every time you finish a task (or discover new work), update the **Progress Tracker** section with your initials, date, and notes so the next engineer knows where to resume.
6
+ - Keep this document as the single source of truth. If you add architectural decisions, reference code paths and link PRs.
7
+
8
+ ## System Context
9
+ ### Backend (hawcx_core_auth)
10
+ - V5 API endpoints live in `app/api/v5/endpoints.py` backed by `DeviceVerificationSystem`, Redis, and `DomainService` (`app/shared/domain_service.py`).
11
+ - Device lifecycle:
12
+ 1. `POST /hc_auth/v5/auth-init` → determines login vs registration via `hindex`.
13
+ 2. `POST /hc_auth/v5/verify-otp` → completes registration with `keyset` + `H(I)`.
14
+ 3. `POST /hc_auth/v5/verify-device` → persists salts, combined key blobs, and device fingerprint.
15
+ 4. `POST /hc_auth/v5/verify-cipher` → login challenge signed with Ed25519 private key stored on device.
16
+ - Device info schema (`app/api/v5/v5_dependencies/models/fingerprint.py`) expects the client to send fingerprints already computed on-device. Keep React SDK aligned.
17
+ - QR/web session, push, and admin utilities share the same project/domain enforcement.
18
+
19
+ ### Native iOS SDK Baseline (dev_ios/ios_sdk)
20
+ - `HawcxFramework` encapsulates all V5 crypto (see `internal/V5Crypto.swift`, `internal/V5CredentialStore.swift`, `auth/HawcxAuthV5.swift`). Secure Enclave + HKDF salt handling is already production-ready.
21
+ - `HawcxSDK` (in `internal/HawcxSDK.swift`) exposes `authenticateV5`, `submitOtpV5`, session helpers, push registration, and web login utilities.
22
+ - Demo app (`dev_ios/ios_sdk/../ios_demo_dev/HawcxDemoApp`) shows end-to-end UX and state transitions.
23
+ - The React Native SDK MUST wrap this existing Swift implementation—do **not** rewrite crypto or network flows in JavaScript.
24
+
25
+ ### Native Android SDK Baseline (dev_android/android_sdk)
26
+ - Kotlin sources under `app/src/main/Kotlin/com/hawcx` mirror the Swift modules and already implement V5.1. `internal/HawcxSDK.kt` wires `HawcxAuthV5`, `DevSession`, `WebLoginManager`, `KeystoreManager`, and `HawcxPushAuthDelegate` so the facade matches iOS.
27
+ - `auth/HawcxAuthV5.kt`, `v5/V5CredentialStore.kt`, `v5/V5Crypto.kt`, `storage/KeystoreManager.kt`, and `utils/FingerprintGenerator.kt` cover HKDF salts, Ed25519 keypairs, AES-GCM secrets, and Android Keystore usage.
28
+ - OAuth + network helpers live in `internal/HawcxOAuthConfig.kt`, `internal/OAuthTokenVerifier.kt`, and `network/ApiService.kt`, pointing at the same `/hc_auth/v5/oauth/token` + `EndPoints` constants as iOS.
29
+ - Device/session utilities (`auth/WebLoginManager.kt`, `auth/DevSession.kt`, `utils/DeviceDetailsUtil.kt`) emit the same JSON schema that `hawcx_core_auth` expects, and push logic (`utils/HawcxPushAuthDelegate.kt`) drives `setFcmToken`, `handlePushNotification`, and approve/decline helpers.
30
+ - `app/build.gradle` already packages the SDK as an AAR for the Compose demo. The React Native module should consume that binary (or a git submodule) instead of re-implementing anything in JS.
31
+
32
+ ### Android Demo App Baseline (dev_android/HawcxAndroidDemoApp/HawcxDemoApp)
33
+ - Jetpack Compose UI demonstrates every V5 flow. `MainApplication.kt` instantiates `HawcxSDK`, configures OAuth, wires Firebase, and registers the push delegate.
34
+ - View models under `app/src/main/java/com/hawcx/android/demoapp/viewmodel/` (`AuthenticateViewModel.kt`, `WebLoginViewModel.kt`, `SharedAuthManager.kt`, etc.) show how callbacks drive UX states identical to SwiftUI.
35
+ - `messaging/DemoFirebaseMessagingService.kt` feeds FCM payloads into `hawcxSDK.handlePushNotification()` and raises approve/decline actions through `AppViewModel`.
36
+ - `ui/` and `util/Constants.kt` document theme switching, device details cards, and the required configuration (API key, OAuth endpoints, `google-services.json`).
37
+ - Treat this app as the Android source of truth when reproducing flows inside the React Native example and when validating push/web-session parity.
38
+
39
+ ## Goals & Non-Goals
40
+ 1. Keep the shipping iOS React Native bridge stable while extending the same Hawcx V5 feature set to Android by delegating directly to `dev_ios/ios_sdk/HawcxFramework` and `dev_android/android_sdk/app/src/main/Kotlin/com/hawcx/internal/HawcxSDK.kt`.
41
+ 2. Provide one TypeScript API (hooks, events, session + push helpers) that transparently drives both platforms and emits identical payloads/error codes for auth, OTP, device management, push approvals, and web login.
42
+ 3. Deliver an RN example application plus QA harness that runs end-to-end on simulators/emulators so customers can validate both platforms with minimal setup.
43
+ 4. Non-goals: rewriting crypto/network logic in JavaScript, deviating from backend lifecycles, or adding Android-only V4 bindings unless product explicitly asks for legacy support.
44
+
45
+ ## Guiding Principles
46
+ - **Parity with Native SDKs**: All flows must call into `HawcxAuthV5`, `V5CredentialStore`, `ApiService`, `DevSession`, etc., on both Swift and Kotlin. Use bridging—no duplicated crypto logic.
47
+ - **Cross-Platform Contract**: Event names (`hawcx.auth.event`, `hawcx.session.event`, `hawcx.push.event`) and payload shapes stay identical so TS hooks remain platform-agnostic. Any change must be reflected on iOS + Android simultaneously.
48
+ - **Single Source Config**: Accept `projectApiKey`, optional OAuth config, and environment overrides identical to `ApiService` usage.
49
+ - **Safety First**: Never expose private key material or salts to JS. Keep secrets on native side; JS receives only tokens/errors.
50
+ - **Traceability**: Reference source files (e.g., `dev_ios/ios_sdk/HawcxFramework/auth/HawcxAuthV5.swift`, `dev_android/android_sdk/app/src/main/Kotlin/com/hawcx/auth/HawcxAuthV5.kt`) whenever you mirror behavior.
51
+ - **Progress Discipline**: Update the tracker after each deliverable (even partial) so management can slot another engineer instantly.
52
+
53
+ ## High-Level Architecture
54
+ 1. **React Native Modules (iOS + Android)**
55
+ - iOS: `ios/HawcxReactNative.swift` (RCTEventEmitter subclass) maintains a singleton `HawcxSDK`, configures OAuth, and exposes bridging methods.
56
+ - Android: `android/src/main/java/com/hawcx/reactnative/HawcxReactNativeModule.kt` + `HawcxReactNativePackage.kt` will mirror the Swift API, hold a singleton `HawcxSDK`, and parse config maps.
57
+ 2. **Event Emitters**
58
+ - iOS already emits `hawcx.auth.event`, `hawcx.session.event`, `hawcx.push.event`.
59
+ - Android must use `DeviceEventManagerModule.RCTDeviceEventEmitter` to fire the exact same event names/payload schemas from `AuthV5Callback`, `DevSession.DevSessionCallback`, and `HawcxPushAuthDelegate`.
60
+ 3. **TypeScript API Layer** (`src/index.ts`)
61
+ - Provides typed methods (`authenticate`, `submitOtp`, `webLogin`, `webApprove`, `setApnsDeviceToken`/`setFcmToken`, `handlePushNotification`, etc.).
62
+ - Wraps native calls in promises + hooks so consuming apps stay identical regardless of platform, but enforces platform guards where necessary (e.g., APNs vs FCM tokens).
63
+ 4. **Distribution**
64
+ - npm package `@hawcx/react-native-sdk` ships `ios/` + `android/`. iOS uses `HawcxReactNative.podspec` to pull `HawcxFramework.xcframework`; Android ships Gradle config referencing the Hawcx Android AAR (`dev_android/android_sdk`) or Maven artifact and applies `maven-publish` for validation.
65
+ 5. **Example App**
66
+ - `example/` demonstrates the JS API on both platforms, mirroring the native demos for OTP, push approvals, biometrics, and web login. Maintains documentation for dev credentials (`hawcx.config.ts`, `google-services.json`).
67
+
68
+ ## Detailed Implementation Plan
69
+ _Phases 0–6 capture the completed iOS scope and remain here for historical context. Keep them green while layering the Android workstreams described later in this section._
70
+ ### Phase 0 – Repo Bootstrap
71
+ - Initialize monorepo structure inside `dev_react` (`package.json`, `tsconfig`, `babel.config`, RN scaffolding, Podspec referencing `HawcxFramework`).
72
+ - Add linting (`eslint`, `prettier`) and SwiftLint; configure GitHub Actions skeleton.
73
+ - Document environment setup (Xcode, CocoaPods, Node version) in this file.
74
+
75
+ ### Phase 1 – Native Bridge Skeleton
76
+ - Create `HawcxReactNative` module exposing `initialize`, `authenticateV5`, `submitOtpV5`, `clearSession`, `getLastLoggedInUser` by delegating to `HawcxSDK`.
77
+ - Implement `AuthV5Callback` bridging: translate `onOtpRequired`, `onAuthSuccess`, `onError` to RN events.
78
+ - Ensure `isLoginFlow`, `accessToken`, `refreshToken`, and `AuthV5ErrorCode` are included in event payloads.
79
+
80
+ ### Phase 2 – TypeScript Surface & State Helpers
81
+ - Implement `src/HawcxClient.ts` with:
82
+ - `initialize(config: HawcxConfig)`
83
+ - `authenticate(userId: string, options?: AuthOptions)` returning `{ promise, cancel }` + event channel
84
+ - `submitOtp(otp: string)`
85
+ - Session helpers (`getLastLoggedInUser`, `clearSessionTokens`, `clearDeviceData`).
86
+ - Provide `AuthEvent` typings and optionally `useHawcxAuth` React hook to simplify OTP UI.
87
+
88
+ ### Phase 3 – Session, Web, and Admin Utilities
89
+ - Bridge `devSessionManager` methods for device listing and token cleanup.
90
+ - Expose `webLogin(pin)` / `webApprove(token)` hooking into backend `/websession-*` endpoints via `HawcxSDK`.
91
+ - Document Domain/RP expectations from backend `DomainService` and how project IDs map to React clients.
92
+
93
+ ### Phase 4 – Push Notification Support
94
+ - Wrap `setAPNsDeviceToken`, `handlePushNotification`, `approveLoginRequest`, `declineLoginRequest`.
95
+ - Provide JS helpers for registering APNs token post-auth and handling push payloads.
96
+ - Update example app to demonstrate receiving a login approval request.
97
+
98
+ ### Phase 5 – Example App & QA Harness
99
+ - Build RN example mirroring `ios_demo_dev` flows (email input, OTP modal, success screen, push + web login tabs).
100
+ - Write integration script to run on a simulator hitting `https://dev-api.hawcx.com` (registration + login scenario).
101
+ - Add Detox/Maestro scenarios for OTP happy path and failure to ensure regressions are caught.
102
+
103
+ ### Phase 6 – Documentation & Release Prep
104
+ - Generate API reference (typedoc or hand-written Markdown) and copy relevant sections into `docs/`.
105
+ - Describe environment configuration, initialization snippet, error codes, troubleshooting, and migration guidance.
106
+ - Finalize podspec, `package.json` metadata, semantic versioning, changelog, and publish instructions.
107
+
108
+ ### Android React Native Enablement (New Work)
109
+ #### Phase A – Android module bootstrap & dependency wiring
110
+ - Create the `android/` library (Gradle wrapper, `build.gradle`, `gradle.properties`, manifest, and `src/main/java/com/hawcx/reactnative/`). Mirror the `react-native-builder-bob` template so autolinking works out of the box.
111
+ - Bring in the Android SDK artifact: either copy `dev_android/android_sdk/app/build/outputs/aar/hawcx-release.aar` into `android/libs/` or wire a git submodule. Document how to refresh this artifact whenever the native team bumps versions.
112
+ - Configure AGP (minSdk 23+, target 34), Kotlin 1.9+, `compileOptions` for Java 17, and add dependencies on `com.facebook.react:react-native` + the Hawcx AAR.
113
+ - Add `maven-publish` + `sourcesJar` so CI can run `./gradlew -p android assembleRelease lintRelease publishToMavenLocal` for validation before npm publish.
114
+
115
+ #### Phase B – Kotlin native module + config bridge
116
+ - Implement `HawcxReactNativeModule` (extends `ReactContextBaseJavaModule` + `LifecycleEventListener`) and `HawcxReactNativePackage`.
117
+ - Store a singleton `HawcxSDK` bound to `reactApplicationContext.applicationContext`, mirroring Swift’s lifecycle.
118
+ - Parse the JS config (`projectApiKey`, `oauthConfig`, future endpoint overrides) into `HawcxOAuthConfig`. Reject invalid payloads with the same error codes as iOS (`hawcx.config`, `hawcx.sdk`, `hawcx.input`).
119
+ - Expose `initialize`, `authenticate(userid)`, `submitOtp(otp)`, `getLastLoggedInUser()`, `clearSessionTokens(forUser)`, and `clearUserKeychainData(forUser)` as Promise-returning methods.
120
+ - Provide placeholder callback proxies (`AuthCallbackProxy`) that currently log events; Phase C will replace the internals with actual RN event emission while keeping Phase B green.
121
+
122
+ #### Phase C – Auth/session events & data mapping
123
+ - Create Kotlin proxies implementing `AuthV5Callback`, `DevSession.DevSessionCallback`, and `WebLoginManager.WebLoginCallback`. Convert models (`AuthSuccessPayload`, `DeviceDetails`, error codes) into `WritableMap` objects using the same keys/payloads as the Swift emitter.
124
+ - Use `DeviceEventManagerModule.RCTDeviceEventEmitter` to emit `hawcx.auth.event`, `hawcx.session.event`, `hawcx.push.event`. Guarantee events serialize on the UI thread to avoid race conditions.
125
+ - Bridge session helpers (`getDeviceDetails`, `getStoredDeviceDetails`, `webLogin`, `webApprove`) with Promise completion so JS callers can show loaders identical to iOS.
126
+ - Build exhaustive error translation (map each `AuthV5ErrorCode` + exception to the shared `HawcxAuthError` payload) and add Robolectric tests that assert the payload structure.
127
+
128
+ #### Phase D – Push + device lifecycle support
129
+ - Bridge `setFcmToken`, `userDidAuthenticate`, `handlePushNotification`, `approvePushRequest`, and `declinePushRequest` directly to `HawcxSDK`. Ensure `handlePushNotification` returns whether a requestId was processed so JS can decide to show fallback UI.
130
+ - Implement a `PushDelegateProxy` that conforms to `HawcxPushAuthDelegate`, emits push events, and mirrors the APNs payload shape (requestId, ipAddress, deviceInfo, location, timestamp).
131
+ - Provide a lightweight helper (e.g., `HawcxReactNativePushHandler`) that host apps and the RN example can call from their `FirebaseMessagingService` to feed payloads + tokens back into the SDK even when JS is not running.
132
+ - Surface cleanup helpers (`clearSessionTokens`, `clearUserKeychainData`, `clearLastLoggedInUser`) so RN apps can match the Compose demo’s “reset device” UX.
133
+
134
+ #### Phase E – JS/TS API + platform ergonomics
135
+ - Update `src/index.ts` to detect `Platform.OS` and fan out to `setApnsDeviceToken` vs `setFcmToken`, while maintaining backwards-compatible wrappers. Add `setPushDeviceToken`, `setFcmToken`, and `setApnsDeviceToken` exports with clear docs.
136
+ - Extend the TS event types so push events can include Android-only metadata (e.g., `location`, `deviceInfo`). Ensure hooks (`useHawcxAuth`, `useHawcxWebLogin`) continue to work without platform-specific code.
137
+ - Add Jest coverage that stubs the native module on Android and verifies the new methods reject invalid input the same way they do on iOS.
138
+ - Update `hawcxClient` helpers to expose `notifyUserAuthenticated`, `setPushDeviceToken`, and `handlePushNotification` semantics called out in the Android demo.
139
+
140
+ #### Phase F – Example app & Android QA harness
141
+ - Wire the RN example to run on Android emulators: ensure `npm run android` succeeds, `MainApplication.kt` (generated by RN) references the autolinked package, and Hermes is enabled.
142
+ - Document how to drop `google-services.json` under `example/android/app/` and where to paste `projectApiKey` + OAuth values (mirroring `example/src/hawcx.config.ts`).
143
+ - Add a sample `FirebaseMessagingService` (Kotlin) inside the example app module that forwards tokens/payloads to the RN bridge so push approvals can be exercised without custom native code.
144
+ - Expand Maestro/Detox scripts with Android variants (registration/login, OTP failure, push approval) and pipe them into CI.
145
+
146
+ #### Phase G – Documentation, release, and handoff
147
+ - Update `README.md`, `CHANGELOG.md`, and `docs/RELEASE.md` with Android setup instructions (Gradle sync, FCM requirements, example app steps, release checks for `./gradlew -p android assembleRelease lintRelease`).
148
+ - Capture how to refresh the Hawcx Android AAR (pull from `dev_android/android_sdk`, update checksums, bump versions) so future engineers don’t guess.
149
+ - Ensure the release checklist includes dual-platform smoke tests (`npm run ios`, `npm run android`), TypeScript build, Swift compilation, and Android linting before tagging/publishing.
150
+ - Once Android ships, add new tracker rows + changelog entries summarizing parity status and any follow-up items (analytics hooks, tenant overrides, etc.).
151
+
152
+ ## Testing & Observability Strategy
153
+ - **Unit Tests**: Swift bridging/event translation, Kotlin Robolectric tests for `HawcxReactNativeModule` + callback proxies, and Jest for TS utilities/hooks.
154
+ - **Integration Tests**: Run the RN example on iOS (`npm run ios`) and Android (`npm run android`) against the dev backend with real API keys to cover registration, login, push approvals, and web login.
155
+ - **E2E**: Detox/Maestro suites per platform (cover OTP happy path + failure, push approval, credential reset, biometric opt-in) wired into CI; reuse Maestro YAML from the native demos where possible.
156
+ - **Logging & Telemetry**: Surface `SDKLogger` output to JS only in debug builds, hook Android logs via Logcat, and ensure no secrets leave native boundaries. Optionally expose an analytics callback so host apps can track auth outcomes consistently.
157
+
158
+ ## Deployment & Release Checklist
159
+ 1. **Sync + verify**
160
+ ```bash
161
+ git checkout main
162
+ git pull origin main
163
+ git status
164
+ ```
165
+ 2. **Quality gates (repo root unless noted)**
166
+ ```bash
167
+ npm install
168
+ npm run lint
169
+ npm run typecheck
170
+ npm test
171
+ npm run build
172
+ pod lib lint HawcxReactNative.podspec
173
+ ./gradlew -p android clean lintRelease testReleaseUnitTest assembleRelease publishToMavenLocal
174
+ ```
175
+ Confirm `android/build/reports/lint-results-release.html` and `lint-results.html` show no issues.
176
+ 3. **Update versioning artifacts**
177
+ - Bump `package.json` version and the corresponding Android `gradle.properties` (if we mirror the number there).
178
+ - Edit `CHANGELOG.md` with the release summary (mention whether Android/iOS changes are included).
179
+ - Commit once tests pass: `git commit -am "chore: release vX.Y.Z"`.
180
+ 4. **Smoke test example apps**
181
+ ```bash
182
+ cd example
183
+ npm install
184
+ npm run ios
185
+ npm run android
186
+ cd ..
187
+ ```
188
+ Verify OTP, login, push, and web login flows using dev credentials + Firebase config.
189
+ 5. **Publish artifacts**
190
+ - `npm publish --access public`
191
+ - `pod repo push trunk HawcxReactNative.podspec --allow-warnings`
192
+ 6. **Tag + push**
193
+ ```bash
194
+ git tag vX.Y.Z
195
+ git push origin main
196
+ git push origin vX.Y.Z
197
+ ```
198
+ 7. **Customer handoff**
199
+ - Share the npm version + changelog.
200
+ - Provide iOS pod install instructions and Android setup notes (FCM token wiring, `google-services.json`, Gradle sync).
201
+ - Update this plan with any new follow-ups (e.g., analytics hooks, tenant overrides).
202
+
203
+ ## Progress Tracker
204
+ _Update this table as you work. Status values: TODO / IN-PROGRESS / BLOCKED / DONE._
205
+
206
+ | Phase | Task | Owner | Status | Notes |
207
+ | --- | --- | --- | --- | --- |
208
+ | 0 | Repo + tooling scaffold | Codex (2025-11-13) | DONE | Initialized RN package structure, tooling, Swift bridge scaffold |
209
+ | 1 | Native bridge skeleton & callbacks | Codex (2025-11-13) | DONE | Swift bridge wraps HawcxSDK; JS API validates inputs & emits events |
210
+ | 2 | TypeScript API + hooks | Codex (2025-11-13) | DONE | Added HawcxClient, useHawcxAuth, Jest coverage, README docs |
211
+ | 3 | Session + web login utilities | Codex (2025-11-15) | DONE | Added device details + web login/approve bridges, hooks, tests |
212
+ | 4 | Push notification bridge | Codex (2025-11-15) | DONE | Added APNs token handling, push events, approve/decline helpers |
213
+ | 5 | Example RN app + E2E harness | Codex (2025-11-15) | DONE | Added example app + Maestro script under `example/` |
214
+ | 6 | Docs, release pipeline, publishing | Codex (2025-11-15) | DONE | Added CHANGELOG, docs/RELEASE, README release steps, package metadata |
215
+ | A | Android module bootstrap & dependency wiring | Codex (2025-11-15) | DONE | Added Gradle lib + hawcx-5.1.0.aar bundle, README refresh steps, Gradle tasks passing |
216
+ | B | Kotlin native module + auth bridge | Codex (2025-11-15) | DONE | Added HawcxSDK-backed `initialize`/`authenticate`/OTP + cleanup helpers (auth events logging until Phase C) |
217
+ | C | Session + web callbacks + error mapping | Codex (2025-11-15) | DONE | Auth/session events now emitted from Android; added `getDeviceDetails`, `webLogin`, `webApprove` bridges |
218
+ | D | Push + lifecycle support | Codex (2025-11-15) | DONE | Added push delegate proxy, `setFcmToken`, `handlePushNotification`, approve/decline |
219
+ | E | JS/TS platform ergonomics | Codex (2025-11-15) | DONE | Added cross-platform push helpers, Android guards, README/testing updates |
220
+ | F | Example app + Android QA harness | Codex (2025-11-15) | DONE | Example app updated for Android + push harness, README + Maestro docs refreshed |
221
+ | G | Docs + release updates | Codex (2025-11-15) | DONE | Release guide now covers Android Gradle checks + dual-platform handoff |
222
+
223
+ Add rows if new scope appears (e.g., analytics hook, tenant-specific API). Include blockers and links to PRs.
224
+
225
+ ## Key References
226
+ - Backend repo: `hawcx_core_auth`
227
+ - `app/api/v5/endpoints.py`, `app/api/v5/device_verification_manage.py`, `app/shared/domain_service.py`.
228
+ - iOS SDK: `dev_ios/ios_sdk/HawcxFramework/`
229
+ - Crypto/storage: `internal/V5Crypto.swift`, `internal/V5CredentialStore.swift`, `internal/V5EnvironmentInfo.swift`.
230
+ - Auth orchestration: `auth/HawcxAuthV5.swift`, `internal/HawcxSDK.swift`, `network/ApiService.swift`.
231
+ - Fingerprint generation: `utils/FingerprintGenerator.swift`.
232
+ - Android SDK: `dev_android/android_sdk/app/src/main/Kotlin/com/hawcx`
233
+ - Facade & config: `internal/HawcxSDK.kt`, `internal/HawcxOAuthConfig.kt`, `internal/OAuthTokenVerifier.kt`.
234
+ - Auth + storage: `auth/HawcxAuthV5.kt`, `v5/V5CredentialStore.kt`, `storage/KeystoreManager.kt`, `utils/FingerprintGenerator.kt`.
235
+ - Push/session: `auth/DevSession.kt`, `auth/WebLoginManager.kt`, `utils/HawcxPushAuthDelegate.kt`, `utils/DeviceDetailsUtil.kt`.
236
+ - Demo apps
237
+ - iOS: `dev_ios/ios_demo_dev/HawcxDemoApp`
238
+ - Android: `dev_android/HawcxAndroidDemoApp/HawcxDemoApp` (`MainApplication.kt`, `viewmodel/`, `messaging/DemoFirebaseMessagingService.kt`) for parity, push wiring, and Compose UX flows.
239
+
240
+ Keep this plan current. If you modify architecture, document the rationale here so any new engineer can resume without knowledge gaps.
@@ -0,0 +1,163 @@
1
+ import {
2
+ initialize,
3
+ authenticate,
4
+ submitOtp,
5
+ webLogin,
6
+ webApprove,
7
+ approvePushRequest,
8
+ declinePushRequest,
9
+ setPushDeviceToken,
10
+ HawcxClient,
11
+ HawcxAuthError,
12
+ __INTERNAL_EVENTS__,
13
+ } from '../index';
14
+ import { Platform } from 'react-native';
15
+
16
+ const ORIGINAL_PLATFORM = Platform.OS;
17
+
18
+ const overridePlatformOS = (os: typeof Platform.OS) => {
19
+ Object.defineProperty(Platform, 'OS', {
20
+ configurable: true,
21
+ get: () => os,
22
+ });
23
+ };
24
+
25
+ describe('Hawcx React Native SDK', () => {
26
+ it('rejects initialize call without api key', async () => {
27
+ await expect(initialize({ projectApiKey: '' })).rejects.toThrow('projectApiKey is required');
28
+ });
29
+
30
+ it('rejects authenticate call without userId', async () => {
31
+ await expect(authenticate(' ')).rejects.toThrow('userId is required');
32
+ });
33
+
34
+ it('rejects submitOtp call without otp', async () => {
35
+ await expect(submitOtp('')).rejects.toThrow('otp is required');
36
+ });
37
+
38
+ it('rejects webLogin call without pin', async () => {
39
+ await expect(webLogin('')).rejects.toThrow('pin is required');
40
+ });
41
+
42
+ it('rejects webApprove call without token', async () => {
43
+ await expect(webApprove('')).rejects.toThrow('token is required');
44
+ });
45
+
46
+ it('rejects approvePushRequest call without requestId', async () => {
47
+ await expect(approvePushRequest('')).rejects.toThrow('requestId is required');
48
+ });
49
+
50
+ it('rejects declinePushRequest call without requestId', async () => {
51
+ await expect(declinePushRequest('')).rejects.toThrow('requestId is required');
52
+ });
53
+ });
54
+
55
+ describe('HawcxClient helpers', () => {
56
+ const client = new HawcxClient();
57
+ const emitAuth = (event: unknown) => {
58
+ __INTERNAL_EVENTS__.authEmitter.emit(__INTERNAL_EVENTS__.authEventName, event as never);
59
+ };
60
+ const emitSession = (event: unknown) => {
61
+ __INTERNAL_EVENTS__.sessionEmitter.emit(__INTERNAL_EVENTS__.sessionEventName, event as never);
62
+ };
63
+
64
+ afterEach(() => {
65
+ __INTERNAL_EVENTS__.authEmitter.removeAllListeners(__INTERNAL_EVENTS__.authEventName);
66
+ __INTERNAL_EVENTS__.sessionEmitter.removeAllListeners(__INTERNAL_EVENTS__.sessionEventName);
67
+ });
68
+
69
+ it('resolves authenticate promise on success event', async () => {
70
+ const invocation = client.authenticate('user@example.com');
71
+ emitAuth({
72
+ type: 'auth_success',
73
+ payload: { isLoginFlow: true, accessToken: 'a', refreshToken: 'r' },
74
+ });
75
+ await expect(invocation.promise).resolves.toEqual({
76
+ isLoginFlow: true,
77
+ accessToken: 'a',
78
+ refreshToken: 'r',
79
+ });
80
+ });
81
+
82
+ it('rejects authenticate promise on error event', async () => {
83
+ const invocation = client.authenticate('user@example.com');
84
+ emitAuth({
85
+ type: 'auth_error',
86
+ payload: { code: 'otp_invalid', message: 'Invalid OTP' },
87
+ });
88
+ await expect(invocation.promise).rejects.toBeInstanceOf(HawcxAuthError);
89
+ });
90
+
91
+ it('rejects authenticate promise when cancelled', async () => {
92
+ const invocation = client.authenticate('user@example.com');
93
+ invocation.cancel();
94
+ await expect(invocation.promise).rejects.toBeInstanceOf(HawcxAuthError);
95
+ });
96
+
97
+ it('invokes web login session event handler', async () => {
98
+ const handler = jest.fn();
99
+ await client.webLogin('1234', { onEvent: handler });
100
+ emitSession({ type: 'session_success' });
101
+ expect(handler).toHaveBeenCalledWith({ type: 'session_success' });
102
+ });
103
+
104
+ it('invokes web approve session event handler', async () => {
105
+ const handler = jest.fn();
106
+ await client.webApprove('token', { onEvent: handler });
107
+ emitSession({
108
+ type: 'session_error',
109
+ payload: { code: 'failedApprove', message: 'Failed' },
110
+ });
111
+ expect(handler).toHaveBeenCalledWith({
112
+ type: 'session_error',
113
+ payload: { code: 'failedApprove', message: 'Failed' },
114
+ });
115
+ });
116
+
117
+ it('invokes push event handlers', async () => {
118
+ const handler = jest.fn();
119
+ const subscription = client.addPushListener(handler);
120
+ __INTERNAL_EVENTS__.pushEmitter.emit(__INTERNAL_EVENTS__.pushEventName, {
121
+ type: 'push_login_request',
122
+ payload: {
123
+ requestId: 'req',
124
+ ipAddress: '1.1.1.1',
125
+ deviceInfo: 'Safari on Mac',
126
+ location: 'SF',
127
+ timestamp: '2025-01-01T00:00:00Z',
128
+ },
129
+ });
130
+ expect(handler).toHaveBeenCalledWith({
131
+ type: 'push_login_request',
132
+ payload: {
133
+ requestId: 'req',
134
+ ipAddress: '1.1.1.1',
135
+ deviceInfo: 'Safari on Mac',
136
+ location: 'SF',
137
+ timestamp: '2025-01-01T00:00:00Z',
138
+ },
139
+ });
140
+ subscription.remove();
141
+ });
142
+ });
143
+
144
+ describe('push token helpers', () => {
145
+ afterEach(() => {
146
+ overridePlatformOS(ORIGINAL_PLATFORM);
147
+ jest.restoreAllMocks();
148
+ });
149
+
150
+ it('rejects APNs string tokens on iOS', async () => {
151
+ overridePlatformOS('ios');
152
+ await expect(setPushDeviceToken('abc')).rejects.toThrow(
153
+ 'APNs tokens must be provided as byte arrays or Uint8Arrays',
154
+ );
155
+ });
156
+
157
+ it('rejects non-string tokens on Android', async () => {
158
+ overridePlatformOS('android');
159
+ await expect(setPushDeviceToken([1, 2, 3])).rejects.toThrow(
160
+ 'FCM token must be a string on Android',
161
+ );
162
+ });
163
+ });