diff options
Diffstat (limited to 'lib/passkey.ts')
-rw-r--r-- | lib/passkey.ts | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/lib/passkey.ts b/lib/passkey.ts new file mode 100644 index 0000000..573c62b --- /dev/null +++ b/lib/passkey.ts @@ -0,0 +1,131 @@ +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() { + const pkok = + await window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(); + console.log({ pkok }); + + // if (Platform.OS !== "web") { + // return false; + // } + console.log("creating passkey"); + + try { + // Generate a random user ID + const userId = new Uint8Array(16); + crypto.getRandomValues(userId); + + // Generate a random challenge + const challenge = new Uint8Array(32); + crypto.getRandomValues(challenge); + + const createCredentialOptions: CredentialCreationOptions = { + publicKey: { + // REQUIRED: Random challenge to prevent replay attacks + // Must be at least 16 bytes, we use 32 for extra security + challenge, + + // REQUIRED: Relying Party (your website/app) + rp: { + // REQUIRED: Human-readable name shown in UI prompts + name: RELYING_PARTY_NAME, + + // OPTIONAL: Domain that owns this credential + // If omitted, defaults to current domain + // Must match or be a registrable suffix of the current domain + id: + window.location.hostname === "localhost" + ? "localhost" + : RELYING_PARTY_ID, + }, + + // REQUIRED: User information + user: { + // REQUIRED: Unique user ID (must not contain PII) + // Used by authenticator to distinguish between accounts + id: userId, + + // REQUIRED: Unique username/identifier for this RP + // Shown in some UI contexts, should be recognizable to user + name: "urbit-user", + + // REQUIRED: Human-readable name for display + // Shown in account selectors and prompts + displayName: "Urbit Wallet User", + }, + + // REQUIRED: List of acceptable public key algorithms + // Order matters - authenticator will use first supported algorithm + pubKeyCredParams: [ + { alg: -7, type: "public-key" }, // ES256 (ECDSA with SHA-256) - most common + { alg: -257, type: "public-key" }, // RS256 (RSASSA-PKCS1-v1_5) - fallback + ], + + // OPTIONAL: Criteria for authenticator selection + authenticatorSelection: { + // OPTIONAL: "platform" (built-in like Touch ID) or "cross-platform" (USB key) + // Omitting allows both types + // authenticatorAttachment: "platform", + + // OPTIONAL: User verification requirement + // "required" - must verify user (biometric/PIN) + // "preferred" - verify if possible, continue if not (default) + // "discouraged" - don't verify unless required by RP + userVerification: "preferred", + + // OPTIONAL: Whether to create a discoverable credential (passkey) + // "required" - must create discoverable credential + // "preferred" - create if possible (default) + // "discouraged" - don't create discoverable credential + residentKey: "preferred", + + // DEPRECATED: Use residentKey instead + // Kept for backwards compatibility with older authenticators + requireResidentKey: false, + }, + + // OPTIONAL: Time limit in milliseconds (default varies by browser) + // 60 seconds is reasonable for user interaction + timeout: 60000, + + // OPTIONAL: Attestation preference (proof of authenticator legitimacy) + // "none" - no attestation needed (default, best for privacy) + // "indirect" - RP prefers attestation but allows anonymization + // "direct" - RP needs attestation from authenticator + // "enterprise" - RP needs attestation and device info (requires permission) + attestation: "none", + + // OPTIONAL: List of credentials to exclude (prevent duplicates) + // excludeCredentials: [ + // { id: existingCredentialId, type: "public-key" } + // ], + + // OPTIONAL: Extensions for additional features + // extensions: { + // credProps: true, // Request additional credential properties + // hmacCreateSecret: true, // For symmetric key operations + // }, + }, + }; + + const credential = (await navigator.credentials.create( + createCredentialOptions, + )) as PublicKeyCredential; + console.log({ credential }); + + if (credential) { + // Store the credential ID for later use + // await SecureStore.setItemAsync(PASSKEY_CREDENTIAL_ID_KEY, credential.id); + + console.log("Passkey created successfully", credential.id); + return true; + } + + return false; + } catch (error) { + console.error("Error creating passkey:", error); + return false; + } +} |