react-native-credentials-manager 0.5.3 → 0.6.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 (36) hide show
  1. package/README.md +192 -1
  2. package/android/build.gradle +6 -5
  3. package/android/generated/java/com/credentialsmanager/NativeCredentialsManagerSpec.java +5 -1
  4. package/android/generated/jni/RNCredentialsManagerSpec-generated.cpp +7 -1
  5. package/android/generated/jni/react/renderer/components/RNCredentialsManagerSpec/RNCredentialsManagerSpecJSI-generated.cpp +8 -2
  6. package/android/generated/jni/react/renderer/components/RNCredentialsManagerSpec/RNCredentialsManagerSpecJSI.h +151 -3
  7. package/android/src/main/java/com/credentialsmanager/handlers/CredentialHandler.kt +59 -21
  8. package/android/src/newarch/java/com/credentialsmanager/CredentialsManagerModule.kt +28 -1
  9. package/android/src/oldarch/java/com/credentialsmanager/CredentialsManagerModule.kt +30 -1
  10. package/ios/CredentialsManager.h +8 -2
  11. package/ios/CredentialsManager.mm +396 -4
  12. package/ios/generated/RNCredentialsManagerSpec/RNCredentialsManagerSpec-generated.mm +14 -1
  13. package/ios/generated/RNCredentialsManagerSpec/RNCredentialsManagerSpec.h +38 -1
  14. package/ios/generated/RNCredentialsManagerSpecJSI-generated.cpp +8 -2
  15. package/ios/generated/RNCredentialsManagerSpecJSI.h +151 -3
  16. package/lib/commonjs/NativeCredentialsManager.js +6 -0
  17. package/lib/commonjs/NativeCredentialsManager.js.map +1 -1
  18. package/lib/commonjs/index.js +45 -3
  19. package/lib/commonjs/index.js.map +1 -1
  20. package/lib/module/NativeCredentialsManager.js +13 -0
  21. package/lib/module/NativeCredentialsManager.js.map +1 -1
  22. package/lib/module/index.js +44 -3
  23. package/lib/module/index.js.map +1 -1
  24. package/lib/typescript/commonjs/src/NativeCredentialsManager.d.ts +49 -7
  25. package/lib/typescript/commonjs/src/NativeCredentialsManager.d.ts.map +1 -1
  26. package/lib/typescript/commonjs/src/index.d.ts +10 -4
  27. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  28. package/lib/typescript/module/src/NativeCredentialsManager.d.ts +49 -7
  29. package/lib/typescript/module/src/NativeCredentialsManager.d.ts.map +1 -1
  30. package/lib/typescript/module/src/index.d.ts +10 -4
  31. package/lib/typescript/module/src/index.d.ts.map +1 -1
  32. package/package.json +1 -1
  33. package/react-native-credentials-manager.podspec +4 -1
  34. package/react-native.config.js +0 -1
  35. package/src/NativeCredentialsManager.ts +67 -8
  36. package/src/index.tsx +81 -5
@@ -49,14 +49,34 @@ class CredentialsManagerModule(
49
49
  }
50
50
  }
51
51
 
52
- override fun signUpWithPassword(credObject: ReadableMap) {
52
+ override fun signUpWithPassword(credObject: ReadableMap, promise: Promise) {
53
53
  val username = credObject.getString("username") ?: ""
54
54
  val password = credObject.getString("password") ?: ""
55
+
56
+ if (username.isEmpty()) {
57
+ promise.reject("INVALID_USERNAME", "Username cannot be empty")
58
+ return
59
+ }
60
+
61
+ if (password.isEmpty()) {
62
+ promise.reject("INVALID_PASSWORD", "Password cannot be empty")
63
+ return
64
+ }
65
+
55
66
  coroutineScope.launch {
56
67
  try {
57
68
  credentialHandler.createPassword(username, password)
69
+
70
+ // Create success response
71
+ val result = mapOf(
72
+ "type" to "password",
73
+ "username" to username,
74
+ "success" to true
75
+ )
76
+ promise.resolve(result)
58
77
  } catch (e: CreateCredentialException) {
59
78
  ErrorHandler.handleCredentialError(e)
79
+ promise.reject("CREDENTIAL_ERROR", e.message.toString())
60
80
  }
61
81
  }
62
82
  }
@@ -140,4 +160,11 @@ class CredentialsManagerModule(
140
160
  }
141
161
  }
142
162
  }
163
+
164
+ override fun signUpWithApple(params: ReadableMap, promise: Promise) {
165
+ promise.reject(
166
+ "PLATFORM_NOT_SUPPORTED",
167
+ "Sign up with Apple is only supported on iOS devices"
168
+ )
169
+ }
143
170
  }
@@ -55,14 +55,34 @@ class CredentialsManagerModule(
55
55
  }
56
56
 
57
57
  @ReactMethod
58
- fun signUpWithPassword(credObject: ReadableMap) {
58
+ fun signUpWithPassword(credObject: ReadableMap, promise: Promise) {
59
59
  val username = credObject.getString("username") ?: ""
60
60
  val password = credObject.getString("password") ?: ""
61
+
62
+ if (username.isEmpty()) {
63
+ promise.reject("INVALID_USERNAME", "Username cannot be empty")
64
+ return
65
+ }
66
+
67
+ if (password.isEmpty()) {
68
+ promise.reject("INVALID_PASSWORD", "Password cannot be empty")
69
+ return
70
+ }
71
+
61
72
  coroutineScope.launch {
62
73
  try {
63
74
  credentialHandler.createPassword(username, password)
75
+
76
+ // Create success response
77
+ val result = mapOf(
78
+ "type" to "password",
79
+ "username" to username,
80
+ "success" to true
81
+ )
82
+ promise.resolve(result)
64
83
  } catch (e: CreateCredentialException) {
65
84
  ErrorHandler.handleCredentialError(e)
85
+ promise.reject("CREDENTIAL_ERROR", e.message.toString())
66
86
  }
67
87
  }
68
88
  }
@@ -161,4 +181,13 @@ class CredentialsManagerModule(
161
181
  }
162
182
  }
163
183
  }
184
+
185
+ @ReactMethod
186
+ fun signUpWithApple(params: ReadableMap, promise: Promise) {
187
+ // Since this is an iOS-specific function, we just reject with an appropriate message on Android
188
+ promise.reject(
189
+ "PLATFORM_NOT_SUPPORTED",
190
+ "Sign up with Apple is only supported on iOS devices"
191
+ )
192
+ }
164
193
  }
@@ -1,6 +1,12 @@
1
-
1
+ #import <Foundation/Foundation.h>
2
+ #import <AuthenticationServices/AuthenticationServices.h>
2
3
  #import "generated/RNCredentialsManagerSpec/RNCredentialsManagerSpec.h"
3
4
 
4
- @interface CredentialsManager : NSObject <NativeCredentialsManagerSpec>
5
+ @interface CredentialsManager : NSObject <NativeCredentialsManagerSpec, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding>
6
+
7
+ @property (nonatomic, strong) ASPresentationAnchor authenticationAnchor;
8
+ @property (nonatomic, copy) RCTPromiseResolveBlock currentResolve;
9
+ @property (nonatomic, copy) RCTPromiseRejectBlock currentReject;
10
+ @property (nonatomic, strong) NSString *relyingPartyIdentifier;
5
11
 
6
12
  @end
@@ -1,18 +1,410 @@
1
1
  #import "CredentialsManager.h"
2
+ #import <React/RCTUtils.h>
3
+ #import <React/RCTLog.h>
4
+ #import <AuthenticationServices/AuthenticationServices.h>
2
5
 
3
6
  @implementation CredentialsManager
4
- RCT_EXPORT_MODULE()
5
7
 
6
- - (NSNumber *)multiply:(double)a b:(double)b {
7
- NSNumber *result = @(a * b);
8
+ RCT_EXPORT_MODULE()
8
9
 
9
- return result;
10
+ - (instancetype)init {
11
+ self = [super init];
12
+ if (self) {
13
+ // Default relying party identifier - should be configurable
14
+ self.relyingPartyIdentifier = @"www.benjamineruvieru.com";
15
+
16
+ // Get the main window as the authentication anchor
17
+ dispatch_async(dispatch_get_main_queue(), ^{
18
+ UIWindow *keyWindow = nil;
19
+ for (UIWindowScene *windowScene in [UIApplication sharedApplication].connectedScenes) {
20
+ if (windowScene.activationState == UISceneActivationStateForegroundActive) {
21
+ for (UIWindow *window in windowScene.windows) {
22
+ if (window.isKeyWindow) {
23
+ keyWindow = window;
24
+ break;
25
+ }
26
+ }
27
+ }
28
+ }
29
+ self.authenticationAnchor = keyWindow;
30
+ });
31
+ }
32
+ return self;
10
33
  }
11
34
 
35
+ #pragma mark - TurboModule
36
+
12
37
  - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
13
38
  (const facebook::react::ObjCTurboModule::InitParams &)params
14
39
  {
15
40
  return std::make_shared<facebook::react::NativeCredentialsManagerSpecJSI>(params);
16
41
  }
17
42
 
43
+ #pragma mark - NativeCredentialsManagerSpec
44
+
45
+ - (void)signUpWithPasskeys:(NSDictionary *)requestJson
46
+ preferImmediatelyAvailableCredentials:(BOOL)preferImmediatelyAvailableCredentials
47
+ resolve:(RCTPromiseResolveBlock)resolve
48
+ reject:(RCTPromiseRejectBlock)reject {
49
+
50
+ dispatch_async(dispatch_get_main_queue(), ^{
51
+ self.currentResolve = resolve;
52
+ self.currentReject = reject;
53
+
54
+ // Extract challenge and user info from requestJson
55
+ NSString *challengeString = requestJson[@"challenge"];
56
+ NSDictionary *userInfo = requestJson[@"user"];
57
+ NSDictionary *rpInfo = requestJson[@"rp"];
58
+
59
+ if (!challengeString || !userInfo || !rpInfo) {
60
+ reject(@"INVALID_REQUEST", @"Missing required fields in request JSON", nil);
61
+ return;
62
+ }
63
+
64
+ // Decode base64 challenge
65
+ NSData *challenge = [[NSData alloc] initWithBase64EncodedString:challengeString options:0];
66
+ if (!challenge) {
67
+ reject(@"INVALID_CHALLENGE", @"Invalid base64 challenge", nil);
68
+ return;
69
+ }
70
+
71
+ // Extract user information
72
+ NSString *userName = userInfo[@"name"];
73
+ NSString *userIdString = userInfo[@"id"];
74
+
75
+ // Decode user ID
76
+ NSData *userId = [[NSData alloc] initWithBase64EncodedString:userIdString options:0];
77
+ if (!userId) {
78
+ // If not base64, use the string directly
79
+ userId = [userIdString dataUsingEncoding:NSUTF8StringEncoding];
80
+ }
81
+
82
+ // Update relying party identifier
83
+ NSString *rpId = rpInfo[@"id"];
84
+ if (rpId) {
85
+ self.relyingPartyIdentifier = rpId;
86
+ }
87
+
88
+ // Create the passkey registration request using Apple's Authentication Services
89
+ ASAuthorizationPlatformPublicKeyCredentialProvider *provider =
90
+ [[ASAuthorizationPlatformPublicKeyCredentialProvider alloc] initWithRelyingPartyIdentifier:self.relyingPartyIdentifier];
91
+
92
+ ASAuthorizationPlatformPublicKeyCredentialRegistrationRequest *registrationRequest =
93
+ [provider createCredentialRegistrationRequestWithChallenge:challenge name:userName userID:userId];
94
+
95
+ // Configure the request
96
+ registrationRequest.userVerificationPreference = ASAuthorizationPublicKeyCredentialUserVerificationPreferenceRequired;
97
+
98
+ // Create and configure the authorization controller
99
+ ASAuthorizationController *authController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[registrationRequest]];
100
+ authController.delegate = self;
101
+ authController.presentationContextProvider = self;
102
+
103
+ // Perform the request
104
+ [authController performRequests];
105
+ });
106
+ }
107
+
108
+ - (void)signUpWithPassword:(JS::NativeCredentialsManager::CredObject &)credObject
109
+ resolve:(RCTPromiseResolveBlock)resolve
110
+ reject:(RCTPromiseRejectBlock)reject {
111
+ // Apple's Authentication Services only supports AutoFill passwords, not manual credential storage
112
+ // Manual keychain storage is not part of Authentication Services framework
113
+ reject(@"UNSUPPORTED_OPERATION", @"Manual password storage is not supported. Use AutoFill passwords through signIn method instead.", nil);
114
+ }
115
+
116
+ - (void)signIn:(NSArray *)options
117
+ params:(JS::NativeCredentialsManager::SpecSignInParams &)params
118
+ resolve:(RCTPromiseResolveBlock)resolve
119
+ reject:(RCTPromiseRejectBlock)reject {
120
+
121
+ dispatch_async(dispatch_get_main_queue(), ^{
122
+ self.currentResolve = resolve;
123
+ self.currentReject = reject;
124
+
125
+ NSMutableArray<ASAuthorizationRequest *> *authRequests = [[NSMutableArray alloc] init];
126
+
127
+ for (NSString *option in options) {
128
+ if ([option isEqualToString:@"passkeys"]) {
129
+ // Extract passkeys parameters from the params object
130
+ id passkeyParams = params.passkeys();
131
+ if (!passkeyParams || ![passkeyParams isKindOfClass:[NSDictionary class]]) {
132
+ RCTLogError(@"Missing or invalid passkeys parameters");
133
+ continue;
134
+ }
135
+
136
+ NSDictionary *passkeyDict = (NSDictionary *)passkeyParams;
137
+ NSString *challengeString = passkeyDict[@"challenge"];
138
+
139
+ if (!challengeString || ![challengeString isKindOfClass:[NSString class]]) {
140
+ RCTLogError(@"Missing or invalid challenge in passkeys parameters");
141
+ continue;
142
+ }
143
+
144
+ NSData *challenge = [[NSData alloc] initWithBase64EncodedString:challengeString options:0];
145
+
146
+ if (challenge) {
147
+ // Update relying party identifier if provided
148
+ NSString *rpId = passkeyDict[@"rpId"];
149
+ if (rpId && [rpId isKindOfClass:[NSString class]]) {
150
+ self.relyingPartyIdentifier = rpId;
151
+ }
152
+
153
+ ASAuthorizationPlatformPublicKeyCredentialProvider *provider =
154
+ [[ASAuthorizationPlatformPublicKeyCredentialProvider alloc] initWithRelyingPartyIdentifier:self.relyingPartyIdentifier];
155
+
156
+ ASAuthorizationPlatformPublicKeyCredentialAssertionRequest *assertionRequest =
157
+ [provider createCredentialAssertionRequestWithChallenge:challenge];
158
+ assertionRequest.userVerificationPreference = ASAuthorizationPublicKeyCredentialUserVerificationPreferenceRequired;
159
+
160
+ [authRequests addObject:assertionRequest];
161
+ } else {
162
+ RCTLogError(@"Failed to decode challenge for passkey authentication");
163
+ }
164
+ } else if ([option isEqualToString:@"password"]) {
165
+ // Use Apple's AutoFill password provider (not manual keychain)
166
+ ASAuthorizationPasswordProvider *passwordProvider = [[ASAuthorizationPasswordProvider alloc] init];
167
+ ASAuthorizationPasswordRequest *passwordRequest = [passwordProvider createRequest];
168
+ [authRequests addObject:passwordRequest];
169
+ } else if ([option isEqualToString:@"apple-signin"]) {
170
+ // Apple Sign In is supported by Authentication Services
171
+ ASAuthorizationAppleIDProvider *appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init];
172
+ ASAuthorizationAppleIDRequest *appleIDRequest = [appleIDProvider createRequest];
173
+
174
+ // Default scopes - always use these
175
+ NSArray<ASAuthorizationScope> *defaultScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
176
+ appleIDRequest.requestedScopes = defaultScopes;
177
+
178
+ [authRequests addObject:appleIDRequest];
179
+ } else if ([option isEqualToString:@"google-signin"]) {
180
+ // Google Sign In is not part of Apple's Authentication Services framework
181
+ RCTLogError(@"Google Sign In is not supported on iOS. Use Apple Sign In instead.");
182
+ continue;
183
+ }
184
+ }
185
+
186
+ if (authRequests.count == 0) {
187
+ reject(@"NO_AUTH_METHODS", @"No valid authentication methods provided", nil);
188
+ return;
189
+ }
190
+
191
+ // Create and configure the authorization controller
192
+ ASAuthorizationController *authController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:authRequests];
193
+ authController.delegate = self;
194
+ authController.presentationContextProvider = self;
195
+
196
+ // Perform the request
197
+ [authController performRequests];
198
+ });
199
+ }
200
+
201
+ - (void)signUpWithGoogle:(JS::NativeCredentialsManager::GoogleSignInParams &)params
202
+ resolve:(RCTPromiseResolveBlock)resolve
203
+ reject:(RCTPromiseRejectBlock)reject {
204
+ // Google Sign In is not part of Apple's Authentication Services framework
205
+ // This should be handled at the JavaScript layer for cross-platform compatibility
206
+ reject(@"UNSUPPORTED_OPERATION", @"Google Sign In is not available on iOS. Use Apple Sign In instead.", nil);
207
+ }
208
+
209
+ - (void)signUpWithApple:(JS::NativeCredentialsManager::AppleSignInParams &)params
210
+ resolve:(RCTPromiseResolveBlock)resolve
211
+ reject:(RCTPromiseRejectBlock)reject {
212
+
213
+ dispatch_async(dispatch_get_main_queue(), ^{
214
+ self.currentResolve = resolve;
215
+ self.currentReject = reject;
216
+
217
+ // Apple Sign In is officially supported by Authentication Services framework
218
+ ASAuthorizationAppleIDProvider *appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init];
219
+ ASAuthorizationAppleIDRequest *appleIDRequest = [appleIDProvider createRequest];
220
+
221
+ // Always use the default scopes
222
+ appleIDRequest.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
223
+
224
+ ASAuthorizationController *authController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[appleIDRequest]];
225
+ authController.delegate = self;
226
+ authController.presentationContextProvider = self;
227
+
228
+ [authController performRequests];
229
+ });
230
+ }
231
+
232
+ - (void)signOut:(RCTPromiseResolveBlock)resolve
233
+ reject:(RCTPromiseRejectBlock)reject {
234
+ // Apple's Authentication Services doesn't provide a direct sign-out method
235
+ // Sign-out is typically handled at the application level
236
+ // AutoFill passwords and passkeys are managed by the system
237
+ resolve([NSNull null]);
238
+ }
239
+
240
+ #pragma mark - ASAuthorizationControllerDelegate
241
+
242
+ - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization {
243
+ if (!self.currentResolve) {
244
+ return;
245
+ }
246
+
247
+ RCTPromiseResolveBlock resolve = self.currentResolve;
248
+ self.currentReject = nil;
249
+ self.currentResolve = nil;
250
+
251
+ if ([authorization.credential isKindOfClass:[ASAuthorizationPlatformPublicKeyCredentialRegistration class]]) {
252
+ // Passkey registration - handled by Apple's Authentication Services
253
+ ASAuthorizationPlatformPublicKeyCredentialRegistration *registration = (ASAuthorizationPlatformPublicKeyCredentialRegistration *)authorization.credential;
254
+
255
+ NSDictionary *result = @{
256
+ @"id": registration.credentialID ? [registration.credentialID base64EncodedStringWithOptions:0] : @"",
257
+ @"rawId": registration.credentialID ? [registration.credentialID base64EncodedStringWithOptions:0] : @"",
258
+ @"response": @{
259
+ @"attestationObject": registration.rawAttestationObject ? [registration.rawAttestationObject base64EncodedStringWithOptions:0] : @"",
260
+ @"clientDataJSON": registration.rawClientDataJSON ? [registration.rawClientDataJSON base64EncodedStringWithOptions:0] : @""
261
+ },
262
+ @"type": @"public-key"
263
+ };
264
+
265
+ resolve(result);
266
+ return;
267
+ } else if ([authorization.credential isKindOfClass:[ASAuthorizationPlatformPublicKeyCredentialAssertion class]]) {
268
+ // Passkey authentication - handled by Apple's Authentication Services
269
+ ASAuthorizationPlatformPublicKeyCredentialAssertion *assertion = (ASAuthorizationPlatformPublicKeyCredentialAssertion *)authorization.credential;
270
+
271
+ NSDictionary *result = @{
272
+ @"type": @"passkey",
273
+ @"authenticationResponseJson": [self createAuthenticationResponseJSON:assertion]
274
+ };
275
+
276
+ resolve(result);
277
+ return;
278
+ } else if ([authorization.credential isKindOfClass:[ASPasswordCredential class]]) {
279
+ // AutoFill password authentication - handled by Apple's Authentication Services
280
+ ASPasswordCredential *passwordCredential = (ASPasswordCredential *)authorization.credential;
281
+
282
+ NSDictionary *result = @{
283
+ @"type": @"password",
284
+ @"username": passwordCredential.user,
285
+ @"password": passwordCredential.password
286
+ };
287
+
288
+ resolve(result);
289
+ return;
290
+ } else if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) {
291
+ // Apple Sign In - officially supported by Authentication Services
292
+ ASAuthorizationAppleIDCredential *appleIDCredential = (ASAuthorizationAppleIDCredential *)authorization.credential;
293
+
294
+ NSMutableDictionary *result = [@{
295
+ @"type": @"apple-signin",
296
+ @"id": appleIDCredential.user,
297
+ @"idToken": appleIDCredential.identityToken ? [[NSString alloc] initWithData:appleIDCredential.identityToken encoding:NSUTF8StringEncoding] : @""
298
+ } mutableCopy];
299
+
300
+ if (appleIDCredential.fullName) {
301
+ if (appleIDCredential.fullName.givenName) {
302
+ result[@"givenName"] = appleIDCredential.fullName.givenName;
303
+ }
304
+ if (appleIDCredential.fullName.familyName) {
305
+ result[@"familyName"] = appleIDCredential.fullName.familyName;
306
+ }
307
+ if (appleIDCredential.fullName.givenName || appleIDCredential.fullName.familyName) {
308
+ NSString *fullName = [NSString stringWithFormat:@"%@ %@",
309
+ appleIDCredential.fullName.givenName ?: @"",
310
+ appleIDCredential.fullName.familyName ?: @""];
311
+ result[@"displayName"] = [fullName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
312
+ }
313
+ }
314
+
315
+ if (appleIDCredential.email) {
316
+ result[@"email"] = appleIDCredential.email;
317
+ }
318
+
319
+ resolve([result copy]);
320
+ return;
321
+ }
322
+
323
+ // If we reach here, we couldn't handle the credential type
324
+ resolve(@{@"type": @"unknown"});
325
+ }
326
+
327
+ - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error {
328
+ if (!self.currentReject) {
329
+ return;
330
+ }
331
+
332
+ RCTPromiseRejectBlock reject = self.currentReject;
333
+ self.currentResolve = nil;
334
+ self.currentReject = nil;
335
+
336
+ NSString *errorCode = @"UNKNOWN_ERROR";
337
+ NSString *errorMessage = error.localizedDescription;
338
+
339
+ if ([error.domain isEqualToString:ASAuthorizationErrorDomain]) {
340
+ switch (error.code) {
341
+ case ASAuthorizationErrorCanceled:
342
+ errorCode = @"USER_CANCELED";
343
+ errorMessage = @"User canceled the authorization request";
344
+ break;
345
+ case ASAuthorizationErrorFailed:
346
+ errorCode = @"AUTHORIZATION_FAILED";
347
+ break;
348
+ case ASAuthorizationErrorInvalidResponse:
349
+ errorCode = @"INVALID_RESPONSE";
350
+ break;
351
+ case ASAuthorizationErrorNotHandled:
352
+ errorCode = @"NOT_HANDLED";
353
+ break;
354
+ case ASAuthorizationErrorUnknown:
355
+ errorCode = @"UNKNOWN_ERROR";
356
+ break;
357
+ }
358
+ }
359
+
360
+ reject(errorCode, errorMessage, error);
361
+ }
362
+
363
+ #pragma mark - ASAuthorizationControllerPresentationContextProviding
364
+
365
+ - (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller {
366
+ if (self.authenticationAnchor) {
367
+ return self.authenticationAnchor;
368
+ }
369
+
370
+ // Fallback to finding a key window
371
+ UIWindow *keyWindow = nil;
372
+ for (UIWindowScene *windowScene in [UIApplication sharedApplication].connectedScenes) {
373
+ if (windowScene.activationState == UISceneActivationStateForegroundActive) {
374
+ for (UIWindow *window in windowScene.windows) {
375
+ if (window.isKeyWindow) {
376
+ keyWindow = window;
377
+ break;
378
+ }
379
+ }
380
+ }
381
+ }
382
+
383
+ return keyWindow ?: [[UIApplication sharedApplication] windows].firstObject;
384
+ }
385
+
386
+ #pragma mark - Helper Methods
387
+
388
+ - (NSString *)createAuthenticationResponseJSON:(ASAuthorizationPlatformPublicKeyCredentialAssertion *)assertion {
389
+ NSDictionary *response = @{
390
+ @"id": assertion.credentialID ? [assertion.credentialID base64EncodedStringWithOptions:0] : @"",
391
+ @"rawId": assertion.credentialID ? [assertion.credentialID base64EncodedStringWithOptions:0] : @"",
392
+ @"response": @{
393
+ @"authenticatorData": assertion.rawAuthenticatorData ? [assertion.rawAuthenticatorData base64EncodedStringWithOptions:0] : @"",
394
+ @"clientDataJSON": assertion.rawClientDataJSON ? [assertion.rawClientDataJSON base64EncodedStringWithOptions:0] : @"",
395
+ @"signature": assertion.signature ? [assertion.signature base64EncodedStringWithOptions:0] : @"",
396
+ @"userHandle": assertion.userID ? [assertion.userID base64EncodedStringWithOptions:0] : @""
397
+ },
398
+ @"type": @"public-key"
399
+ };
400
+
401
+ NSError *error;
402
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject:response options:0 error:&error];
403
+ if (error) {
404
+ return @"{}";
405
+ }
406
+
407
+ return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
408
+ }
409
+
18
410
  @end
@@ -35,6 +35,12 @@
35
35
  return facebook::react::managedPointer<JS::NativeCredentialsManager::GoogleSignInParams>(json);
36
36
  }
37
37
  @end
38
+ @implementation RCTCxxConvert (NativeCredentialsManager_AppleSignInParams)
39
+ + (RCTManagedPointer *)JS_NativeCredentialsManager_AppleSignInParams:(id)json
40
+ {
41
+ return facebook::react::managedPointer<JS::NativeCredentialsManager::AppleSignInParams>(json);
42
+ }
43
+ @end
38
44
  @implementation RCTCxxConvert (NativeCredentialsManager_SpecSignInParams)
39
45
  + (RCTManagedPointer *)JS_NativeCredentialsManager_SpecSignInParams:(id)json
40
46
  {
@@ -48,7 +54,7 @@ namespace facebook::react {
48
54
  }
49
55
 
50
56
  static facebook::jsi::Value __hostFunction_NativeCredentialsManagerSpecJSI_signUpWithPassword(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
51
- return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "signUpWithPassword", @selector(signUpWithPassword:), args, count);
57
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "signUpWithPassword", @selector(signUpWithPassword:resolve:reject:), args, count);
52
58
  }
53
59
 
54
60
  static facebook::jsi::Value __hostFunction_NativeCredentialsManagerSpecJSI_signIn(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
@@ -59,6 +65,10 @@ namespace facebook::react {
59
65
  return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "signUpWithGoogle", @selector(signUpWithGoogle:resolve:reject:), args, count);
60
66
  }
61
67
 
68
+ static facebook::jsi::Value __hostFunction_NativeCredentialsManagerSpecJSI_signUpWithApple(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
69
+ return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "signUpWithApple", @selector(signUpWithApple:resolve:reject:), args, count);
70
+ }
71
+
62
72
  static facebook::jsi::Value __hostFunction_NativeCredentialsManagerSpecJSI_signOut(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
63
73
  return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "signOut", @selector(signOut:reject:), args, count);
64
74
  }
@@ -78,6 +88,9 @@ namespace facebook::react {
78
88
  methodMap_["signUpWithGoogle"] = MethodMetadata {1, __hostFunction_NativeCredentialsManagerSpecJSI_signUpWithGoogle};
79
89
  setMethodArgConversionSelector(@"signUpWithGoogle", 0, @"JS_NativeCredentialsManager_GoogleSignInParams:");
80
90
 
91
+ methodMap_["signUpWithApple"] = MethodMetadata {1, __hostFunction_NativeCredentialsManagerSpecJSI_signUpWithApple};
92
+ setMethodArgConversionSelector(@"signUpWithApple", 0, @"JS_NativeCredentialsManager_AppleSignInParams:");
93
+
81
94
  methodMap_["signOut"] = MethodMetadata {0, __hostFunction_NativeCredentialsManagerSpecJSI_signOut};
82
95
 
83
96
  }
@@ -65,11 +65,28 @@ namespace JS {
65
65
  @interface RCTCxxConvert (NativeCredentialsManager_GoogleSignInParams)
66
66
  + (RCTManagedPointer *)JS_NativeCredentialsManager_GoogleSignInParams:(id)json;
67
67
  @end
68
+ namespace JS {
69
+ namespace NativeCredentialsManager {
70
+ struct AppleSignInParams {
71
+ NSString *nonce() const;
72
+ facebook::react::LazyVector<NSString *> requestedScopes() const;
73
+
74
+ AppleSignInParams(NSDictionary *const v) : _v(v) {}
75
+ private:
76
+ NSDictionary *_v;
77
+ };
78
+ }
79
+ }
80
+
81
+ @interface RCTCxxConvert (NativeCredentialsManager_AppleSignInParams)
82
+ + (RCTManagedPointer *)JS_NativeCredentialsManager_AppleSignInParams:(id)json;
83
+ @end
68
84
  namespace JS {
69
85
  namespace NativeCredentialsManager {
70
86
  struct SpecSignInParams {
71
87
  id<NSObject> _Nullable passkeys() const;
72
88
  std::optional<JS::NativeCredentialsManager::GoogleSignInParams> googleSignIn() const;
89
+ std::optional<JS::NativeCredentialsManager::AppleSignInParams> appleSignIn() const;
73
90
 
74
91
  SpecSignInParams(NSDictionary *const v) : _v(v) {}
75
92
  private:
@@ -87,7 +104,9 @@ namespace JS {
87
104
  preferImmediatelyAvailableCredentials:(BOOL)preferImmediatelyAvailableCredentials
88
105
  resolve:(RCTPromiseResolveBlock)resolve
89
106
  reject:(RCTPromiseRejectBlock)reject;
90
- - (void)signUpWithPassword:(JS::NativeCredentialsManager::CredObject &)credObject;
107
+ - (void)signUpWithPassword:(JS::NativeCredentialsManager::CredObject &)credObject
108
+ resolve:(RCTPromiseResolveBlock)resolve
109
+ reject:(RCTPromiseRejectBlock)reject;
91
110
  - (void)signIn:(NSArray *)options
92
111
  params:(JS::NativeCredentialsManager::SpecSignInParams &)params
93
112
  resolve:(RCTPromiseResolveBlock)resolve
@@ -95,6 +114,9 @@ preferImmediatelyAvailableCredentials:(BOOL)preferImmediatelyAvailableCredential
95
114
  - (void)signUpWithGoogle:(JS::NativeCredentialsManager::GoogleSignInParams &)params
96
115
  resolve:(RCTPromiseResolveBlock)resolve
97
116
  reject:(RCTPromiseRejectBlock)reject;
117
+ - (void)signUpWithApple:(JS::NativeCredentialsManager::AppleSignInParams &)params
118
+ resolve:(RCTPromiseResolveBlock)resolve
119
+ reject:(RCTPromiseRejectBlock)reject;
98
120
  - (void)signOut:(RCTPromiseResolveBlock)resolve
99
121
  reject:(RCTPromiseRejectBlock)reject;
100
122
 
@@ -143,6 +165,16 @@ inline bool JS::NativeCredentialsManager::GoogleSignInParams::autoSelectEnabled(
143
165
  id const p = _v[@"autoSelectEnabled"];
144
166
  return RCTBridgingToBool(p);
145
167
  }
168
+ inline NSString *JS::NativeCredentialsManager::AppleSignInParams::nonce() const
169
+ {
170
+ id const p = _v[@"nonce"];
171
+ return RCTBridgingToString(p);
172
+ }
173
+ inline facebook::react::LazyVector<NSString *> JS::NativeCredentialsManager::AppleSignInParams::requestedScopes() const
174
+ {
175
+ id const p = _v[@"requestedScopes"];
176
+ return RCTBridgingToVec(p, ^NSString *(id itemValue_0) { return RCTBridgingToString(itemValue_0); });
177
+ }
146
178
  inline id<NSObject> _Nullable JS::NativeCredentialsManager::SpecSignInParams::passkeys() const
147
179
  {
148
180
  id const p = _v[@"passkeys"];
@@ -153,5 +185,10 @@ inline std::optional<JS::NativeCredentialsManager::GoogleSignInParams> JS::Nativ
153
185
  id const p = _v[@"googleSignIn"];
154
186
  return (p == nil ? std::nullopt : std::make_optional(JS::NativeCredentialsManager::GoogleSignInParams(p)));
155
187
  }
188
+ inline std::optional<JS::NativeCredentialsManager::AppleSignInParams> JS::NativeCredentialsManager::SpecSignInParams::appleSignIn() const
189
+ {
190
+ id const p = _v[@"appleSignIn"];
191
+ return (p == nil ? std::nullopt : std::make_optional(JS::NativeCredentialsManager::AppleSignInParams(p)));
192
+ }
156
193
  NS_ASSUME_NONNULL_END
157
194
  #endif // RNCredentialsManagerSpec_H