Skip to content

Commit

Permalink
Merge pull request #204 from zerodevapp/feat/react-native-passkeys-su…
Browse files Browse the repository at this point in the history
…pport

feat: add react-native support for passkeys
  • Loading branch information
adnpark authored Jan 31, 2025
2 parents eaf7365 + e58677d commit 3b016a2
Show file tree
Hide file tree
Showing 21 changed files with 568 additions and 32 deletions.
Binary file modified bun.lockb
Binary file not shown.
6 changes: 6 additions & 0 deletions plugins/multi-chain-web-authn/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @zerodev/multi-chain-web-auth

## 5.4.5

### Patch Changes

- feat: add custom signing method support

## 5.4.4

### Patch Changes
Expand Down
4 changes: 2 additions & 2 deletions plugins/multi-chain-web-authn/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zerodev/multi-chain-web-authn-validator",
"version": "5.4.4",
"version": "5.4.5",
"author": "ZeroDev",
"main": "./_cjs/index.js",
"module": "./_esm/index.js",
Expand Down Expand Up @@ -36,7 +36,7 @@
"peerDependencies": {
"viem": "^2.21.40",
"@zerodev/sdk": "^5.4.0",
"@zerodev/webauthn-key": "^5.4.0"
"@zerodev/webauthn-key": "^5.4.2"
},
"dependencies": {
"@simplewebauthn/browser": "^9.0.1",
Expand Down
13 changes: 10 additions & 3 deletions plugins/multi-chain-web-authn/toMultiChainWebAuthnValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,16 @@ export async function toMultiChainWebAuthnValidator<
// note that this address will be overwritten by actual address
address: "0x0000000000000000000000000000000000000000",
async signMessage({ message }) {
return signMessageUsingWebAuthn(message, chainId, rpId, [
{ id: webAuthnKey.authenticatorId, type: "public-key" }
])
return webAuthnKey.signMessageCallback
? webAuthnKey.signMessageCallback(
message,
webAuthnKey.rpID,
chainId,
[{ id: webAuthnKey.authenticatorId, type: "public-key" }]
)
: signMessageUsingWebAuthn(message, chainId, rpId, [
{ id: webAuthnKey.authenticatorId, type: "public-key" }
])
},
async signTransaction(_, __) {
throw new SignTransactionNotSupportedBySmartAccountError()
Expand Down
6 changes: 6 additions & 0 deletions plugins/multi-chain-weighted-validator/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @zerodev/multi-chain-weighted-validator

## 5.4.3

### Patch Changes

- feat: add custom signing method support

## 5.4.2

### Patch Changes
Expand Down
4 changes: 2 additions & 2 deletions plugins/multi-chain-weighted-validator/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zerodev/multi-chain-weighted-validator",
"version": "5.4.2",
"version": "5.4.3",
"author": "ZeroDev",
"main": "./_cjs/index.js",
"module": "./_esm/index.js",
Expand Down Expand Up @@ -41,7 +41,7 @@
"peerDependencies": {
"viem": "^2.21.40",
"@zerodev/sdk": "^5.4.0",
"@zerodev/webauthn-key": "^5.4.0"
"@zerodev/webauthn-key": "^5.4.2"
},
"type": "module"
}
13 changes: 10 additions & 3 deletions plugins/multi-chain-weighted-validator/signers/toWebAuthnSigner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,16 @@ export const toWebAuthnSigner = async <
address: "0x0000000000000000000000000000000000000000",
async signMessage({ message }) {
const chainId = await getChainId(client)
return signMessageUsingWebAuthn(message, chainId, [
{ id: webAuthnKey.authenticatorId, type: "public-key" }
])
return webAuthnKey.signMessageCallback
? webAuthnKey.signMessageCallback(
message,
webAuthnKey.rpID,
chainId,
[{ id: webAuthnKey.authenticatorId, type: "public-key" }]
)
: signMessageUsingWebAuthn(message, chainId, [
{ id: webAuthnKey.authenticatorId, type: "public-key" }
])
},
async signTransaction(_, __) {
throw new Error(
Expand Down
6 changes: 6 additions & 0 deletions plugins/passkey/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @zerodev/passkey-validator

## 5.5.2

### Patch Changes

- feat: add custom signing method support

## 5.5.1

### Patch Changes
Expand Down
4 changes: 2 additions & 2 deletions plugins/passkey/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zerodev/passkey-validator",
"version": "5.5.1",
"version": "5.5.2",
"author": "ZeroDev",
"main": "./_cjs/index.js",
"module": "./_esm/index.js",
Expand Down Expand Up @@ -40,7 +40,7 @@
"peerDependencies": {
"viem": "^2.21.40",
"@zerodev/sdk": "^5.4.0",
"@zerodev/webauthn-key": "^5.4.0"
"@zerodev/webauthn-key": "^5.4.2"
},
"type": "module"
}
13 changes: 10 additions & 3 deletions plugins/passkey/toPasskeyValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,16 @@ export async function toPasskeyValidator<
// note that this address will be overwritten by actual address
address: "0x0000000000000000000000000000000000000000",
async signMessage({ message }) {
return signMessageUsingWebAuthn(message, chainId, [
{ id: webAuthnKey.authenticatorId, type: "public-key" }
])
return webAuthnKey.signMessageCallback
? webAuthnKey.signMessageCallback(
message,
webAuthnKey.rpID,
chainId,
[{ id: webAuthnKey.authenticatorId, type: "public-key" }]
)
: signMessageUsingWebAuthn(message, chainId, [
{ id: webAuthnKey.authenticatorId, type: "public-key" }
])
},
async signTransaction(_, __) {
throw new Error(
Expand Down
19 changes: 19 additions & 0 deletions plugins/react-native-passkeys-utils/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# @zerodev/react-native-passkeys-utils

## 5.4.2

### Patch Changes

- feat: export parseLoginCred

## 5.4.1

### Patch Changes

- feat: add parseLoginCred

## 5.4.0

### Patch Changes

- feat: add react native passkeys support
2 changes: 2 additions & 0 deletions plugins/react-native-passkeys-utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { signMessageWithReactNativePasskeys } from "./signMessageWithReactNativePasskeys.js"
export { parsePasskeyCred, parseLoginCred } from "./utils.js"
47 changes: 47 additions & 0 deletions plugins/react-native-passkeys-utils/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "@zerodev/react-native-passkeys-utils",
"version": "5.4.2",
"author": "ZeroDev",
"main": "./_cjs/index.js",
"module": "./_esm/index.js",
"types": "./_types/index.d.ts",
"typings": "./_types/index.d.ts",
"sideEffects": false,
"license": "MIT",
"files": [
"_esm",
"_cjs",
"_types",
"./**/*.ts",
"!_esm/**/*.tsbuildinfo",
"!_cjs/**/*.tsbuildinfo",
"!_types/**/*.tsbuildinfo",
"!.env",
"!./**/*.test.ts",
"!.changeset"
],
"scripts": {
"build": "bun run clean && bun run build:cjs && bun run build:esm && bun run build:types",
"build:cjs": "tsc --project ./tsconfig.build.json --module commonjs --outDir ./_cjs --removeComments --verbatimModuleSyntax false && printf '{\"type\":\"commonjs\"}' > ./_cjs/package.json",
"build:esm": "tsc --project ./tsconfig.build.json --module es2020 --outDir ./_esm && printf '{\"type\": \"module\",\"sideEffects\":false}' > ./_esm/package.json",
"build:types": "tsc --project ./tsconfig.build.json --module esnext --declarationDir ./_types --emitDeclarationOnly --declaration --declarationMap",
"clean": "rimraf _esm _cjs _types",
"changeset": "changeset",
"changeset:release": "bun run build && changeset publish",
"changeset:version": "changeset version && bun install --lockfile-only",
"format": "biome format . --write",
"lint": "biome check .",
"lint:fix": "bun run lint --apply"
},
"dependencies": {
"@noble/curves": "^1.3.0",
"@simplewebauthn/types": "^12.0.0",
"asn1js": "^3.0.5",
"react-native-passkeys": "^0.3.1"
},
"peerDependencies": {
"viem": "^2.21.40",
"@zerodev/webauthn-key": "^5.4.2"
},
"type": "module"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import type { PublicKeyCredentialRequestOptionsJSON } from "@simplewebauthn/typescript-types"
import * as passkey from "react-native-passkeys"
import { type SignableMessage, encodeAbiParameters } from "viem"

import {
b64ToBytes,
base64FromUint8Array,
findQuoteIndices,
hexStringToUint8Array,
isRIP7212SupportedNetwork,
parseAndNormalizeSig,
uint8ArrayToHexString
} from "./utils"

export async function signMessageWithReactNativePasskeys(
message: SignableMessage,
rpID: string,
chainId: number,
allowCredentials?: PublicKeyCredentialRequestOptionsJSON["allowCredentials"]
) {
let messageContent: string
if (typeof message === "string") {
messageContent = message
} else if ("raw" in message && typeof message.raw === "string") {
messageContent = message.raw
} else if ("raw" in message && message.raw instanceof Uint8Array) {
// convert raw bytes -> hex string (without 0x)
messageContent = Buffer.from(message.raw).toString("hex")
} else {
throw new Error("Unsupported message format")
}

// remove any 0x prefix for simpler base64 conversion
const formattedMessage = messageContent.startsWith("0x")
? messageContent.slice(2)
: messageContent

// convert hex -> base64
const challenge = base64FromUint8Array(
hexStringToUint8Array(formattedMessage),
true
)

// call react-native-passkeys
const cred = await passkey.get({
rpId: rpID,
challenge,
allowCredentials,
userVerification: "required"
})

if (!cred) {
throw new Error("No passkey credential found")
}

// get authenticator data
const { authenticatorData } = cred.response
const authenticatorDataHex = uint8ArrayToHexString(
b64ToBytes(authenticatorData)
)

// parse client data JSON
const clientDataJSON = atob(cred.response.clientDataJSON)
const { beforeType } = findQuoteIndices(clientDataJSON)

// parse signature r/s
const { signature } = cred.response
const signatureHex = uint8ArrayToHexString(b64ToBytes(signature))
const { r, s } = parseAndNormalizeSig(signatureHex)

// encode signature in the format the Passkey Validator expects
const encodedSignature = encodeAbiParameters(
[
{ name: "authenticatorData", type: "bytes" },
{ name: "clientDataJSON", type: "string" },
{ name: "responseTypeLocation", type: "uint256" },
{ name: "r", type: "uint256" },
{ name: "s", type: "uint256" },
{ name: "usePrecompiled", type: "bool" }
],
[
authenticatorDataHex,
clientDataJSON,
beforeType,
BigInt(r),
BigInt(s),
isRIP7212SupportedNetwork(chainId)
]
)

return encodedSignature
}
11 changes: 11 additions & 0 deletions plugins/react-native-passkeys-utils/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../../templates/typescript/tsconfig.build.json",
"include": ["./"],
"exclude": ["./**/*.test.ts", "./**/*.test-d.ts", "./**/*.bench.ts"],
"compilerOptions": {
"moduleResolution": "node",
"sourceMap": true,
"rootDir": "./",
"baseUrl": "./"
}
}
Loading

0 comments on commit 3b016a2

Please sign in to comment.