UNPKG

@wauth/sdk

Version:
892 lines 37.4 kB
import PocketBase, {} from "pocketbase"; import Arweave from "arweave"; import Transaction from "arweave/web/lib/transaction"; import {} from "arconnect"; import { DataItem } from "@dha-team/arbundles"; import axios from "axios"; import base64url from "base64url"; import { WAUTH_VERSION } from "./version"; import { createModal, createModalContainer } from "./modal-helper"; import { dryrun } from "@permaweb/aoconnect"; // HTML Sanitization Utility class HTMLSanitizer { /** * Escapes HTML entities to prevent XSS attacks * @param text - The text to escape * @returns Escaped text safe for innerHTML */ static escapeHTML(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } /** * Creates a safe HTML string with basic formatting * @param text - The text content * @param allowedTags - Array of allowed HTML tags (default: ['br', 'strong', 'em']) * @returns Sanitized HTML string */ static sanitizeHTML(text, allowedTags = ['br', 'strong', 'em']) { // First escape all HTML let sanitized = this.escapeHTML(text); // Then allow specific tags back in a controlled way allowedTags.forEach(tag => { const escapedOpenTag = `&lt;${tag}&gt;`; const escapedCloseTag = `&lt;/${tag}&gt;`; const openTagRegex = new RegExp(escapedOpenTag, 'gi'); const closeTagRegex = new RegExp(escapedCloseTag, 'gi'); sanitized = sanitized.replace(openTagRegex, `<${tag}>`); sanitized = sanitized.replace(closeTagRegex, `</${tag}>`); }); return sanitized; } /** * Safely sets innerHTML with sanitization * @param element - The DOM element * @param html - The HTML content to set * @param allowedTags - Array of allowed HTML tags */ static safeSetInnerHTML(element, html, allowedTags) { element.innerHTML = this.sanitizeHTML(html, allowedTags); } /** * Creates a safe link element * @param href - The URL (will be validated) * @param text - The link text (will be escaped) * @param target - Link target (default: '_blank') * @returns HTMLAnchorElement */ static createSafeLink(href, text, target = '_blank') { const link = document.createElement('a'); // Validate URL - only allow http/https try { const url = new URL(href); if (url.protocol !== 'http:' && url.protocol !== 'https:') { throw new Error('Invalid protocol'); } link.href = url.toString(); } catch { // If URL is invalid, don't set href link.href = '#'; console.warn('Invalid URL provided to createSafeLink:', href); } link.textContent = text; // textContent automatically escapes link.target = target; // Security attributes for external links if (target === '_blank') { link.rel = 'noopener noreferrer'; } return link; } } // Export HTMLSanitizer for external use export { HTMLSanitizer }; // Frontend password encryption utilities class PasswordEncryption { static publicKey = null; static async getBackendPublicKey(backendUrl) { if (this.publicKey) { return this.publicKey; } try { const response = await fetch(`${backendUrl}/public-key`); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); if (data.error) { throw new Error(`Backend error: ${data.error}`); } if (!data.publicKey) { throw new Error("No public key in response"); } this.publicKey = await crypto.subtle.importKey("jwk", data.publicKey, { name: "RSA-OAEP", hash: "SHA-256", }, false, ["encrypt"]); return this.publicKey; } catch (error) { console.error("Failed to get backend public key:", error); throw new Error("Failed to initialize password encryption: " + error.message); } } static async encryptPassword(password, backendUrl) { try { const publicKey = await this.getBackendPublicKey(backendUrl); // Generate nonce and timestamp for anti-replay protection const nonce = crypto.randomUUID(); const timestamp = Date.now(); const payload = { password, nonce, timestamp }; const encrypted = await crypto.subtle.encrypt({ name: "RSA-OAEP" }, publicKey, new TextEncoder().encode(JSON.stringify(payload))); return btoa(String.fromCharCode(...new Uint8Array(encrypted))); } catch (error) { console.error("Password encryption failed:", error); throw new Error("Failed to encrypt password: " + error.message); } } } export var WAuthProviders; (function (WAuthProviders) { WAuthProviders["Google"] = "google"; WAuthProviders["Github"] = "github"; WAuthProviders["Discord"] = "discord"; WAuthProviders["X"] = "twitter"; })(WAuthProviders || (WAuthProviders = {})); export var WalletActions; (function (WalletActions) { WalletActions["SIGN"] = "sign"; WalletActions["ENCRYPT"] = "encrypt"; WalletActions["DECRYPT"] = "decrypt"; WalletActions["DISPATCH"] = "dispatch"; WalletActions["SIGN_DATA_ITEM"] = "signDataItem"; WalletActions["SIGNATURE"] = "signature"; })(WalletActions || (WalletActions = {})); export class WauthSigner { wauth; publicKey; ownerLength = 512; signatureLength = 512; signatureType = 1; constructor(wauth) { this.wauth = wauth; // Initialize publicKey as empty buffer, will be set in setPublicKey this.publicKey = Buffer.alloc(0); // Immediately set the public key this.setPublicKey().catch(error => { console.error("Failed to set initial public key:", error); throw error; }); } async setPublicKey() { try { const arOwner = await this.wauth.getActivePublicKey(); this.publicKey = base64url.toBuffer(arOwner); if (this.publicKey.length !== this.ownerLength) { throw new Error(`Public key length mismatch. Expected ${this.ownerLength} bytes but got ${this.publicKey.length} bytes`); } } catch (error) { console.error("Failed to set public key:", error); throw error; } } async sign(message) { try { if (!this.publicKey.length || this.publicKey.length !== this.ownerLength) { await this.setPublicKey(); } const signature = await this.wauth.signature(message); const buf = new Uint8Array(Object.values(signature).map((v) => +v)); if (buf.length !== this.signatureLength) { throw new Error(`Signature length mismatch. Expected ${this.signatureLength} bytes but got ${buf.length} bytes`); } return buf; } catch (error) { console.error("Sign operation failed:", error); throw error; } } static async verify(pk, message, signature) { try { // Convert Buffer to string if needed const publicKeyString = Buffer.isBuffer(pk) ? pk.toString() : pk; // Import the public key for verification const publicJWK = { e: "AQAB", ext: true, kty: "RSA", n: publicKeyString }; // Import public key for verification const verificationKey = await crypto.subtle.importKey("jwk", publicJWK, { name: "RSA-PSS", hash: "SHA-256" }, false, ["verify"]); // Verify the signature const result = await crypto.subtle.verify({ name: "RSA-PSS", saltLength: 32 }, verificationKey, signature, message); return result; } catch (error) { console.error("Verify operation failed:", error?.message || "Unknown error"); return false; } } } async function getTokenDetails(token) { const res = await dryrun({ process: token, tags: [{ name: "Action", value: "Info" }] }); if (res.Messages.length < 1) throw new Error("No info message found"); const msg = res.Messages[0]; const tags = msg.Tags; // transform tags {name,value}[] to {name:value} const tagsObj = tags.reduce((acc, tag) => { acc[tag.name] = tag.value; return acc; }, {}); return tagsObj; } export class WAuth { static devUrl = "http://localhost:8090"; static devBackendUrl = "http://localhost:8091"; static prodUrl = "https://wauth.arnode.asia"; static prodBackendUrl = "https://wauth-backend.arnode.asia"; pb; authData; wallet; authRecord; backendUrl; static version = WAUTH_VERSION; version = WAuth.version; authDataListeners = []; sessionPassword = null; // Store decrypted password in memory only sessionKey = null; // Key for local session encryption async initializeSessionKey() { if (this.sessionKey) return this.sessionKey; // Try to load existing session key const storedKey = sessionStorage.getItem('wauth_session_key'); if (storedKey) { try { const keyData = JSON.parse(storedKey); this.sessionKey = await crypto.subtle.importKey('jwk', keyData, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']); return this.sessionKey; } catch (error) { // If key loading fails, generate a new one sessionStorage.removeItem('wauth_session_key'); sessionStorage.removeItem('wauth_encrypted_password'); } } // Generate new session key this.sessionKey = await crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']); // Store the key for the session const exportedKey = await crypto.subtle.exportKey('jwk', this.sessionKey); sessionStorage.setItem('wauth_session_key', JSON.stringify(exportedKey)); return this.sessionKey; } async storePasswordInSession(password) { if (typeof window === 'undefined' || !password) return; try { const sessionKey = await this.initializeSessionKey(); const encoder = new TextEncoder(); const data = encoder.encode(password); // Generate a random IV for each encryption const iv = crypto.getRandomValues(new Uint8Array(12)); const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, sessionKey, data); // Store encrypted password with IV const encryptedData = { encrypted: Array.from(new Uint8Array(encrypted)), iv: Array.from(iv) }; sessionStorage.setItem('wauth_encrypted_password', JSON.stringify(encryptedData)); } catch (error) { console.error("Failed to store password in session:", error); } } async loadPasswordFromSession() { if (typeof window === 'undefined') return null; try { const storedData = sessionStorage.getItem('wauth_encrypted_password'); if (!storedData) return null; const { encrypted, iv } = JSON.parse(storedData); const sessionKey = await this.initializeSessionKey(); const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: new Uint8Array(iv) }, sessionKey, new Uint8Array(encrypted)); const decoder = new TextDecoder(); return decoder.decode(decrypted); } catch (error) { console.error("Failed to load password from session:", error); // Clear invalid data sessionStorage.removeItem('wauth_encrypted_password'); return null; } } clearSessionPassword() { if (typeof window !== 'undefined') { sessionStorage.removeItem('wauth_encrypted_password'); sessionStorage.removeItem('wauth_session_key'); } this.sessionPassword = null; this.sessionKey = null; } clearAllAuthData() { // Clear session password and storage this.clearSessionPassword(); // Clear PocketBase auth data this.pb.authStore.clear(); // Clear additional localStorage items if any if (typeof window !== 'undefined') { // Clear any PocketBase auth data from localStorage localStorage.removeItem('pocketbase_auth'); // Clear any other wauth-related items Object.keys(localStorage).forEach(key => { if (key.startsWith('wauth_')) { localStorage.removeItem(key); } }); } // Reset instance variables this.authData = null; this.wallet = null; this.authRecord = null; console.log("[wauth] Cleared all authentication data"); } constructor({ dev = false, url, backendUrl }) { this.pb = new PocketBase(url || (dev ? WAuth.devUrl : WAuth.prodUrl)); this.backendUrl = backendUrl || (dev ? WAuth.devBackendUrl : WAuth.prodBackendUrl); this.authData = null; this.wallet = null; this.authRecord = null; this.sessionPassword = null; this.sessionKey = null; // Load password from session storage on initialization if (typeof window !== 'undefined') { this.loadPasswordFromSession().then(async (password) => { if (password) { this.sessionPassword = password; // Try to load wallet if user is already authenticated if (this.isLoggedIn()) { try { this.wallet = await this.getWallet(); } catch (error) { console.warn("Could not load wallet after session restore:", error); } } } }).catch(error => { console.error("Failed to load session password:", error); }); } this.pb.authStore.onChange(async (token, record) => { if (!record || !localStorage.getItem("pocketbase_auth")) { return; } this.authRecord = record; this.authData = this.getAuthData(); // Only try to get wallet if we have a session password // This prevents the race condition during connect() if (this.sessionPassword) { try { this.wallet = await this.getWallet(); } catch (error) { console.warn("[wauth] Could not get wallet in auth change handler:", error); } } this.authDataListeners.forEach(listener => listener(this.getAuthData())); }, true); } onAuthDataChange(callback) { this.authDataListeners.push(callback); if (this.authData) { callback(this.authData); } } async runAction(action, payload = {}) { // make sure the user is logged in if (!this.isLoggedIn()) throw new Error("[wauth] Not logged in"); // make sure the wallet is connected if (!this.wallet) this.wallet = await this.getWallet(); if (!this.wallet) throw new Error("[wauth] No wallet found"); // Helper to show modal and await result const showModal = (type, payload) => { return new Promise((resolve) => { this.createModal(type, payload, (result) => { resolve(result); }); }); }; switch (action) { case WalletActions.SIGN: // check for Action=Transfer Tag and ask user for approval if (payload && payload.transaction && payload.transaction.tags) { const actionTag = payload.transaction.tags.find((tag) => tag.name === "Action"); if (actionTag?.value === "Transfer") { // Show modal and await user confirmation const result = await showModal("confirm-tx", { transaction: payload.transaction }); if (!result.proceed) { throw new Error("[wauth] Transaction cancelled by user"); } } } break; case WalletActions.SIGN_DATA_ITEM: // check for Action=Transfer Tag and ask user for approval if (payload && payload.dataItem && payload.dataItem.tags) { const actionTag = payload.dataItem.tags.find((tag) => tag.name === "Action"); if (actionTag?.value === "Transfer") { // Show modal and await user confirmation const result = await showModal("confirm-tx", { dataItem: payload.dataItem }); if (!result.proceed) { throw new Error("[wauth] Transaction cancelled by user"); } } } break; } // Encrypt session password for backend if (!this.sessionPassword) { throw new Error("Session password not available - please reconnect"); } const encryptedPassword = await PasswordEncryption.encryptPassword(this.sessionPassword, this.backendUrl); // send the action, payload, and encrypted password to the backend const res = await axios.post(`${this.backendUrl}/wallet-action`, { action, payload, encryptedPassword }, { headers: { "Content-Type": "application/json", "Accept": "application/json", "Authorization": `Bearer ${this.getAuthToken()}` }, responseType: 'json' }); return res.data; } // There can be 2 types of modals: // 1. Transaction verification- for when the user is about to transfer tokens and needs user confirmation, // this modal would have info text, amount to be transfered, and proceed/cancel buttons // 2. Password input modal- either when user is connecting for the first time (ask for password and confirm password) // or when they already have an account and just logging in (ask for password), // this will be send in an encrypted way to the backend for use with decoding JWK async createModal(type, payload = {}, callback) { // if type is confirm-tx, check payload.transaction or payload.dataItem and tell the user that some tokens are being transferred and its details, and ask for confirmation // if type is password-new, ask for password and confirm password // if type is password-existing, ask for password and return it // based on the users actions, call the callback with the result const container = createModalContainer(); // Create modal immediately with current payload const modal = createModal(type, payload, (result) => { // Remove the modal container from the DOM after callback if (container.parentNode) { container.parentNode.removeChild(container); } callback(result); }); container.appendChild(modal); // Add powered by element as sibling to modal content const powered = document.createElement("div"); powered.className = "wauth-powered"; // Use secure link creation instead of innerHTML const poweredLink = HTMLSanitizer.createSafeLink("https://wauth_subspace.ar.io", "powered by wauth", "_blank"); powered.appendChild(poweredLink); powered.style.position = "absolute"; powered.style.bottom = "20px"; powered.style.textAlign = "center"; powered.style.fontSize = "0.9rem"; powered.style.color = "rgba(255, 255, 255, 0.5)"; powered.style.opacity = "0.8"; powered.style.letterSpacing = "0.5px"; powered.style.fontWeight = "500"; powered.style.left = "0"; powered.style.right = "0"; powered.style.transition = "all 0.2s ease"; powered.style.textShadow = "0 1px 2px rgba(0, 0, 0, 0.5)"; // Style the link directly poweredLink.style.color = "inherit"; poweredLink.style.textDecoration = "none"; poweredLink.style.transition = "all 0.2s ease"; poweredLink.style.borderRadius = "8px"; poweredLink.style.padding = "6px 12px"; poweredLink.style.background = "rgba(255, 255, 255, 0.05)"; poweredLink.style.backdropFilter = "blur(10px)"; poweredLink.style.border = "1px solid rgba(255, 255, 255, 0.1)"; poweredLink.onmouseover = () => { poweredLink.style.color = "rgba(255, 255, 255, 0.9)"; poweredLink.style.background = "rgba(255, 255, 255, 0.1)"; poweredLink.style.borderColor = "rgba(255, 255, 255, 0.2)"; poweredLink.style.transform = "translateY(-1px)"; poweredLink.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)"; }; poweredLink.onmouseleave = () => { poweredLink.style.color = "rgba(255, 255, 255, 0.5)"; poweredLink.style.background = "rgba(255, 255, 255, 0.05)"; poweredLink.style.borderColor = "rgba(255, 255, 255, 0.1)"; poweredLink.style.transform = "translateY(0)"; poweredLink.style.boxShadow = "none"; }; container.appendChild(powered); document.body.appendChild(container); // Now fetch token details asynchronously and update the modal if (type === "confirm-tx") { const data = payload.transaction || payload.dataItem; if (data && data.target) { try { const tokenDetails = await getTokenDetails(data.target); // Update the modal with token details const enhancedPayload = { ...payload, tokenDetails }; const updatedModal = createModal(type, enhancedPayload, (result) => { // Remove the modal container from the DOM after callback if (container.parentNode) { container.parentNode.removeChild(container); } callback(result); }); // Replace the existing modal content (keep powered by element) container.replaceChild(updatedModal, modal); } catch (error) { console.warn("[wauth] Failed to fetch token details:", error); // Modal continues to work without token details } } } } async connect({ provider, scopes }) { if (!Object.values(WAuthProviders).includes(provider)) throw new Error(`Invalid provider: ${provider}. Valid providers are: ${Object.values(WAuthProviders).join(", ")}`); try { this.authData = await this.pb.collection("users").authWithOAuth2({ provider, scopes }); this.authDataListeners.forEach(listener => listener(this.getAuthData())); } catch (e) { console.error("[wauth] error logging in,", e); return null; } if (!this.isLoggedIn()) return null; // Small delay to ensure OAuth process is fully completed await new Promise(resolve => setTimeout(resolve, 100)); // Verify backend connectivity try { const response = await fetch(`${this.backendUrl}/`); if (!response.ok) { throw new Error(`Backend not accessible: ${response.status}`); } } catch (error) { console.error("Backend connectivity check failed:", error); throw new Error("Cannot connect to backend server. Please try again later."); } try { // Check if user has an existing wallet const existingWallet = await this.checkExistingWallet(); if (existingWallet) { // Existing user - ask for password to decrypt wallet using modal let passwordResult; let attempts = 0; const maxAttempts = 3; let errorMessage = ""; do { passwordResult = await new Promise((resolve) => { this.createModal("password-existing", { errorMessage }, resolve); }); if (!passwordResult.proceed || !passwordResult.password) { // User cancelled - clear all authentication data this.clearAllAuthData(); throw new Error("Password required to access existing wallet"); } // Verify password before storing it const isValidPassword = await this.verifyPassword(passwordResult.password); if (isValidPassword) { break; // Password is valid, exit loop } attempts++; if (attempts >= maxAttempts) { throw new Error("Too many failed password attempts. Please try again later."); } // Set error message for next modal display errorMessage = `Invalid password. Please try again. (${maxAttempts - attempts} attempts remaining)`; } while (attempts < maxAttempts); // Store password in session for future use this.sessionPassword = passwordResult.password; await this.storePasswordInSession(passwordResult.password); // Get wallet (password is already verified) this.wallet = await this.getWallet(); } else { // New user - ask for password to create wallet using modal const result = await new Promise((resolve) => { this.createModal("password-new", {}, resolve); }); if (!result.proceed || !result.password) { // User cancelled - clear all authentication data this.clearAllAuthData(); throw new Error("Password required to create wallet"); } // Store password in session this.sessionPassword = result.password; await this.storePasswordInSession(result.password); // Create new wallet this.wallet = await this.getWallet(); } if (!this.wallet) { console.error("[wauth] no wallet found"); throw new Error("Failed to create or access wallet"); } } catch (e) { console.error("[wauth]", e); // Clear all authentication data on error this.clearAllAuthData(); throw e; } return this.getAuthData(); } async checkExistingWallet() { try { // Ensure we have a user record if (!this.pb.authStore.record?.id) { return false; } const userId = this.pb.authStore.record.id; // Use getList instead of getFirstListItem to avoid 404 when no records exist const result = await this.pb.collection("wallets").getList(1, 1, { filter: `user.id = "${userId}"` }); return result.totalItems > 0; } catch (e) { console.error("Error checking for existing wallet:", e); return false; } } async verifyPassword(password) { try { // Encrypt password for backend const encryptedPassword = await PasswordEncryption.encryptPassword(password, this.backendUrl); // Call verification endpoint const response = await fetch(`${this.backendUrl}/verify-password`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'encrypted-password': encryptedPassword, 'Authorization': `Bearer ${this.getAuthToken()}` } }); if (!response.ok) { return false; } const result = await response.json(); return result.valid === true; } catch (error) { console.error("Password verification failed:", error); return false; } } async addConnectedWallet(address, pkey, signature) { if (!this.isLoggedIn()) throw new Error("Not logged in"); if (!this.wallet) this.wallet = await this.getWallet(); if (!this.wallet) throw new Error("No wallet found"); const token = this.getAuthToken(); if (!token) throw new Error("No auth token"); const res = await fetch(`${this.backendUrl}/connect-wallet`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` }, body: JSON.stringify({ address, pkey, signature }) }); const data = await res.json(); return data; } isLoggedIn() { return this.pb.authStore.isValid; } async getActiveAddress() { if (!this.isLoggedIn()) throw new Error("Not logged in"); if (!this.wallet) this.wallet = await this.getWallet(); return this.wallet?.address || ""; } async getActivePublicKey() { if (!this.isLoggedIn()) throw new Error("Not logged in"); if (!this.wallet) this.wallet = await this.getWallet(); return this.wallet?.public_key || ""; } async getPermissions() { return ["ACCESS_ADDRESS", "SIGN_TRANSACTION"]; } async getWalletNames() { return { [await this.getActiveAddress()]: "WAuth" }; } async getArweaveConfig() { // TODO: make this configurable const config = { host: "arweave.net", port: 443, protocol: "https", }; return config; } getAuthData() { if (!this.isLoggedIn()) return null; return this.authData; } getAuthToken() { if (!this.isLoggedIn()) return null; if (!this.pb.authStore.token) return null; return this.pb.authStore.token; } async getWallet() { if (!this.isLoggedIn()) { return null; } if (!this.sessionPassword) { throw new Error("Session password not available - please reconnect"); } // Ensure we have a user record if (!this.pb.authStore.record?.id) { throw new Error("User record not available - please log in again"); } const userId = this.pb.authStore.record.id; try { // Use getList instead of getFirstListItem to avoid 404 when no records exist const result = await this.pb.collection("wallets").getList(1, 1, { filter: `user.id = "${userId}"` }); if (result.totalItems > 0) { // Existing wallet found this.wallet = result.items[0]; return this.wallet; } else { // No wallet exists, create one const encryptedPassword = await PasswordEncryption.encryptPassword(this.sessionPassword, this.backendUrl); const encryptedConfirmPassword = await PasswordEncryption.encryptPassword(this.sessionPassword, this.backendUrl); await this.pb.collection("wallets").create({}, { headers: { "encrypted-password": encryptedPassword, "encrypted-confirm-password": encryptedConfirmPassword } }); // Use getList instead of getFirstListItem to avoid 404 if creation failed const createdResult = await this.pb.collection("wallets").getList(1, 1, { filter: `user.id = "${userId}"` }); if (createdResult.totalItems === 0) { throw new Error("Failed to create wallet - no record found after creation"); } this.wallet = createdResult.items[0]; return this.wallet; } } catch (e) { if (`${e}`.includes("autocancelled")) return null; // Check if this is a password-related error if (e.message && e.message.includes("decrypt") || e.message.includes("password")) { this.clearSessionPassword(); // Clear invalid password throw new Error("Invalid password - please reconnect and try again"); } console.error("Error in getWallet:", e.message || e); throw e; // Re-throw to preserve error handling in connect() } } async getConnectedWallets() { const res = await this.pb.collection("connected_wallets").getFullList({ filter: `user.id = "${this.pb.authStore.record?.id}"` }); return res; } async removeConnectedWallet(walletId) { if (!this.isLoggedIn()) throw new Error("Not logged in"); try { // First verify the wallet belongs to the current user const wallet = await this.pb.collection("connected_wallets").getOne(walletId, { filter: `user.id = "${this.pb.authStore.record?.id}"` }); if (!wallet) { throw new Error("Wallet not found or not owned by current user"); } // Delete the wallet record await this.pb.collection("connected_wallets").delete(walletId); return { success: true, walletId }; } catch (error) { console.error("Error removing connected wallet:", error); throw error; } } getAuthRecord() { if (!this.isLoggedIn()) return null; return this.authRecord; } pocketbase() { return this.pb; } async sign(transaction, options) { if (options) console.warn("[wauth] Signature options are not supported yet"); return await this.runAction(WalletActions.SIGN, { transaction: transaction.toJSON() }); } async signature(data, algorithm) { if (algorithm) { console.warn("[wauth] Signature algorithm is not supported and Rsa4096Pss will be used by default"); } return Object.values(await this.runAction(WalletActions.SIGNATURE, { data })); } async signAns104(dataItem) { return await this.runAction(WalletActions.SIGN_DATA_ITEM, { dataItem }); } async signDataItem(dataItem) { return (await this.runAction(WalletActions.SIGN_DATA_ITEM, { dataItem })).raw; } getWauthSigner() { return new WauthSigner(this); } getAoSigner() { if (!this.isLoggedIn()) throw new Error("Not logged in"); if (!this.wallet) throw new Error("No wallet found"); return async (create, createDataItem) => { const { data, tags, target, anchor } = await create({ alg: 'rsa-v1_5-sha256', passthrough: true }); const signedDataItem = await this.signAns104({ data, tags, target, anchor }); const dataItem = new DataItem(Buffer.from(signedDataItem.raw)); return { id: dataItem.id, raw: dataItem.getRaw() }; }; } hasSessionPassword() { return this.sessionPassword !== null; } async refreshWallet() { if (this.isLoggedIn() && this.sessionPassword) { try { this.wallet = await this.getWallet(); } catch (error) { console.error("Failed to refresh wallet:", error); throw error; } } } logout() { this.clearAllAuthData(); } } //# sourceMappingURL=index.js.map