Skip to content

Commit

Permalink
feat: add passkey ios native
Browse files Browse the repository at this point in the history
  • Loading branch information
code-z2 committed Oct 16, 2024
1 parent a51fb89 commit 566f5ba
Show file tree
Hide file tree
Showing 8 changed files with 683 additions and 59 deletions.
6 changes: 6 additions & 0 deletions apps/native/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- ExpoSystemUI (3.0.7):
- ExpoModulesCore
- FBLazyVector (0.74.5)
- fmt (9.1.0)
- glog (0.3.5)
Expand Down Expand Up @@ -1236,6 +1238,7 @@ DEPENDENCIES:
- ExpoFont (from `../../../node_modules/expo-font/ios`)
- ExpoKeepAwake (from `../../../node_modules/expo-keep-awake/ios`)
- ExpoModulesCore (from `../../../node_modules/expo-modules-core`)
- ExpoSystemUI (from `../../../node_modules/expo-system-ui/ios`)
- FBLazyVector (from `../../../node_modules/react-native/Libraries/FBLazyVector`)
- fmt (from `../../../node_modules/react-native/third-party-podspecs/fmt.podspec`)
- glog (from `../../../node_modules/react-native/third-party-podspecs/glog.podspec`)
Expand Down Expand Up @@ -1317,6 +1320,8 @@ EXTERNAL SOURCES:
:path: "../../../node_modules/expo-keep-awake/ios"
ExpoModulesCore:
:path: "../../../node_modules/expo-modules-core"
ExpoSystemUI:
:path: "../../../node_modules/expo-system-ui/ios"
FBLazyVector:
:path: "../../../node_modules/react-native/Libraries/FBLazyVector"
fmt:
Expand Down Expand Up @@ -1436,6 +1441,7 @@ SPEC CHECKSUMS:
ExpoFont: 00756e6c796d8f7ee8d211e29c8b619e75cbf238
ExpoKeepAwake: 3b8815d9dd1d419ee474df004021c69fdd316d08
ExpoModulesCore: f30a203ff1863bab3dd9f4421e7fc1564797f18a
ExpoSystemUI: d4f065a016cae6721b324eb659cdee4d4cf0cb26
FBLazyVector: ac12dc084d1c8ec4cc4d7b3cf1b0ebda6dab85af
fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120
glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f
Expand Down
2 changes: 2 additions & 0 deletions apps/native/ios/native.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -275,13 +275,15 @@
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,31 +73,31 @@ class PublicKeyCred : Record {
}

data class CreateCredentialOptions(
val rp: RelyingParty,
val challenge: String,

val user: UserEntity = UserEntity(),
val rp: RelyingParty,

val challenge: String,
val user: UserEntity,

val pubKeyCredParams: List<PublicKeyCred>,

val timeout: Int?,

val authenticatorSelection: AuthenticatorSelection,

val attestation: String?,

val authenticatorSelection: AuthenticatorSelection,

val excludeCredentials: List<PublicKeyCredentialDescriptor>?
)

data class GetCredentialOptions(
val challenge: String,

val allowCredentials: List<PublicKeyCredentialDescriptor>,

val timeout: Int?,
val allowCredentials: List<PublicKeyCredentialDescriptor>?,

val userVerification: String?,

val rpId: String?
val timeout: Int?,

val rpId: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,13 @@ class Cosmr1CredentialHandlerModule : Module() {
"onAuthenticationSuccess"
)

AsyncFunction("authenticate") Coroutine { challenge: String,
timeout: Int?,
rpId: String?,
userVerification: String?,
allowCredentials: ExclusiveCredentials?
AsyncFunction("authenticate") Coroutine {
prefersImmediatelyAvailableCred: Boolean,
challenge: String,
timeout: Int?,
rpId: String,
userVerification: String?,
allowCredentials: ExclusiveCredentials?
->

val getOptions = parseAssertionOptions(
Expand All @@ -74,22 +76,22 @@ class Cosmr1CredentialHandlerModule : Module() {
userVerification
)
val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(
requestJson = Gson().toJson(getOptions),
)
requestJson = Gson().toJson(getOptions))
val getCredRequest = GetCredentialRequest(
listOf(getPublicKeyCredentialOption)
)
listOf(getPublicKeyCredentialOption),
preferImmediatelyAvailableCredentials = prefersImmediatelyAvailableCred)
return@Coroutine authenticate(getCredRequest)
}

AsyncFunction("register") Coroutine { prefersImmediatelyAvailableCred: Boolean,
challenge: String,
rp: RelyingParty,
user: UserEntity,
timeout: Int?,
attestation: String?,
excludeCredentials: ExclusiveCredentials?,
authenticatorSelection: AuthenticatorSelection?
AsyncFunction("register") Coroutine {
prefersImmediatelyAvailableCred: Boolean,
challenge: String,
rp: RelyingParty,
user: UserEntity,
timeout: Int?,
attestation: String?,
excludeCredentials: ExclusiveCredentials?,
authenticatorSelection: AuthenticatorSelection?
->

val createOptions = parseAttestationOptions(
Expand All @@ -113,12 +115,12 @@ class Cosmr1CredentialHandlerModule : Module() {
challenge: String,
allowCredentials: ExclusiveCredentials?,
timeout: Int?,
rpId: String?,
rpId: String,
userVerification: String?
): GetCredentialOptions {
return GetCredentialOptions(
challenge = challenge,
allowCredentials = allowCredentials?.items ?: emptyList(),
allowCredentials = allowCredentials?.items,
timeout = timeout ?: Constants.TIMEOUT,
rpId = rpId,
userVerification = userVerification ?: Constants.USER_VERIFICATION
Expand All @@ -141,7 +143,7 @@ class Cosmr1CredentialHandlerModule : Module() {
pubKeyCredParams = listOf(PublicKeyCred()),
timeout = timeout ?: Constants.TIMEOUT,
attestation = attestation ?: Constants.ATTESTATION,
excludeCredentials = excludeCredentials?.items ?: emptyList(),
excludeCredentials = excludeCredentials?.items,
authenticatorSelection = authenticatorSelection ?: AuthenticatorSelection()
)
}
Expand All @@ -160,7 +162,7 @@ class Cosmr1CredentialHandlerModule : Module() {
handleAttestationResult(result)
} catch (e: CreateCredentialException) {
handleAttestationFailure(e)
null
throw e
}
}

Expand Down Expand Up @@ -233,7 +235,7 @@ class Cosmr1CredentialHandlerModule : Module() {
handleAssertionResult(result)
} catch (e: GetCredentialException) {
handleAssertionFailure(e)
null
throw e
}
}

Expand Down
175 changes: 175 additions & 0 deletions packages/cred-native/ios/Constants.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import ExpoModulesCore

class Konstants {
static let TIMEOUT = 60000
static let ATTESTATION = "direct"
static let AUTHENTICATOR_ATTACHMENT = "platform"
static let REQUIRE_RESIDENT_KEY = true
static let RESIDENT_KEY = "required"
static let USER_VERIFICATION = "required"
static let PUB_KEY_CRED_PARAM: [String: Any] = [
"type": "public-key",
"alg": -7
]
}

struct RelyingParty: Record {
@Field
var name: String = ""

@Field
var id: String = ""
}

struct UserEntity: Record {
@Field
var id: String = ""

@Field
var name: String = ""

@Field
var displayName: String = ""
}

struct AuthenticatorSelection: Record {
@Field
var authenticatorAttachment: String = Konstants.AUTHENTICATOR_ATTACHMENT

@Field
var requireResidentKey: Bool = Konstants.REQUIRE_RESIDENT_KEY

@Field
var residentKey: String = Konstants.RESIDENT_KEY

@Field
var userVerification: String = Konstants.USER_VERIFICATION
}

struct PublicKeyCredentialDescriptor: Record {
@Field
var id: String = ""

@Field
var type: String = "public-key"

@Field
var transports: [String]? = nil
}

struct ExclusiveCredentials: Record {
@Field
var items: [PublicKeyCredentialDescriptor] = []
}

struct PublicKeyCred: Record {
@Field
var type: String = Konstants.PUB_KEY_CRED_PARAM["type"] as! String

@Field
var alg: Int = Konstants.PUB_KEY_CRED_PARAM["alg"] as! Int
}

struct CreateCredentialOptions {
var challenge: String

var rp: RelyingParty

var user: UserEntity

var pubKeyCredParams: [PublicKeyCred]

var timeout: Int?

var attestation: String?

var authenticatorSelection: AuthenticatorSelection

var excludeCredentials: [PublicKeyCredentialDescriptor]?

init(
challenge: String,
rp: RelyingParty,
user: UserEntity,
pubKeyCredParams: [PublicKeyCred],
timeout: Int?,
attestation: String?,
authenticatorSelection: AuthenticatorSelection,
excludeCredentials: [PublicKeyCredentialDescriptor]?)
{
self.challenge = challenge
self.rp = rp
self.user = user
self.pubKeyCredParams = pubKeyCredParams
self.timeout = timeout
self.attestation = attestation
self.authenticatorSelection = authenticatorSelection
self.excludeCredentials = excludeCredentials
}

func toDictionary() -> [String: Any] {
return [
"challenge": challenge,
"rp": rp.toDictionary(),
"user": user.toDictionary(),
"pubKeyCredParams": pubKeyCredParams.compactMap{
element in element.toDictionary()},
"timeout": timeout ?? Konstants.TIMEOUT,
"attestation": attestation ?? Konstants.ATTESTATION,
"authenticatorSelection": authenticatorSelection.toDictionary(),
"excludeCredentials": excludeCredentials?.compactMap{
element in element.toDictionary()} ?? []
]
}

func toString() -> String {
if let payloadJSONData = try? JSONSerialization.data(withJSONObject: toDictionary(), options: .fragmentsAllowed) {
guard let payloadJSONText = String(data: payloadJSONData, encoding: .utf8) else { return "" }
return payloadJSONText
}
}
}

struct GetCredentialOptions {
var challenge: String

var allowCredentials: [PublicKeyCredentialDescriptor]?

var userVerification: String?

var timeout: Int?

var rpId: String

init(
challenge: String,
allowCredentials: [PublicKeyCredentialDescriptor]?,
userVerification: String?,
timeout: Int?,
rpId: String)
{
self.challenge = challenge
self.allowCredentials = allowCredentials
self.userVerification = userVerification
self.timeout = timeout
self.rpId = rpId
}

func toDictionary() -> [String: Any] {
return [
"challenge": challenge,
"allowCredentials": allowCredentials?.compactMap{
element in element.toDictionary()} ?? [],
"userVerification": userVerification ?? Konstants.USER_VERIFICATION,
"timeout": timeout ?? Konstants.TIMEOUT,
"rpId": rpId
]
}

func toString() -> String {
if let payloadJSONData = try? JSONSerialization.data(withJSONObject: toDictionary(), options: .fragmentsAllowed) {
guard let payloadJSONText = String(data: payloadJSONData, encoding: .utf8) else { return "" }
return payloadJSONText
}
}
}
Loading

0 comments on commit 566f5ba

Please sign in to comment.