diff options
author | polwex <polwex@sortug.com> | 2025-07-16 08:51:35 +0700 |
---|---|---|
committer | polwex <polwex@sortug.com> | 2025-07-16 08:51:35 +0700 |
commit | 697ed671f394cbd07ea9751fe17f262744d99a49 (patch) | |
tree | 4becdd805b31d326a26cc4589ebeddad061611e3 /lib | |
parent | e2e14e414de25904d791b503d2852c68b3ac9415 (diff) |
m
Diffstat (limited to 'lib')
-rw-r--r-- | lib/passkey.ts | 71 | ||||
-rw-r--r-- | lib/types.ts | 2 |
2 files changed, 68 insertions, 5 deletions
diff --git a/lib/passkey.ts b/lib/passkey.ts index 573c62b..81a21a5 100644 --- a/lib/passkey.ts +++ b/lib/passkey.ts @@ -1,8 +1,14 @@ +"use client"; + +import { Platform } from "react-native"; +import * as SecureStore from "expo-secure-store"; +import type { AsyncRes } from "./types"; + const PASSKEY_CREDENTIAL_ID_KEY = "urbit_wallet_passkey_id"; const RELYING_PARTY_ID = "wallet.urbit.org"; // Change this to your domain const RELYING_PARTY_NAME = "Urbit Wallet"; -async function createPasskey() { +export async function createPasskey(): AsyncRes<PublicKeyCredential> { const pkok = await window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(); console.log({ pkok }); @@ -117,15 +123,70 @@ async function createPasskey() { if (credential) { // Store the credential ID for later use - // await SecureStore.setItemAsync(PASSKEY_CREDENTIAL_ID_KEY, credential.id); + await SecureStore.setItemAsync(PASSKEY_CREDENTIAL_ID_KEY, credential.id); console.log("Passkey created successfully", credential.id); - return true; + return { ok: credential }; } - return false; + return { error: "Failed to create passkey" }; } catch (error) { console.error("Error creating passkey:", error); - return false; + return { error: `${error}` }; + } +} + +export async function authenticateWithPasskey(): AsyncRes<PublicKeyCredential> { + // if (Platform.OS !== "web") { + // return false; + // } + + try { + const credentialId = await SecureStore.getItemAsync( + PASSKEY_CREDENTIAL_ID_KEY, + ); + + if (!credentialId) { + console.log("No passkey found"); + return { error: "No passkey found" }; + } + + // Generate a random challenge + const challenge = new Uint8Array(32); + crypto.getRandomValues(challenge); + + const getCredentialOptions: CredentialRequestOptions = { + publicKey: { + challenge, + rpId: + window.location.hostname === "localhost" + ? "localhost" + : RELYING_PARTY_ID, + allowCredentials: [ + { + id: Uint8Array.from(atob(credentialId), (c) => c.charCodeAt(0)), + type: "public-key", + transports: ["internal", "hybrid"] as AuthenticatorTransport[], + }, + ], + userVerification: "preferred", + timeout: 60000, + }, + }; + + const credential = (await navigator.credentials.get( + getCredentialOptions, + )) as PublicKeyCredential; + + if (credential) { + // In a production app, you would send the assertion to your server + // to verify it. For this demo, we're just checking if we got a credential. + console.log("Passkey authentication successful"); + return { ok: credential }; + } + + return { error: "failed to generate creds" }; + } catch (error) { + return { error: `${error}` }; } } diff --git a/lib/types.ts b/lib/types.ts new file mode 100644 index 0000000..a22e8be --- /dev/null +++ b/lib/types.ts @@ -0,0 +1,2 @@ +export type Result<T> = { ok: T } | { error: string }; +export type AsyncRes<T> = Promise<Result<T>>; |