summaryrefslogtreecommitdiff
path: root/components/auth/Auth.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'components/auth/Auth.tsx')
-rw-r--r--components/auth/Auth.tsx149
1 files changed, 149 insertions, 0 deletions
diff --git a/components/auth/Auth.tsx b/components/auth/Auth.tsx
new file mode 100644
index 0000000..40512ca
--- /dev/null
+++ b/components/auth/Auth.tsx
@@ -0,0 +1,149 @@
+"use client";
+
+import PrimaryButton from "../PrimaryButton";
+import { SymbolView, SymbolViewProps, SymbolWeight } from "expo-symbols";
+import { useState } from "react";
+import { Platform, StyleProp, ViewStyle } from "react-native";
+
+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";
+
+export function Passkee() {
+ const [isLoading, setIsLoading] = useState(false);
+ async function handleCreatePasskey() {
+ 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;
+ }
+ }
+ return (
+ <PrimaryButton
+ label="Create Passkey"
+ onPress={handleCreatePasskey}
+ isLoading={isLoading}
+ style={{ marginBottom: 16 }}
+ />
+ );
+}