@passkeyme/auth 2.0.11 → 2.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -1048,6 +1048,25 @@ class PasskeymeAuth {
1048
1048
  });
1049
1049
  }
1050
1050
  }
1051
+ /**
1052
+ * Internal helper: Redirect to login in hosted mode, or throw error in direct mode
1053
+ */
1054
+ redirectOrThrow(userMessage, technicalMessage) {
1055
+ if (this.config.mode === "hosted") {
1056
+ this.redirectToLogin();
1057
+ return { method: "redirect" };
1058
+ }
1059
+ else {
1060
+ throw new PasskeymeError({
1061
+ code: PasskeymeErrorCode.PASSKEY_NOT_SUPPORTED,
1062
+ message: technicalMessage,
1063
+ userMessage: userMessage,
1064
+ recoverable: true,
1065
+ retryable: false,
1066
+ suggestedAction: "Use OAuth sign-in instead",
1067
+ });
1068
+ }
1069
+ }
1051
1070
  /**
1052
1071
  * Handle authentication callback from hosted auth page
1053
1072
  */
@@ -1429,6 +1448,19 @@ class PasskeymeAuth {
1429
1448
  }
1430
1449
  // Extract token and user info from response
1431
1450
  const { token, user_uuid, success, message } = completeResponse.data;
1451
+ // Decode JWT to extract email and other user info
1452
+ let tokenEmail;
1453
+ try {
1454
+ const tokenParts = token.split(".");
1455
+ if (tokenParts.length === 3) {
1456
+ const payload = JSON.parse(atob(tokenParts[1]));
1457
+ tokenEmail = payload.email;
1458
+ logger.debug("Extracted email from JWT:", tokenEmail);
1459
+ }
1460
+ }
1461
+ catch (decodeError) {
1462
+ logger.debug("Failed to decode JWT token:", decodeError);
1463
+ }
1432
1464
  // Store tokens - use the JWT token as access token
1433
1465
  const tokens = {
1434
1466
  accessToken: token,
@@ -1437,11 +1469,13 @@ class PasskeymeAuth {
1437
1469
  };
1438
1470
  await this.tokenStorage.setTokens(tokens);
1439
1471
  // Create user object with available information
1472
+ // Prefer email from JWT token, fallback to username
1473
+ const userEmail = tokenEmail || username;
1440
1474
  const user = {
1441
1475
  id: user_uuid,
1442
1476
  uuid: user_uuid,
1443
1477
  username: username,
1444
- email: username, // Assume username is email for now
1478
+ email: userEmail,
1445
1479
  authenticated: true,
1446
1480
  };
1447
1481
  // Update state
@@ -1499,7 +1533,7 @@ class PasskeymeAuth {
1499
1533
  * 1. Check app config for discoverable credentials support
1500
1534
  * 2. If supported: attempt discoverable credentials first (no username)
1501
1535
  * 3. If not supported or fails: use stored username or prompt for username
1502
- * 4. Fallback to hosted auth if all passkey attempts fail
1536
+ * 4. Fallback to hosted auth if all passkey attempts fail (unless mode is 'direct')
1503
1537
  */
1504
1538
  async smartLogin(username, apiKey) {
1505
1539
  logger.debug("[DEBUG] smartLogin called with:", {
@@ -1514,8 +1548,9 @@ class PasskeymeAuth {
1514
1548
  logger.debug("[DEBUG] Passkey support check:", isSupported);
1515
1549
  if (!isSupported || !effectiveApiKey) {
1516
1550
  logger.debug("Conditions not met, redirecting to hosted auth. isSupported:", isSupported, "hasApiKey:", !!effectiveApiKey);
1517
- this.redirectToLogin();
1518
- return { method: "redirect" };
1551
+ return this.redirectOrThrow(isSupported
1552
+ ? "API key is required for passkey authentication."
1553
+ : "Your device doesn't support passkeys.", "Passkey authentication not available");
1519
1554
  }
1520
1555
  try {
1521
1556
  // Get app configuration to check discoverable credentials support
@@ -1538,8 +1573,7 @@ class PasskeymeAuth {
1538
1573
  // If passkeys are disabled at the app level, fallback to hosted auth
1539
1574
  if (!appConfig.passkeyEnabled) {
1540
1575
  logger.debug("Passkeys disabled for this app, falling back to hosted auth");
1541
- this.redirectToLogin();
1542
- return { method: "redirect" };
1576
+ return this.redirectOrThrow("Passkey authentication is not available for this app.", "Passkeys are disabled for this application");
1543
1577
  }
1544
1578
  let authUsername = username;
1545
1579
  // Smart username resolution following industry best practices
@@ -1561,15 +1595,13 @@ class PasskeymeAuth {
1561
1595
  const storageKey = `last_username_${this.config.appId}`;
1562
1596
  logger.debug("Looking for stored username with key:", storageKey);
1563
1597
  const storedUsername = await this.storage.getItem(storageKey);
1564
- logger.debug("Found stored username:", storedUsername);
1565
1598
  if (storedUsername) {
1566
1599
  logger.debug("Using stored username for targeted authentication:", storedUsername);
1567
1600
  authUsername = storedUsername;
1568
1601
  }
1569
1602
  else {
1570
1603
  logger.debug("No username available and discoverable auth failed/unsupported, falling back to hosted auth");
1571
- this.redirectToLogin();
1572
- return { method: "redirect" };
1604
+ return this.redirectOrThrow("Unable to authenticate with passkey. Please sign in with another method.", "No username available for passkey authentication");
1573
1605
  }
1574
1606
  }
1575
1607
  // Attempt passkey authentication with username
@@ -1588,9 +1620,15 @@ class PasskeymeAuth {
1588
1620
  if (this.config.debug) {
1589
1621
  logger.error("[PasskeymeAuth] Passkey authentication error details:", error);
1590
1622
  }
1591
- // Fallback to hosted auth for any passkey failures
1592
- this.redirectToLogin();
1593
- return { method: "redirect" };
1623
+ // Fallback to hosted auth for any passkey failures (unless mode is 'direct')
1624
+ // In direct mode, re-throw the original error for the caller to handle
1625
+ if (this.config.mode === "hosted") {
1626
+ this.redirectToLogin();
1627
+ return { method: "redirect" };
1628
+ }
1629
+ else {
1630
+ throw error;
1631
+ }
1594
1632
  }
1595
1633
  }
1596
1634
  /**