Skip to content

Commit 933c8a6

Browse files
committed
feat(PSG-3836): users can register with passkey creation options
1 parent 1c0047c commit 933c8a6

File tree

5 files changed

+45
-9
lines changed

5 files changed

+45
-9
lines changed

android/src/main/java/com/passagereactnative/PassageReactNativeModule.kt

+11-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import android.os.Build
44
import com.facebook.react.bridge.ReactApplicationContext
55
import com.facebook.react.bridge.ReactContextBaseJavaModule
66
import com.facebook.react.bridge.ReactMethod
7+
import com.facebook.react.bridge.ReadableMap
78
import com.facebook.react.bridge.Promise
89
import kotlinx.coroutines.CoroutineScope
910
import kotlinx.coroutines.Dispatchers
@@ -12,11 +13,13 @@ import com.google.gson.Gson
1213
import id.passage.android.Passage
1314
import id.passage.android.PassageSocialConnection
1415
import id.passage.android.PassageToken
16+
import id.passage.android.PasskeyCreationOptions
1517
import id.passage.android.exceptions.AddDevicePasskeyCancellationException
1618
import id.passage.android.exceptions.LoginWithPasskeyCancellationException
1719
import id.passage.android.exceptions.OneTimePasscodeActivateExceededAttemptsException
1820
import id.passage.android.exceptions.PassageUserUnauthorizedException
1921
import id.passage.android.exceptions.RegisterWithPasskeyCancellationException
22+
import id.passage.android.model.AuthenticatorAttachment
2023

2124
@Suppress("unused")
2225
class PassageReactNativeModule(reactContext: ReactApplicationContext) :
@@ -45,10 +48,16 @@ class PassageReactNativeModule(reactContext: ReactApplicationContext) :
4548
// region PASSKEY METHODS
4649

4750
@ReactMethod
48-
fun registerWithPasskey(identifier: String, promise: Promise) {
51+
fun registerWithPasskey(identifier: String, optionsMap: ReadableMap?, promise: Promise) {
4952
CoroutineScope(Dispatchers.IO).launch {
5053
try {
51-
val authResult = passage.registerWithPasskey(identifier)
54+
var options: PasskeyCreationOptions? = null
55+
optionsMap?.getString("authenticatorAttachment")?.let {
56+
AuthenticatorAttachment.decode(it)?.let {
57+
options = PasskeyCreationOptions(it)
58+
}
59+
}
60+
val authResult = passage.registerWithPasskey(identifier, options)
5261
val jsonString = Gson().toJson(authResult)
5362
promise.resolve(jsonString)
5463
} catch (e: Exception) {

ios/PassageReactNative.m

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ @interface RCT_EXTERN_MODULE(PassageReactNative, NSObject)
88

99
// MARK: - Passkey Methods
1010
RCT_EXTERN_METHOD(registerWithPasskey:(NSString *)identifier
11+
withOptionsDictionary:( nullable NSDictionary *)optionsDictionary
1112
withResolver:(RCTPromiseResolveBlock)resolve
1213
withRejecter:(RCTPromiseRejectBlock)reject);
1314

ios/PassageReactNative.swift

+9-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ class PassageReactNative: NSObject {
2424

2525
// MARK: - Passkey Methods
2626

27-
@objc(registerWithPasskey:withResolver:withRejecter:)
27+
@objc(registerWithPasskey:withOptionsDictionary:withResolver:withRejecter:)
2828
func registerWithPasskey(
2929
identifier: String,
30+
optionsDictionary: NSDictionary?,
3031
resolve: @escaping RCTPromiseResolveBlock,
3132
reject: @escaping RCTPromiseRejectBlock
3233
) -> Void {
@@ -36,7 +37,13 @@ class PassageReactNative: NSObject {
3637
}
3738
Task {
3839
do {
39-
let authResult = try await passage.registerWithPasskey(identifier: identifier)
40+
var passkeyCreationOptions: PasskeyCreationOptions?
41+
if let authenticatorAttachmentString = optionsDictionary?["authenticatorAttachment"] as? String,
42+
let authenticatorAttachment = AuthenticatorAttachment(rawValue: authenticatorAttachmentString)
43+
{
44+
passkeyCreationOptions = PasskeyCreationOptions(authenticatorAttachment: authenticatorAttachment)
45+
}
46+
let authResult = try await passage.registerWithPasskey(identifier: identifier, options: passkeyCreationOptions)
4047
resolve(authResult.toJsonString())
4148
} catch PassageASAuthorizationError.canceled {
4249
reject("USER_CANCELLED", "User cancelled interaction", nil)

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@passageidentity/passage-react-native",
3-
"version": "0.5.2",
3+
"version": "0.6.0",
44
"description": "Native passkey authentication for your React Native app",
55
"main": "lib/commonjs/index",
66
"module": "lib/module/index",

src/index.tsx

+23-4
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,16 @@ export type EmailAndSMSAuthMethod = {
132132
ttl_display_unit: DisplayUnit;
133133
};
134134

135+
export enum AuthenticatorAttachment {
136+
Platform = 'platform',
137+
CrossPlatform = 'cross-platform',
138+
Any = 'any',
139+
}
140+
141+
export interface PasskeyCreationOptions {
142+
authenticatorAttachment?: AuthenticatorAttachment;
143+
}
144+
135145
export enum DisplayUnit {
136146
Seconds = 's',
137147
Minutes = 'm',
@@ -145,7 +155,10 @@ export enum SocialConnection {
145155
Google = 'google',
146156
}
147157

148-
type RegisterWithPasskey = (identifier: string) => Promise<AuthResult>;
158+
type RegisterWithPasskey = (
159+
identifier: string,
160+
options?: PasskeyCreationOptions
161+
) => Promise<AuthResult>;
149162
type LoginWithPasskey = (identifier?: string | null) => Promise<AuthResult>;
150163
type DeviceSupportsPasskeys = () => Promise<boolean>;
151164
type AuthWithoutPasskey = (identifier: string) => Promise<string>;
@@ -195,10 +208,14 @@ class Passage {
195208
* @throws {PassageError} When user cancels operation, user already exists, app configuration was not done properly, etc.
196209
*/
197210
registerWithPasskey: RegisterWithPasskey = async (
198-
identifier: string
211+
identifier: string,
212+
options?: PasskeyCreationOptions
199213
): Promise<AuthResult> => {
200214
try {
201-
const result = await PassageReactNative.registerWithPasskey(identifier);
215+
const result = await PassageReactNative.registerWithPasskey(
216+
identifier,
217+
options || null
218+
);
202219
const parsedResult = JSON.parse(result);
203220
return parsedResult;
204221
} catch (error: any) {
@@ -220,7 +237,9 @@ class Passage {
220237
identifier?: string | null
221238
): Promise<AuthResult> => {
222239
try {
223-
const result = await PassageReactNative.loginWithPasskey(identifier || null);
240+
const result = await PassageReactNative.loginWithPasskey(
241+
identifier || null
242+
);
224243
const parsedResult = JSON.parse(result);
225244
return parsedResult;
226245
} catch (error: any) {

0 commit comments

Comments
 (0)