Skip to content

Add authentication flow for passkey #388

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quiet-sloths-lose.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"permissionless": patch
---

Added passkeys authentication flow
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Base64 } from "ox"

Check warning on line 1 in packages/permissionless/actions/passkeyServer/startAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/startAuthentication.ts#L1

Added line #L1 was not covered by tests
import type { WebAuthnP256 } from "ox"
import {
type Account,
type Chain,
type Client,
type Transport,
toHex
} from "viem"
import type { PasskeyServerRpcSchema } from "../../types/passkeyServer.js"

export type StartAuthenticationReturnType = WebAuthnP256.sign.Options & {
uuid: string
}

export const startAuthentication = async (
client: Client<

Check warning on line 17 in packages/permissionless/actions/passkeyServer/startAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/startAuthentication.ts#L16-L17

Added lines #L16 - L17 were not covered by tests
Transport,
Chain | undefined,
Account | undefined,
PasskeyServerRpcSchema
>
): Promise<StartAuthenticationReturnType> => {
const response = await client.request({
method: "pks_startAuthentication",
params: []
})

Check warning on line 27 in packages/permissionless/actions/passkeyServer/startAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/startAuthentication.ts#L23-L27

Added lines #L23 - L27 were not covered by tests

return {
challenge: toHex(Base64.toBytes(response.challenge)),
rpId: response.rpId,
userVerification: response.userVerification,
uuid: response.uuid
}
}

Check warning on line 35 in packages/permissionless/actions/passkeyServer/startAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/startAuthentication.ts#L29-L35

Added lines #L29 - L35 were not covered by tests
121 changes: 121 additions & 0 deletions packages/permissionless/actions/passkeyServer/verifyAuthentication.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { Base64, type WebAuthnP256 } from "ox"

Check warning on line 1 in packages/permissionless/actions/passkeyServer/verifyAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyAuthentication.ts#L1

Added line #L1 was not covered by tests
// import { Base64 } from "ox"
import type { Account, Chain, Client, Hex, Transport } from "viem"
import type { PasskeyServerRpcSchema } from "../../types/passkeyServer.js"

export type VerifyAuthenticationParameters = WebAuthnP256.sign.ReturnType & {
uuid: string
}

export type VerifyAuthenticationReturnType = {
success: boolean
id: string
publicKey: Hex
}

export const verifyAuthentication = async (
client: Client<

Check warning on line 17 in packages/permissionless/actions/passkeyServer/verifyAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyAuthentication.ts#L16-L17

Added lines #L16 - L17 were not covered by tests
Transport,
Chain | undefined,
Account | undefined,
PasskeyServerRpcSchema
>,
args: VerifyAuthenticationParameters
): Promise<VerifyAuthenticationReturnType> => {
const { raw, uuid } = args

Check warning on line 25 in packages/permissionless/actions/passkeyServer/verifyAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyAuthentication.ts#L23-L25

Added lines #L23 - L25 were not covered by tests

let responseAuthenticatorData: string

Check warning on line 27 in packages/permissionless/actions/passkeyServer/verifyAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyAuthentication.ts#L27

Added line #L27 was not covered by tests

if ("authenticatorData" in raw.response) {
responseAuthenticatorData = Base64.fromBytes(
new Uint8Array(raw.response.authenticatorData as ArrayBuffer),
{
url: true
}
)
} else {
throw new Error("authenticatorData not found in the signature")
}

Check warning on line 38 in packages/permissionless/actions/passkeyServer/verifyAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyAuthentication.ts#L29-L38

Added lines #L29 - L38 were not covered by tests

let signature: string
if ("signature" in raw.response) {
signature = Base64.fromBytes(
new Uint8Array(raw.response.signature as ArrayBuffer),
{
pad: false,
url: true
}
)
} else {
throw new Error("signature not found in the signature")
}

Check warning on line 51 in packages/permissionless/actions/passkeyServer/verifyAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyAuthentication.ts#L40-L51

Added lines #L40 - L51 were not covered by tests

let userHandle: string | undefined
if ("userHandle" in raw.response) {
userHandle = Base64.fromBytes(
new Uint8Array(raw.response.userHandle as ArrayBuffer),
{
pad: false,
url: true
}
)
}

Check warning on line 62 in packages/permissionless/actions/passkeyServer/verifyAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyAuthentication.ts#L53-L62

Added lines #L53 - L62 were not covered by tests

const serverResponse = await client.request(
{
method: "pks_verifyAuthentication",
params: [
{
id: raw.id,
rawId: Base64.fromBytes(new Uint8Array(raw.rawId), {
pad: false,
url: true
}),
authenticatorAttachment: raw.authenticatorAttachment as

Check warning on line 74 in packages/permissionless/actions/passkeyServer/verifyAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyAuthentication.ts#L64-L74

Added lines #L64 - L74 were not covered by tests
| "cross-platform"
| "platform",
response: {
clientDataJSON: Base64.fromBytes(
new Uint8Array(raw.response.clientDataJSON),
{
pad: false,
url: true
}
),
authenticatorData: responseAuthenticatorData,
signature,
userHandle
},
clientExtensionResults: raw.getClientExtensionResults(),
type: raw.type as "public-key"
},
{
uuid
}
]
},
{
retryCount: 0
}
)

Check warning on line 100 in packages/permissionless/actions/passkeyServer/verifyAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyAuthentication.ts#L77-L100

Added lines #L77 - L100 were not covered by tests

const success = Boolean(serverResponse?.success)
const id = serverResponse?.id
const publicKey = serverResponse?.publicKey

Check warning on line 104 in packages/permissionless/actions/passkeyServer/verifyAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyAuthentication.ts#L102-L104

Added lines #L102 - L104 were not covered by tests

if (typeof id !== "string") {
throw new Error("Invalid passkey id returned from server")
}

Check warning on line 108 in packages/permissionless/actions/passkeyServer/verifyAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyAuthentication.ts#L106-L108

Added lines #L106 - L108 were not covered by tests

if (typeof publicKey !== "string" || !publicKey.startsWith("0x")) {
throw new Error(
"Invalid public key returned from server - must be hex string starting with 0x"
)
}

Check warning on line 114 in packages/permissionless/actions/passkeyServer/verifyAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyAuthentication.ts#L110-L114

Added lines #L110 - L114 were not covered by tests

return {
success,
id,
publicKey: publicKey as Hex
}
}

Check warning on line 121 in packages/permissionless/actions/passkeyServer/verifyAuthentication.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyAuthentication.ts#L116-L121

Added lines #L116 - L121 were not covered by tests
86 changes: 48 additions & 38 deletions packages/permissionless/actions/passkeyServer/verifyRegistration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,49 +48,59 @@
}
}

const serverResponse = await client.request({
method: "pks_verifyRegistration",
params: [
{
id: credential.id,
rawId: Base64.fromBytes(new Uint8Array(credential.raw.rawId), {
pad: false,
url: true
}),
response: {
clientDataJSON: Base64.fromBytes(
new Uint8Array(response.clientDataJSON)
),
attestationObject: Base64.fromBytes(
new Uint8Array(response.attestationObject),
const serverResponse = await client.request(
{
method: "pks_verifyRegistration",
params: [
{
id: credential.id,
rawId: Base64.fromBytes(
new Uint8Array(credential.raw.rawId),

Check warning on line 58 in packages/permissionless/actions/passkeyServer/verifyRegistration.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyRegistration.ts#L51-L58

Added lines #L51 - L58 were not covered by tests
{
pad: false,

Check warning on line 60 in packages/permissionless/actions/passkeyServer/verifyRegistration.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyRegistration.ts#L60

Added line #L60 was not covered by tests
url: true
}
),
transports:
typeof response.getTransports === "function"
? (response.getTransports() as (
| "ble"
| "cable"
| "hybrid"
| "internal"
| "nfc"
| "smart-card"
| "usb"
)[])
: undefined,
publicKeyAlgorithm: responsePublicKeyAlgorithm,
authenticatorData: responseAuthenticatorData
response: {
clientDataJSON: Base64.fromBytes(
new Uint8Array(response.clientDataJSON)
),
attestationObject: Base64.fromBytes(
new Uint8Array(response.attestationObject),
{
url: true
}
),
transports:
typeof response.getTransports === "function"
? (response.getTransports() as (

Check warning on line 76 in packages/permissionless/actions/passkeyServer/verifyRegistration.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyRegistration.ts#L64-L76

Added lines #L64 - L76 were not covered by tests
| "ble"
| "cable"
| "hybrid"
| "internal"
| "nfc"
| "smart-card"
| "usb"
)[])
: undefined,
publicKeyAlgorithm: responsePublicKeyAlgorithm,
authenticatorData: responseAuthenticatorData
},
authenticatorAttachment: credential.raw
.authenticatorAttachment as

Check warning on line 90 in packages/permissionless/actions/passkeyServer/verifyRegistration.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyRegistration.ts#L85-L90

Added lines #L85 - L90 were not covered by tests
| "cross-platform"
| "platform",
clientExtensionResults:
credential.raw.getClientExtensionResults(),
type: credential.raw.type as "public-key"

Check warning on line 95 in packages/permissionless/actions/passkeyServer/verifyRegistration.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyRegistration.ts#L93-L95

Added lines #L93 - L95 were not covered by tests
},
authenticatorAttachment: credential.raw
.authenticatorAttachment as "cross-platform" | "platform",
clientExtensionResults:
credential.raw.getClientExtensionResults(),
type: credential.raw.type as "public-key"
},
context
]
})
context
]
},
{
retryCount: 0
}
)

Check warning on line 103 in packages/permissionless/actions/passkeyServer/verifyRegistration.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/actions/passkeyServer/verifyRegistration.ts#L97-L103

Added lines #L97 - L103 were not covered by tests

const success = Boolean(serverResponse?.success)
const id = serverResponse?.id
Expand Down
17 changes: 16 additions & 1 deletion packages/permissionless/clients/decorators/passkeyServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,20 @@
type GetCredentialsReturnType,
getCredentials
} from "../../actions/passkeyServer/getCredentials.js"
import {
type StartAuthenticationReturnType,
startAuthentication
} from "../../actions/passkeyServer/startAuthentication.js"
import {
type StartRegistrationParameters,
type StartRegistrationReturnType,
startRegistration
} from "../../actions/passkeyServer/startRegistration.js"
import {
type VerifyAuthenticationParameters,
type VerifyAuthenticationReturnType,
verifyAuthentication
} from "../../actions/passkeyServer/verifyAuthentication.js"
import {
type VerifyRegistrationParameters,
type VerifyRegistrationReturnType,
Expand All @@ -23,6 +32,10 @@
verifyRegistration: (
args: VerifyRegistrationParameters
) => Promise<VerifyRegistrationReturnType>
startAuthentication: () => Promise<StartAuthenticationReturnType>
verifyAuthentication: (
args: VerifyAuthenticationParameters
) => Promise<VerifyAuthenticationReturnType>
getCredentials: (
args: GetCredentialsParameters
) => Promise<GetCredentialsReturnType>
Expand All @@ -38,5 +51,7 @@
): PasskeyServerActions => ({
startRegistration: (args) => startRegistration(client, args),
verifyRegistration: (args) => verifyRegistration(client, args),
getCredentials: (args) => getCredentials(client, args)
getCredentials: (args) => getCredentials(client, args),
startAuthentication: () => startAuthentication(client),
verifyAuthentication: (args) => verifyAuthentication(client, args)

Check warning on line 56 in packages/permissionless/clients/decorators/passkeyServer.ts

View check run for this annotation

Codecov / codecov/patch

packages/permissionless/clients/decorators/passkeyServer.ts#L54-L56

Added lines #L54 - L56 were not covered by tests
})
41 changes: 41 additions & 0 deletions packages/permissionless/types/passkeyServer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import type { Hex } from "viem"

export type PasskeyServerRpcSchema = [
{
Method: "pks_startAuthentication"
Parameters: []
ReturnType: {
challenge: string
rpId: string
timeout?: number
userVerification?: "required" | "preferred" | "discouraged"
uuid: string
}
},
{
Method: "pks_startRegistration"
Parameters: [context: unknown]
Expand Down Expand Up @@ -71,6 +82,36 @@ export type PasskeyServerRpcSchema = [
publicKey: Hex
}
},
{
Method: "pks_verifyAuthentication"
Parameters: [
{
id: string
rawId: string
response: {
clientDataJSON: string
authenticatorData: string
signature: string
userHandle?: string
}
authenticatorAttachment: "cross-platform" | "platform"
clientExtensionResults: {
appid?: boolean
credProps?: {
rk?: boolean
}
hmacCreateSecret?: boolean
}
type: "public-key"
},
context: unknown
]
ReturnType: {
success: boolean
id: string
publicKey: Hex
}
},
{
Method: "pks_getCredentials"
Parameters: [context: unknown]
Expand Down
Loading
Loading