expo-passkey
Version:
Passkey authentication for Expo apps with Better Auth integration
186 lines • 7.36 kB
JavaScript
/**
* @file Biometric authentication utilities
* @module expo-passkey/client/utils/biometrics
*/
import { isSupportedPlatform } from "./environment";
import ExpoPasskeyModule from "../../ExpoPasskeyModule";
import { loadExpoModules } from "./modules";
// Helper function to get modules only when needed
function getModules() {
return loadExpoModules();
}
/**
* Checks if biometric authentication is supported and available
* @returns Promise resolving to biometric support information
*/
export async function checkBiometricSupport() {
try {
const { LocalAuthentication, Platform, Device } = getModules();
// Get basic hardware support info
const isSupported = await LocalAuthentication.hasHardwareAsync();
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
const availableTypes = await LocalAuthentication.supportedAuthenticationTypesAsync();
// Get platform details for debugging and feature detection
const platformDetails = {
platform: Platform.OS,
version: Platform.Version,
apiLevel: Platform.OS === "android" ? Device.platformApiLevel : undefined,
manufacturer: Platform.OS === "android" ? Device.manufacturer : undefined,
brand: Platform.OS === "android" ? Device.brand : undefined,
};
// console.debug("[ExpoPasskey] Platform details:", platformDetails);
// console.debug("[ExpoPasskey] Biometric hardware support:", isSupported);
// console.debug("[ExpoPasskey] Biometric enrollment status:", isEnrolled);
// console.debug("[ExpoPasskey] Available auth types:", availableTypes);
// Check if the platform meets version requirements using the utility function
if (!isSupportedPlatform(Platform.OS, Platform.OS === "android"
? platformDetails.apiLevel || Platform.Version
: Platform.Version)) {
// console.debug(
// "[ExpoPasskey] Platform/version not supported:",
// Platform.OS,
// Platform.Version,
// );
return {
isSupported: false,
isEnrolled: false,
availableTypes: [],
authenticationType: "None",
error: Platform.OS === "ios"
? "iOS 16 or higher required for passkey support"
: "Android 9 (API 28) or higher required for passkey support",
platformDetails,
};
}
// Check if native WebAuthn implementation is available
// This is the final check after platform requirements are met
let nativePasskeySupported = false;
try {
// Use the new ExpoPasskeyModule directly
nativePasskeySupported = ExpoPasskeyModule.isPasskeySupported();
}
catch (error) {
console.warn("[ExpoPasskey] Error checking native passkey support:", error);
// Continue with false result
}
// If native module check fails, we still have incomplete information
if (!nativePasskeySupported) {
return {
isSupported: false,
isEnrolled: isEnrolled,
availableTypes,
authenticationType: getBiometricType(availableTypes),
error: "Native passkey module not available or not supported",
platformDetails,
};
}
// Determine the biometric type for user-friendly display
const authenticationType = getBiometricType(availableTypes);
return {
isSupported: isSupported && nativePasskeySupported,
isEnrolled,
availableTypes,
authenticationType,
error: null,
platformDetails,
};
}
catch (error) {
console.error("[ExpoPasskey] Error in checkBiometricSupport:", error);
const { Platform } = getModules();
return {
isSupported: false,
isEnrolled: false,
availableTypes: [],
authenticationType: "None",
error: error instanceof Error
? error.message
: "Unknown error checking biometric support",
platformDetails: {
platform: Platform.OS,
version: Platform.Version,
},
};
}
}
/**
* Determines the type of biometric authentication available
* @param types Available authentication types
* @returns Human-readable biometric type
*/
export function getBiometricType(types) {
const { LocalAuthentication, Platform } = getModules();
if (Platform.OS === "ios") {
return types.includes(LocalAuthentication.AuthenticationType.FACIAL_RECOGNITION)
? "Face ID"
: types.includes(LocalAuthentication.AuthenticationType.FINGERPRINT)
? "Touch ID"
: "None";
}
if (Platform.OS === "android") {
if (types.includes(LocalAuthentication.AuthenticationType.FINGERPRINT)) {
return "Fingerprint";
}
if (types.includes(LocalAuthentication.AuthenticationType.FACIAL_RECOGNITION)) {
return "Face Unlock";
}
if (types.includes(LocalAuthentication.AuthenticationType.IRIS)) {
return "Iris";
}
// Additional check - if we have types but none match specific ones,
// assume it's some form of biometric
if (types.length > 0) {
return "Biometric";
}
}
return "Biometric";
}
/**
* Checks if passkeys are supported on the current device
* This checks both platform requirements and native module availability
*
* @returns Promise resolving to true if passkeys are supported
*/
export async function isPasskeySupported() {
try {
const { Platform, Device } = getModules();
// First check if biometrics are supported and enrolled
let biometricSupport;
try {
biometricSupport = await checkBiometricSupport();
}
catch (error) {
console.error("[ExpoPasskey] Error checking biometric support:", error);
return false;
}
// Check for basic biometric hardware and enrollment
if (!biometricSupport.isSupported) {
return false;
}
if (!biometricSupport.isEnrolled) {
return false;
}
// Check platform version requirements using the utility function
const platformVersion = Platform.OS === "android"
? Device.platformApiLevel || Platform.Version
: Platform.Version;
if (!isSupportedPlatform(Platform.OS, platformVersion)) {
return false;
}
// Final check: native WebAuthn module
try {
// Use the new ExpoPasskeyModule directly
const nativeSupported = ExpoPasskeyModule.isPasskeySupported();
return nativeSupported;
}
catch (error) {
console.error("[ExpoPasskey] Error checking native module:", error);
return false;
}
}
catch (error) {
console.error("[ExpoPasskey] Unexpected error in isPasskeySupported:", error);
return false;
}
}
//# sourceMappingURL=biometrics.js.map