Skip to main content
Glama
iosBiometricAuth.tsโ€ข5.53 kB
import { Tool, ToolResult } from '../types/tool.js'; import { IOSService } from '../services/iosService.js'; const iosService = new IOSService(); export const biometricAuthTool: Tool = { name: 'ios_biometric_auth', description: 'Integrate Touch/Face ID with Lightning operations', inputSchema: { type: 'object', properties: { operation: { type: 'string', enum: ['send_payment', 'open_channel', 'close_channel', 'export_seed'], description: 'Operation requiring biometric auth', default: 'send_payment' }, requireAuth: { type: 'boolean', description: 'Whether to require biometric auth', default: true } } }, execute: async (args: any): Promise<ToolResult> => { try { const result = await iosService.testBiometricAuth(); return { content: [{ type: 'text', text: JSON.stringify({ success: result.success, message: result.message, swiftExample: result.swiftCode, operation: args.operation, requireAuth: args.requireAuth, securityGuidelines: [ 'Always require biometric auth for seed access', 'Use biometrics for high-value payments', 'Provide passcode fallback option', 'Clear biometric data on app reinstall', 'Implement anti-tampering measures', 'Use LAContext evaluation for each operation' ], uiIntegration: ` // SwiftUI biometric-protected payment view import SwiftUI import LocalAuthentication struct SecurePaymentView: View { @State private var isAuthenticated = false @State private var showingError = false @State private var errorMessage = "" let invoice: String let amountSats: Int var body: some View { VStack(spacing: 30) { // Payment details VStack(spacing: 16) { Image(systemName: "bolt.circle.fill") .font(.system(size: 60)) .foregroundColor(.orange) Text("Confirm Payment") .font(.title) .fontWeight(.semibold) Text("\\(amountSats) sats") .font(.title2) .foregroundColor(.secondary) } // Biometric prompt if !isAuthenticated { VStack(spacing: 20) { Image(systemName: biometricIcon) .font(.system(size: 50)) .foregroundColor(.blue) Text("Authenticate to send payment") .font(.headline) Button(action: authenticate) { Label("Authenticate", systemImage: biometricIcon) .frame(maxWidth: .infinity) } .buttonStyle(.borderedProminent) .controlSize(.large) } } else { // Payment in progress VStack(spacing: 16) { ProgressView() .scaleEffect(1.5) Text("Sending payment...") .font(.headline) } .onAppear { sendPayment() } } } .padding() .alert("Authentication Failed", isPresented: $showingError) { Button("OK") { } } message: { Text(errorMessage) } } var biometricIcon: String { let biometricType = BiometricLightningAuth.getBiometricType() switch biometricType { case .faceID: return "faceid" case .touchID: return "touchid" default: return "lock" } } func authenticate() { Task { let authenticated = await BiometricLightningAuth.authenticateForPayment( amount: UInt64(amountSats) ) if authenticated { withAnimation { isAuthenticated = true } } else { errorMessage = "Authentication failed. Please try again." showingError = true } } } func sendPayment() { Task { do { // Send payment via LDK try await LDKManager.shared.payInvoice( invoice: invoice, maxFeeSats: UInt64(amountSats) / 100 // 1% max fee ) // Navigate to success view await MainActor.run { // Show success } } catch { await MainActor.run { errorMessage = "Payment failed: \\(error.localizedDescription)" showingError = true } } } } }`.trim() }, null, 2) }] }; } catch (error) { return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : 'Unknown error' }, null, 2) }], isError: true }; } } };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/StevenGeller/ldk-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server