From ed58f5d3c4c5f81f2ad12dd4289467698fa4b65f Mon Sep 17 00:00:00 2001 From: Jared Perreault <90656038+jaredperreault-okta@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:23:00 -0400 Subject: [PATCH 1/2] Webcrypto Polyfill package (#23) OKTA-1147173 feat: MVP of react-native-webcrypto-bridge package --- .circleci/config.yml | 88 + e2e/apps/react-native-oidc/.gitignore | 2 + e2e/apps/react-native-oidc/app.config.ts | 36 +- .../react-native-oidc/app/(login)/index.tsx | 41 +- .../react-native-oidc/app/(login)/token.tsx | 4 +- e2e/apps/react-native-oidc/app/_layout.tsx | 24 - e2e/apps/react-native-oidc/auth.ts | 50 +- e2e/apps/react-native-oidc/babel.config.js | 10 + e2e/apps/react-native-oidc/eslint.config.js | 7 +- e2e/apps/react-native-oidc/hooks/useAuth.ts | 20 +- e2e/apps/react-native-oidc/index.js | 10 + e2e/apps/react-native-oidc/metro.config.js | 12 +- e2e/apps/react-native-oidc/package.json | 61 +- .../react-native-oidc/react-native.config.js | 9 + e2e/apps/react-native-oidc/tsconfig.json | 3 +- package.json | 4 +- packages/auth-foundation/package.json | 6 +- packages/oauth2-flows/package.json | 1 - packages/react-native-platform/.eslintrc.cjs | 4 + packages/react-native-platform/LICENSE | 189 + packages/react-native-platform/package.json | 76 + .../react-native-platform/rollup.config.mjs | 18 + packages/react-native-platform/src/index.ts | 28 + .../src/platform/defaults.ts | 12 + packages/react-native-platform/tsconfig.json | 24 + .../.eslintrc.cjs | 4 + .../react-native-webcrypto-bridge/LICENSE | 189 + .../react-native-webcrypto-bridge/README.md | 5 + .../react-native-webcrypto-bridge/TESTS.md | 292 ++ .../android/.gitignore | 3 + .../android/build.gradle | 75 + .../android/gradle.properties | 2 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43583 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + .../android/gradlew | 251 ++ .../android/gradlew.bat | 94 + .../android/src/main/AndroidManifest.xml | 4 + .../webcryptobridge/CryptoAlgorithmHandler.kt | 77 + .../CryptoAlgorithmRegistry.kt | 82 + .../com/okta/webcryptobridge/CryptoUtils.kt | 50 + .../webcryptobridge/WebCryptoBridgeModule.kt | 514 +++ .../webcryptobridge/WebCryptoBridgePackage.kt | 16 + .../webcryptobridge/algorithms/RSAHandler.kt | 107 + .../CryptoAlgorithmRegistryTest.kt | 199 ++ .../okta/webcryptobridge/CryptoUtilsTest.kt | 117 + .../algorithms/RSAHandlerTest.kt | 233 ++ .../webcryptobridge/integration/JWKTest.kt | 95 + .../integration/KeyGenerationTest.kt | 136 + .../integration/SignatureTest.kt | 36 + .../webcryptobridge/integration/TestUtils.kt | 30 + .../integration/WebCryptoBridgeModuleTest.kt | 206 ++ .../babel.config.cjs | 1 + .../ios/.gitignore | 5 + .../ios/Package.swift | 29 + .../RNWebCryptoBridge/AlgorithmRegistry.swift | 78 + .../CryptoAlgorithmHandler.swift | 41 + .../RNWebCryptoBridge/CryptoExtensions.swift | 26 + .../RNWebCryptoBridge/RSAHandler.swift | 65 + .../RNWebCryptoBridge/RSAKeyUtils.swift | 183 + .../RNWebCryptoBridge/WebCryptoBridge.h | 11 + .../RNWebCryptoBridge/WebCryptoBridge.swift | 446 +++ .../RNWebCryptoBridge/WebCryptoBridgeModule.m | 46 + .../AlgorithmRegistryTests.swift | 165 + .../Integration/IntegrationTestHelpers.swift | 156 + .../Integration/JWKIntegrationTests.swift | 202 ++ .../KeyGenerationIntegrationTests.swift | 168 + .../SignatureIntegrationTests.swift | 185 + .../WebCryptoBridgeIntegrationTestCase.swift | 171 + .../RSAHandlerTests.swift | 158 + .../RSAKeyUtilsTests.swift | 63 + .../jest.config.js | 28 + .../package.json | 81 + .../react-native-webcrypto-bridge.podspec | 37 + .../react-native.config.js | 10 + .../rollup.config.mjs | 17 + .../src/NativeWebCryptoBridge.ts | 70 + .../src/WebCryptoPolyfill.ts | 221 ++ .../src/index.ts | 6 + .../test/jest.setup.js | 36 + .../test/spec/digest.spec.ts | 59 + .../test/spec/getRandomValues.spec.ts | 50 + .../test/spec/importKey.spec.ts | 69 + .../test/spec/polyfill.spec.ts | 30 + .../test/spec/verify.spec.ts | 187 + .../test/tsconfig.json | 20 + .../tsconfig.json | 23 + scripts/bacon/publish.sh | 11 +- tooling/eslint-config/react-native-sdk.js | 9 + tooling/eslint-config/sdk.js | 3 +- yarn.lock | 3058 ++++++++++++----- 90 files changed, 8799 insertions(+), 988 deletions(-) create mode 100644 .circleci/config.yml create mode 100644 e2e/apps/react-native-oidc/babel.config.js create mode 100644 e2e/apps/react-native-oidc/index.js create mode 100644 e2e/apps/react-native-oidc/react-native.config.js create mode 100644 packages/react-native-platform/.eslintrc.cjs create mode 100644 packages/react-native-platform/LICENSE create mode 100644 packages/react-native-platform/package.json create mode 100644 packages/react-native-platform/rollup.config.mjs create mode 100644 packages/react-native-platform/src/index.ts create mode 100644 packages/react-native-platform/src/platform/defaults.ts create mode 100644 packages/react-native-platform/tsconfig.json create mode 100644 packages/react-native-webcrypto-bridge/.eslintrc.cjs create mode 100644 packages/react-native-webcrypto-bridge/LICENSE create mode 100644 packages/react-native-webcrypto-bridge/README.md create mode 100644 packages/react-native-webcrypto-bridge/TESTS.md create mode 100644 packages/react-native-webcrypto-bridge/android/.gitignore create mode 100644 packages/react-native-webcrypto-bridge/android/build.gradle create mode 100644 packages/react-native-webcrypto-bridge/android/gradle.properties create mode 100644 packages/react-native-webcrypto-bridge/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 packages/react-native-webcrypto-bridge/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 packages/react-native-webcrypto-bridge/android/gradlew create mode 100644 packages/react-native-webcrypto-bridge/android/gradlew.bat create mode 100644 packages/react-native-webcrypto-bridge/android/src/main/AndroidManifest.xml create mode 100644 packages/react-native-webcrypto-bridge/android/src/main/java/com/okta/webcryptobridge/CryptoAlgorithmHandler.kt create mode 100644 packages/react-native-webcrypto-bridge/android/src/main/java/com/okta/webcryptobridge/CryptoAlgorithmRegistry.kt create mode 100644 packages/react-native-webcrypto-bridge/android/src/main/java/com/okta/webcryptobridge/CryptoUtils.kt create mode 100644 packages/react-native-webcrypto-bridge/android/src/main/java/com/okta/webcryptobridge/WebCryptoBridgeModule.kt create mode 100644 packages/react-native-webcrypto-bridge/android/src/main/java/com/okta/webcryptobridge/WebCryptoBridgePackage.kt create mode 100644 packages/react-native-webcrypto-bridge/android/src/main/java/com/okta/webcryptobridge/algorithms/RSAHandler.kt create mode 100644 packages/react-native-webcrypto-bridge/android/src/test/java/com/okta/webcryptobridge/CryptoAlgorithmRegistryTest.kt create mode 100644 packages/react-native-webcrypto-bridge/android/src/test/java/com/okta/webcryptobridge/CryptoUtilsTest.kt create mode 100644 packages/react-native-webcrypto-bridge/android/src/test/java/com/okta/webcryptobridge/algorithms/RSAHandlerTest.kt create mode 100644 packages/react-native-webcrypto-bridge/android/src/test/java/com/okta/webcryptobridge/integration/JWKTest.kt create mode 100644 packages/react-native-webcrypto-bridge/android/src/test/java/com/okta/webcryptobridge/integration/KeyGenerationTest.kt create mode 100644 packages/react-native-webcrypto-bridge/android/src/test/java/com/okta/webcryptobridge/integration/SignatureTest.kt create mode 100644 packages/react-native-webcrypto-bridge/android/src/test/java/com/okta/webcryptobridge/integration/TestUtils.kt create mode 100644 packages/react-native-webcrypto-bridge/android/src/test/java/com/okta/webcryptobridge/integration/WebCryptoBridgeModuleTest.kt create mode 100644 packages/react-native-webcrypto-bridge/babel.config.cjs create mode 100644 packages/react-native-webcrypto-bridge/ios/.gitignore create mode 100644 packages/react-native-webcrypto-bridge/ios/Package.swift create mode 100644 packages/react-native-webcrypto-bridge/ios/Sources/RNWebCryptoBridge/AlgorithmRegistry.swift create mode 100644 packages/react-native-webcrypto-bridge/ios/Sources/RNWebCryptoBridge/CryptoAlgorithmHandler.swift create mode 100644 packages/react-native-webcrypto-bridge/ios/Sources/RNWebCryptoBridge/CryptoExtensions.swift create mode 100644 packages/react-native-webcrypto-bridge/ios/Sources/RNWebCryptoBridge/RSAHandler.swift create mode 100644 packages/react-native-webcrypto-bridge/ios/Sources/RNWebCryptoBridge/RSAKeyUtils.swift create mode 100644 packages/react-native-webcrypto-bridge/ios/Sources/RNWebCryptoBridge/WebCryptoBridge.h create mode 100644 packages/react-native-webcrypto-bridge/ios/Sources/RNWebCryptoBridge/WebCryptoBridge.swift create mode 100644 packages/react-native-webcrypto-bridge/ios/Sources/RNWebCryptoBridge/WebCryptoBridgeModule.m create mode 100644 packages/react-native-webcrypto-bridge/ios/Tests/RNWebCryptoBridgeTests/AlgorithmRegistryTests.swift create mode 100644 packages/react-native-webcrypto-bridge/ios/Tests/RNWebCryptoBridgeTests/Integration/IntegrationTestHelpers.swift create mode 100644 packages/react-native-webcrypto-bridge/ios/Tests/RNWebCryptoBridgeTests/Integration/JWKIntegrationTests.swift create mode 100644 packages/react-native-webcrypto-bridge/ios/Tests/RNWebCryptoBridgeTests/Integration/KeyGenerationIntegrationTests.swift create mode 100644 packages/react-native-webcrypto-bridge/ios/Tests/RNWebCryptoBridgeTests/Integration/SignatureIntegrationTests.swift create mode 100644 packages/react-native-webcrypto-bridge/ios/Tests/RNWebCryptoBridgeTests/Integration/WebCryptoBridgeIntegrationTestCase.swift create mode 100644 packages/react-native-webcrypto-bridge/ios/Tests/RNWebCryptoBridgeTests/RSAHandlerTests.swift create mode 100644 packages/react-native-webcrypto-bridge/ios/Tests/RNWebCryptoBridgeTests/RSAKeyUtilsTests.swift create mode 100644 packages/react-native-webcrypto-bridge/jest.config.js create mode 100644 packages/react-native-webcrypto-bridge/package.json create mode 100644 packages/react-native-webcrypto-bridge/react-native-webcrypto-bridge.podspec create mode 100644 packages/react-native-webcrypto-bridge/react-native.config.js create mode 100644 packages/react-native-webcrypto-bridge/rollup.config.mjs create mode 100644 packages/react-native-webcrypto-bridge/src/NativeWebCryptoBridge.ts create mode 100644 packages/react-native-webcrypto-bridge/src/WebCryptoPolyfill.ts create mode 100644 packages/react-native-webcrypto-bridge/src/index.ts create mode 100644 packages/react-native-webcrypto-bridge/test/jest.setup.js create mode 100644 packages/react-native-webcrypto-bridge/test/spec/digest.spec.ts create mode 100644 packages/react-native-webcrypto-bridge/test/spec/getRandomValues.spec.ts create mode 100644 packages/react-native-webcrypto-bridge/test/spec/importKey.spec.ts create mode 100644 packages/react-native-webcrypto-bridge/test/spec/polyfill.spec.ts create mode 100644 packages/react-native-webcrypto-bridge/test/spec/verify.spec.ts create mode 100644 packages/react-native-webcrypto-bridge/test/tsconfig.json create mode 100644 packages/react-native-webcrypto-bridge/tsconfig.json create mode 100644 tooling/eslint-config/react-native-sdk.js diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..6c98d465 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,88 @@ +version: 2.1 + +orbs: + general-platform-helpers: okta/general-platform-helpers@1.9.4 + platform-helpers: okta/platform-helpers@2.0.0 + macos: circleci/macos@2 + android: circleci/android@3.1.0 + +executors: + # android: + # docker: + # - image: cimg/android:2024.12 + # environment: + # GRADLE_OPTS: -Xmx2g + # resource_class: large + + macos: + macos: + xcode: "16.3" + resource_class: m4pro.medium + +jobs: + test-android: + executor: + name: android/android_machine + resource_class: large + tag: default + steps: + - checkout + + - restore_cache: + keys: + - gradle-{{ checksum "packages/react-native-webcrypto-bridge/android/build.gradle" }} + - gradle- + + - run: + name: Run Android Unit and Integration Tests + command: | + cd packages/react-native-webcrypto-bridge/android + ./gradlew testDebugUnitTest --info + + - save_cache: + key: gradle-{{ checksum "packages/react-native-webcrypto-bridge/android/build.gradle" }} + paths: + - ~/.gradle + - .gradle + + - store_test_results: + path: packages/react-native-webcrypto-bridge/android/build/test-results + + - store_artifacts: + path: packages/react-native-webcrypto-bridge/android/build/reports + destination: android-test-reports + + test-ios: + executor: macos + steps: + - checkout + + - restore_cache: + keys: + - swift-spm-{{ checksum "packages/react-native-webcrypto-bridge/ios/Package.swift" }} + - swift-spm- + + - run: + name: Run iOS Unit and Integration Tests + command: | + cd packages/react-native-webcrypto-bridge/ios + swift test --verbose + + - save_cache: + key: swift-spm-{{ checksum "packages/react-native-webcrypto-bridge/ios/Package.swift" }} + paths: + - packages/react-native-webcrypto-bridge/ios/.build + + - store_test_results: + path: packages/react-native-webcrypto-bridge/ios/.build/test-results + + - store_artifacts: + path: packages/react-native-webcrypto-bridge/ios/.build + destination: ios-build-artifacts + +workflows: + version: 2 + build_and_test: + jobs: + - test-android + - test-ios diff --git a/e2e/apps/react-native-oidc/.gitignore b/e2e/apps/react-native-oidc/.gitignore index 8cbb0d26..9516583d 100644 --- a/e2e/apps/react-native-oidc/.gitignore +++ b/e2e/apps/react-native-oidc/.gitignore @@ -39,3 +39,5 @@ yarn-error.* *.tsbuildinfo app-example + +.idea/ \ No newline at end of file diff --git a/e2e/apps/react-native-oidc/app.config.ts b/e2e/apps/react-native-oidc/app.config.ts index 7856dacb..7509c40d 100644 --- a/e2e/apps/react-native-oidc/app.config.ts +++ b/e2e/apps/react-native-oidc/app.config.ts @@ -17,5 +17,39 @@ export default ({ config }: ConfigContext) => ({ ...config, extra: { env - } + }, + "android": { + "package": "com.anonymous.reporeactnativeoidc" + }, + "ios": { + "bundleIdentifier": "com.anonymous.reporeactnativeoidc" + }, + scheme: "com.oktapreview.jperreault-test", + intentFilters: [ + { + action: "VIEW", + autoVerify: true, + data: [ + { + scheme: "com.oktapreview.jperreault-test" + } + ], + category: ["BROWSABLE", "DEFAULT"] + } + ], + "plugins": [ + "expo-font", + "expo-router", + [ + "expo-build-properties", + { + "ios": { + "newArchEnabled": true + }, + "android": { + "newArchEnabled": true + } + } + ] + ] }); \ No newline at end of file diff --git a/e2e/apps/react-native-oidc/app/(login)/index.tsx b/e2e/apps/react-native-oidc/app/(login)/index.tsx index 13dcace1..e3fd92d3 100644 --- a/e2e/apps/react-native-oidc/app/(login)/index.tsx +++ b/e2e/apps/react-native-oidc/app/(login)/index.tsx @@ -1,5 +1,5 @@ -import { useEffect } from 'react'; -import { StyleSheet } from 'react-native'; +import { useCallback, useEffect, useState } from 'react'; +import { StyleSheet, Button } from 'react-native'; import { Image } from 'expo-image'; import Constants from 'expo-constants'; import { useAuth } from '@/hooks/useAuth'; @@ -12,12 +12,42 @@ import { HelloWave } from '@/components/HelloWave'; export default function LoginScreen () { const { signIn } = useAuth(); + const [ authError, setAuthError ] = useState(null); + + const signInFunc = useCallback(async () => { + try { + await signIn('/(login)/token'); + } + catch (err) { + setAuthError(err as Error); + } + }, [signIn, setAuthError]); useEffect(() => { (async () => { - await signIn('/(login)/token'); + await signInFunc(); })(); - }, [signIn]); + }, [signInFunc]); + + let view = ( + <> + Loading... + + + ); + + if (authError) { + view = ( + <> + Error + {authError.message} +