diff --git a/README.md b/README.md index 90f40b1..aa396cd 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ React Native Credentials Manager ![App Screens](IMG/flow.png) -A React Native library that implements the [Credential Manager](https://developer.android.com/identity/sign-in/credential-manager) API for Android. This library allows you to manage passwords, passkeys and google signin in your React Native applications. +A React Native library that implements the [Credential Manager](https://developer.android.com/identity/sign-in/credential-manager) API for Android and [AuthenticationServices](https://developer.apple.com/documentation/authenticationservices) for iOS. This library allows you to manage passwords, passkeys and platform-specific sign-in (Google Sign-In on Android, Apple Sign In on iOS) in your React Native applications.

@@ -18,5 +18,196 @@ A React Native library that implements the [Credential Manager](https://develope

+## Platform Support + +- ✅ **Android**: Full implementation with Credential Manager API (Android 14+ / API 34+) +- ✅ **iOS**: Full implementation with AuthenticationServices (iOS 16.0+) + +### Platform-Specific Features + +| Feature | Android | iOS | +| ------------------------- | ------------------------- | --------------------------------- | +| Passkeys | ✅ Credential Manager API | ✅ AuthenticationServices | +| AutoFill Password Support | ✅ Credential Manager API | ✅ AuthenticationServices | +| Manual Password Storage | ✅ Credential Manager API | ❌ Not supported (iOS limitation) | +| Third-party Sign In | ✅ Google Sign In | ✅ Apple Sign In | + +> [!NOTE] > **iOS Implementation**: This library strictly follows Apple's Authentication Services framework. Manual password storage is not supported on iOS as it's not part of Apple's official Authentication Services APIs. Use AutoFill passwords instead. + > [!IMPORTANT] > 📚 **Documentation has moved!** The complete documentation is now available at [https://docs.benjamineruvieru.com/docs/react-native-credentials-manager/](https://docs.benjamineruvieru.com/docs/react-native-credentials-manager/) + +## Platform-Specific Parameters + +When using this library, be aware that some parameters are platform-specific: + +| Function | Parameter | Platform Support | Notes | +| ---------------------- | --------------------------------------- | ---------------- | ----------------------------------------------------------------- | +| `signUpWithPasskeys()` | `preferImmediatelyAvailableCredentials` | Android only | This parameter is ignored on iOS | +| `signUpWithPassword()` | All parameters | Android only | This function is not supported on iOS and will throw an error | +| `signIn()` | `googleSignIn` | Android only | This parameter is ignored on iOS | +| `signIn()` | `appleSignIn` | iOS only | This parameter is ignored on Android | +| `signUpWithGoogle()` | All parameters | Cross-platform | Uses Google Sign-In on Android, Apple Sign-In on iOS | +| `signUpWithApple()` | All parameters | iOS only | This function is not supported on Android and will throw an error | + +### Handling Platform Differences + +To handle these platform differences, you can use conditional code: + +```typescript +// For passkey registration +await signUpWithPasskeys( + requestJson, + Platform.OS === 'android' ? true : false // preferImmediatelyAvailableCredentials +); + +// For sign-in +await signIn( + [ + 'passkeys', + 'password', + Platform.OS === 'android' ? 'google-signin' : 'apple-signin', + ], + { + passkeys: passkeyParams, + ...(Platform.OS === 'android' + ? { googleSignIn: { serverClientId: 'your-client-id' } } + : { appleSignIn: { requestedScopes: ['fullName', 'email'] } }), + } +); +``` + +## iOS Setup Requirements + +### 1. Associated Domains + +Add the Associated Domains capability to your iOS app: + +1. In Xcode, select your project +2. Go to Signing & Capabilities +3. Add "Associated Domains" capability +4. Add your domain with the `webcredentials` service: `webcredentials:yourdomain.com` + +### 2. Apple App Site Association (AASA) + +Ensure your domain has a proper AASA file at `https://yourdomain.com/.well-known/apple-app-site-association`: + +```json +{ + "webcredentials": { + "apps": ["TEAMID.com.yourcompany.yourapp"] + } +} +``` + +### 3. Apple Sign In Setup (Optional) + +If using Apple Sign In, configure it in your Apple Developer account: + +1. Enable "Sign In with Apple" capability in Xcode +2. Configure Sign In with Apple in your Apple Developer account +3. Add your app's bundle identifier to the Sign In with Apple configuration + +## Quick Example + +```typescript +import { + signUpWithPasskeys, + signUpWithPassword, // Android only - throws error on iOS + signUpWithGoogle, // Cross-platform: Google on Android, Apple on iOS + signUpWithApple, // iOS-specific function + signIn, + signOut, + type Credential, + type AppleCredential, + type GoogleCredential, + type AppleSignInParams, + type GoogleSignInParams, +} from 'react-native-credentials-manager'; + +// Sign up with passkey (works on both platforms) +const passkeyResult = await signUpWithPasskeys({ + challenge: 'base64-challenge', + rp: { name: 'Your App', id: 'yourdomain.com' }, + user: { id: 'user-id', name: 'username', displayName: 'User Name' }, + // ... other WebAuthn options +}); + +// Password handling - platform differences +try { + // This will work on Android but throw an error on iOS + await signUpWithPassword({ username: 'user', password: 'pass' }); +} catch (error) { + if (Platform.OS === 'ios') { + console.log( + 'Manual password storage not supported on iOS. Use AutoFill instead.' + ); + } +} + +// Unified sign in (supports passkeys, AutoFill passwords, and platform sign-in) +const credential = await signIn( + ['passkeys', 'password', 'google-signin'], // 'google-signin' becomes 'apple-signin' on iOS + { + passkeys: { challenge: 'base64-challenge', rpId: 'yourdomain.com' }, + googleSignIn: { serverClientId: 'your-client-id' }, + } +); + +// Platform-specific sign-up methods +if (Platform.OS === 'ios') { + // Direct Apple Sign In (iOS only) - uses AuthenticationServices + const appleCredential = await signUpWithApple({ + requestedScopes: ['fullName', 'email'], + }); +} else { + // Google Sign In (Android only) + const googleCredential = await signUpWithGoogle({ + serverClientId: 'your-client-id', + }); +} + +// Cross-platform sign-up (automatically uses the appropriate method) +const credential = await signUpWithGoogle({ + serverClientId: 'your-client-id', // Used on Android, ignored on iOS +}); +// Returns GoogleCredential on Android, AppleCredential on iOS + +// Handle different credential types +if (credential.type === 'passkey') { + console.log('Passkey authentication:', credential.authenticationResponseJson); +} else if (credential.type === 'password') { + console.log('AutoFill Password:', credential.username, credential.password); +} else if (credential.type === 'google-signin') { + console.log('Google Sign In:', credential.idToken); +} else if (credential.type === 'apple-signin') { + console.log('Apple Sign In:', credential.idToken, credential.email); +} +``` + +## Cross-Platform Compatibility + +The library provides excellent cross-platform compatibility while respecting platform limitations: + +- **`signUpWithGoogle()`**: Cross-platform function + - Android: Uses Google Sign In + - iOS: Automatically uses Apple Sign In (AuthenticationServices) +- **`signUpWithApple()`**: iOS-specific function + - iOS: Uses Apple Sign In (AuthenticationServices) + - Android: Rejects with clear error message +- **`signUpWithPassword()`**: Platform-specific behavior + - Android: Uses Credential Manager API + - iOS: Rejects (manual storage not supported by AuthenticationServices) +- **AutoFill Passwords**: Available on both platforms through `signIn` method +- **Error handling**: Platform-specific errors are handled gracefully + +## iOS AuthenticationServices Integration + +The iOS implementation strictly follows Apple's Authentication Services framework: + +- **Passkeys**: `ASAuthorizationPlatformPublicKeyCredentialProvider` +- **AutoFill Passwords**: `ASAuthorizationPasswordProvider` +- **Apple Sign In**: `ASAuthorizationAppleIDProvider` +- **No Custom Keychain**: Manual credential storage is handled by the system + +This ensures compliance with Apple's security guidelines and provides the best user experience on iOS. diff --git a/android/build.gradle b/android/build.gradle index ba723fb..d489e47 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -100,11 +100,12 @@ def kotlin_version = getExtOrDefault("kotlinVersion") dependencies { implementation "com.facebook.react:react-android" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "androidx.credentials:credentials:1.5.0-rc01" - implementation "androidx.credentials:credentials-play-services-auth:1.5.0-rc01" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4" - implementation "com.google.android.libraries.identity.googleid:googleid:1.1.1" + implementation("androidx.credentials:credentials:1.6.0-alpha02") + + implementation("androidx.credentials:credentials-play-services-auth:1.6.0-alpha02") + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4" + implementation "com.google.android.libraries.identity.googleid:googleid:1.1.1" } if (isNewArchitectureEnabled()) { diff --git a/android/src/main/java/com/credentialsmanager/handlers/CredentialHandler.kt b/android/src/main/java/com/credentialsmanager/handlers/CredentialHandler.kt index 7340f6d..ba2f815 100644 --- a/android/src/main/java/com/credentialsmanager/handlers/CredentialHandler.kt +++ b/android/src/main/java/com/credentialsmanager/handlers/CredentialHandler.kt @@ -36,26 +36,33 @@ class CredentialHandler( jsonString: String, preferImmediatelyAvailableCredentials: Boolean, ): ReadableMap? { - val request = - CreatePublicKeyCredentialRequest( - requestJson = jsonString, - preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials, - ) - - val response = - credentialManager.createCredential( - context, - request, - ) as CreatePublicKeyCredentialResponse - - return response.data.getString("androidx.credentials.BUNDLE_KEY_REGISTRATION_RESPONSE_JSON")?.let { json -> - val jsonObject = Arguments.createMap() - val parsedObject = JSONObject(json) - - parsedObject.keys().forEach { key -> - jsonObject.putString(key, parsedObject.getString(key)) + Log.d("CredentialManager", "Creating passkey with request: $jsonString") + + try { + val request = + CreatePublicKeyCredentialRequest( + requestJson = jsonString, + preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials, + ) + + val response = + credentialManager.createCredential( + context, + request, + ) as CreatePublicKeyCredentialResponse + + return response.data.getString("androidx.credentials.BUNDLE_KEY_REGISTRATION_RESPONSE_JSON")?.let { json -> + val jsonObject = Arguments.createMap() + val parsedObject = JSONObject(json) + + parsedObject.keys().forEach { key -> + jsonObject.putString(key, parsedObject.getString(key)) + } + jsonObject } - jsonObject + } catch (e: Exception) { + Log.e("CredentialManager", "Error creating passkey", e) + throw e } } @@ -63,8 +70,15 @@ class CredentialHandler( username: String, password: String, ) { - val createPasswordRequest = CreatePasswordRequest(id = username, password = password) - credentialManager.createCredential(context, createPasswordRequest) + Log.d("CredentialManager", "Creating password credential for username: $username") + + try { + val createPasswordRequest = CreatePasswordRequest(id = username, password = password) + credentialManager.createCredential(context, createPasswordRequest) + } catch (e: Exception) { + Log.e("CredentialManager", "Error creating password credential", e) + throw e + } } suspend fun signIn( diff --git a/android/src/newarch/java/com/credentialsmanager/CredentialsManagerModule.kt b/android/src/newarch/java/com/credentialsmanager/CredentialsManagerModule.kt index 2f133e1..7ea1bf7 100644 --- a/android/src/newarch/java/com/credentialsmanager/CredentialsManagerModule.kt +++ b/android/src/newarch/java/com/credentialsmanager/CredentialsManagerModule.kt @@ -49,14 +49,34 @@ class CredentialsManagerModule( } } - override fun signUpWithPassword(credObject: ReadableMap) { + override fun signUpWithPassword(credObject: ReadableMap, promise: Promise) { val username = credObject.getString("username") ?: "" val password = credObject.getString("password") ?: "" + + if (username.isEmpty()) { + promise.reject("INVALID_USERNAME", "Username cannot be empty") + return + } + + if (password.isEmpty()) { + promise.reject("INVALID_PASSWORD", "Password cannot be empty") + return + } + coroutineScope.launch { try { credentialHandler.createPassword(username, password) + + // Create success response + val result = mapOf( + "type" to "password", + "username" to username, + "success" to true + ) + promise.resolve(result) } catch (e: CreateCredentialException) { ErrorHandler.handleCredentialError(e) + promise.reject("CREDENTIAL_ERROR", e.message.toString()) } } } @@ -140,4 +160,11 @@ class CredentialsManagerModule( } } } + + override fun signUpWithApple(params: ReadableMap, promise: Promise) { + promise.reject( + "PLATFORM_NOT_SUPPORTED", + "Sign up with Apple is only supported on iOS devices" + ) + } } diff --git a/android/src/oldarch/java/com/credentialsmanager/CredentialsManagerModule.kt b/android/src/oldarch/java/com/credentialsmanager/CredentialsManagerModule.kt index 2ad676f..a7ac8f5 100644 --- a/android/src/oldarch/java/com/credentialsmanager/CredentialsManagerModule.kt +++ b/android/src/oldarch/java/com/credentialsmanager/CredentialsManagerModule.kt @@ -55,14 +55,34 @@ class CredentialsManagerModule( } @ReactMethod - fun signUpWithPassword(credObject: ReadableMap) { + fun signUpWithPassword(credObject: ReadableMap, promise: Promise) { val username = credObject.getString("username") ?: "" val password = credObject.getString("password") ?: "" + + if (username.isEmpty()) { + promise.reject("INVALID_USERNAME", "Username cannot be empty") + return + } + + if (password.isEmpty()) { + promise.reject("INVALID_PASSWORD", "Password cannot be empty") + return + } + coroutineScope.launch { try { credentialHandler.createPassword(username, password) + + // Create success response + val result = mapOf( + "type" to "password", + "username" to username, + "success" to true + ) + promise.resolve(result) } catch (e: CreateCredentialException) { ErrorHandler.handleCredentialError(e) + promise.reject("CREDENTIAL_ERROR", e.message.toString()) } } } @@ -161,4 +181,13 @@ class CredentialsManagerModule( } } } + + @ReactMethod + fun signUpWithApple(params: ReadableMap, promise: Promise) { + // Since this is an iOS-specific function, we just reject with an appropriate message on Android + promise.reject( + "PLATFORM_NOT_SUPPORTED", + "Sign up with Apple is only supported on iOS devices" + ) + } } diff --git a/example/ios/.xcode.env.local b/example/ios/.xcode.env.local index 9726762..cc1ba63 100644 --- a/example/ios/.xcode.env.local +++ b/example/ios/.xcode.env.local @@ -1 +1 @@ -export NODE_BINARY=/Users/mac/.nvm/versions/node/v20.14.0/bin/node +export NODE_BINARY=/opt/homebrew/Cellar/node/23.11.0/bin/node diff --git a/example/ios/CredentialsManagerExample.xcodeproj/project.pbxproj b/example/ios/CredentialsManagerExample.xcodeproj/project.pbxproj index 84e87b2..429eb0a 100644 --- a/example/ios/CredentialsManagerExample.xcodeproj/project.pbxproj +++ b/example/ios/CredentialsManagerExample.xcodeproj/project.pbxproj @@ -24,6 +24,7 @@ 5DCACB8F33CDC322A6C60F78 /* libPods-CredentialsManagerExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CredentialsManagerExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = CredentialsManagerExample/AppDelegate.swift; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = CredentialsManagerExample/LaunchScreen.storyboard; sourceTree = ""; }; + B546FE0F2DE77843007A8E3F /* CredentialsManagerExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = CredentialsManagerExample.entitlements; path = CredentialsManagerExample/CredentialsManagerExample.entitlements; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -42,6 +43,7 @@ 13B07FAE1A68108700A75B9A /* CredentialsManagerExample */ = { isa = PBXGroup; children = ( + B546FE0F2DE77843007A8E3F /* CredentialsManagerExample.entitlements */, 13B07FB51A68108700A75B9A /* Images.xcassets */, 761780EC2CA45674006654EE /* AppDelegate.swift */, 13B07FB61A68108700A75B9A /* Info.plist */, @@ -259,7 +261,9 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = CredentialsManagerExample/CredentialsManagerExample.entitlements; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = UDVM22XGUG; ENABLE_BITCODE = NO; INFOPLIST_FILE = CredentialsManagerExample/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; @@ -273,7 +277,7 @@ "-ObjC", "-lc++", ); - PRODUCT_BUNDLE_IDENTIFIER = credentialsmanager.example; + PRODUCT_BUNDLE_IDENTIFIER = com.jobpro.id; PRODUCT_NAME = CredentialsManagerExample; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -287,7 +291,9 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = CredentialsManagerExample/CredentialsManagerExample.entitlements; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = UDVM22XGUG; INFOPLIST_FILE = CredentialsManagerExample/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; LD_RUNPATH_SEARCH_PATHS = ( @@ -300,7 +306,7 @@ "-ObjC", "-lc++", ); - PRODUCT_BUNDLE_IDENTIFIER = credentialsmanager.example; + PRODUCT_BUNDLE_IDENTIFIER = com.jobpro.id; PRODUCT_NAME = CredentialsManagerExample; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; @@ -376,10 +382,7 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = ( - "$(inherited)", - " ", - ); + OTHER_LDFLAGS = "$(inherited) "; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; @@ -448,10 +451,7 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = ( - "$(inherited)", - " ", - ); + OTHER_LDFLAGS = "$(inherited) "; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; diff --git a/example/ios/CredentialsManagerExample/CredentialsManagerExample.entitlements b/example/ios/CredentialsManagerExample/CredentialsManagerExample.entitlements new file mode 100644 index 0000000..b0cfae8 --- /dev/null +++ b/example/ios/CredentialsManagerExample/CredentialsManagerExample.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.developer.applesignin + + Default + + com.apple.developer.associated-domains + + webcredentials:www.benjamineruvieru.com + + + diff --git a/example/ios/CredentialsManagerExample/Info.plist b/example/ios/CredentialsManagerExample/Info.plist index a16852a..ae2ceda 100644 --- a/example/ios/CredentialsManagerExample/Info.plist +++ b/example/ios/CredentialsManagerExample/Info.plist @@ -26,7 +26,6 @@ NSAppTransportSecurity - NSAllowsArbitraryLoads NSAllowsLocalNetworking diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 9d0588e..31a80bd 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1210,6 +1210,27 @@ PODS: - React-jsiexecutor - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core + - react-native-credentials-manager (0.5.3): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - React-nativeconfig (0.77.0) - React-NativeModulesApple (0.77.0): - glog @@ -1541,6 +1562,7 @@ DEPENDENCIES: - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) - React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) + - react-native-credentials-manager (from `../..`) - React-nativeconfig (from `../node_modules/react-native/ReactCommon`) - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) @@ -1650,6 +1672,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon" React-microtasksnativemodule: :path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks" + react-native-credentials-manager: + :path: "../.." React-nativeconfig: :path: "../node_modules/react-native/ReactCommon" React-NativeModulesApple: @@ -1719,7 +1743,7 @@ SPEC CHECKSUMS: fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: eb93e2f488219332457c3c4eafd2738ddc7e80b8 hermes-engine: 1f783c3d53940aed0d2c84586f0b7a85ab7827ef - RCT-Folly: e78785aa9ba2ed998ea4151e314036f6c49e6d82 + RCT-Folly: 36fe2295e44b10d831836cc0d1daec5f8abcf809 RCTDeprecation: f5c19ebdb8804b53ed029123eb69914356192fc8 RCTRequired: 6ae6cebe470486e0e0ce89c1c0eabb998e7c51f4 RCTTypeSafety: 50d6ec72a3d13cf77e041ff43a0617050fb98e3f @@ -1748,6 +1772,7 @@ SPEC CHECKSUMS: React-logger: 9a0c4e1e41cd640ac49d69aacadab783f7e0096b React-Mapbuffer: 6993c785c22a170c02489bc78ed207814cbd700f React-microtasksnativemodule: 19230cd0933df6f6dc1336c9a9edc382d62638ae + react-native-credentials-manager: df7d44c3d21d83d5a67bddaf071119361da333f7 React-nativeconfig: cd0fbb40987a9658c24dab5812c14e5522a64929 React-NativeModulesApple: 45187d13c68d47250a7416b18ff082c7cc07bff7 React-perflogger: 15a7bcb6c46eae8a981f7add8c9f4172e2372324 @@ -1778,8 +1803,8 @@ SPEC CHECKSUMS: ReactCodegen: 1baa534318b19e95fb0f02db0a1ae1e3c271944d ReactCommon: 6014af4276bb2debc350e2620ef1bd856b4d981c SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: c0d8564af14a858f962607cd7306539cb2ace926 + Yoga: 78d74e245ed67bb94275a1316cdc170b9b7fe884 PODFILE CHECKSUM: 04ab7ab32404c56cc432a0131cd18e84b514cd04 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/example/src/App.tsx b/example/src/App.tsx index 0dfcda0..8a4d298 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,51 +1,18 @@ -import { View, StyleSheet, Button } from 'react-native'; +import { View, StyleSheet, Button, Platform } from 'react-native'; import { signUpWithPasskeys, signUpWithPassword, signUpWithGoogle, + signUpWithApple, signOut, signIn, } from 'react-native-credentials-manager'; -const WEB_CLIENT_ID = process.env.WEB_CLIENT_ID || ''; - -const requestJson = { - challenge: 'c29tZS1yYW5kb20tY2hhbGxlbmdl', - rp: { - name: 'CredentialsManagerExample', - id: 'www.benjamineruvieru.com', - }, - user: { - id: 'dXNlcl9pZF8xMjM0NTY=', - name: 'johndoe', - displayName: 'John Doe', - }, - pubKeyCredParams: [ - { - type: 'public-key', - alg: -7, - }, - { - type: 'public-key', - alg: -257, - }, - ], - timeout: 1800000, - attestation: 'none', - excludeCredentials: [], - authenticatorSelection: { - authenticatorAttachment: 'platform', - requireResidentKey: true, - residentKey: 'required', - userVerification: 'required', - }, -}; +import { + generateTestRegistrationRequest, + generateTestAuthenticationRequest, +} from './helpers/passkeyTestHelper'; -const signinPasskeysRequestJson = { - challenge: 'HjBbH__fbLuzy95AGR31yEARA0EMtKlY0NrV5oy3NQw', - timeout: 1800000, - userVerification: 'required', - rpId: 'www.benjamineruvieru.com', -}; +const WEB_CLIENT_ID = process.env.WEB_CLIENT_ID || ''; export default function App() { return ( @@ -54,7 +21,9 @@ export default function App() { title="Signup With Passkey" onPress={async () => { try { - const res = await signUpWithPasskeys(requestJson); + // Use the helper to generate a valid registration request + const validRequest = generateTestRegistrationRequest(); + const res = await signUpWithPasskeys(validRequest); console.log(JSON.stringify(res)); console.log(res); } catch (e) { @@ -62,24 +31,40 @@ export default function App() { } }} /> -