@passkey-fas/webauthn-sdk
Version:
Official JavaScript SDK for FaS (FIDO2 as Service) Platform - Easy passwordless authentication integration
3 lines (2 loc) • 5.74 kB
JavaScript
import{startRegistration as e,startAuthentication as t}from"@simplewebauthn/browser";class i{constructor(e){if(!e.clientId&&!e.projectId)throw new Error("Either clientId or projectId is required");if(this.isPublicApi=!!e.projectId,this.isPublicApi)this.projectId=e.projectId,this.apiBase=e.apiBase||"/api/public",this.useProxy=!1,console.log("🌐 FaS SDK initialized in Public API mode");else{if(this.clientId=e.clientId,this.clientSecret=e.clientSecret,this.useProxy=e.useProxy||!1,this.apiBase=e.apiBase||"https://fas-l450.onrender.com/api/webauthn",!this.useProxy&&!this.clientSecret)throw new Error("clientSecret is required when not using proxy mode. Set useProxy: true to use backend proxy.");console.log("🔒 FaS SDK initialized in Private API mode")}this.timeout=e.timeout||6e4}async makeRequest(e,t={}){const i=new AbortController,o=setTimeout(()=>i.abort(),this.timeout);try{let r={"Content-Type":"application/json"};this.isPublicApi?(r["X-Project-ID"]=this.projectId,console.log("🌐 Public API request with Project ID:",this.projectId)):this.useProxy||(r["X-Client-ID"]=this.clientId,r["X-Client-Secret"]=this.clientSecret,console.log("🔒 Private API request with Client ID:",this.clientId));let n=`${this.apiBase}${e}`;if(this.isPublicApi){const t={"/public-register/start":"/register/start","/public-register/finish":"/register/finish","/public-authenticate/start":"/authenticate/start","/public-authenticate/finish":"/authenticate/finish"}[e];t&&(n=`${this.apiBase}${t}`)}else if(this.useProxy){const t={"/public-register/start":"/register/start","/public-register/finish":"/register/finish","/public-authenticate/start":"/authenticate/start","/public-authenticate/finish":"/authenticate/finish"}[e];t&&(n=`${this.apiBase}${t}`)}console.log("📡 Making request to:",n);const s=await fetch(n,{method:"POST",headers:r,body:JSON.stringify(t),signal:i.signal});if(clearTimeout(o),!s.ok){const e=await s.json().catch(()=>({}));throw new Error(e.error||`HTTP ${s.status}: ${s.statusText}`)}return s.json()}catch(e){if(clearTimeout(o),"AbortError"===e.name)throw new Error("Request timeout");throw e}}async registerPasskey(t,i){if(!t)throw new Error("Email is required");try{console.log("🔑 Bắt đầu đăng ký passkey cho:",t);const o=await this.makeRequest("/public-register/start",{email:t,fullname:i||t});console.log("📋 Đã nhận tùy chọn đăng ký");const r=await e(o);console.log("✅ Hoàn tất đăng ký WebAuthn");const n=await this.makeRequest("/public-register/finish",{email:t,credential:r});return console.log("🎉 Xác minh đăng ký thành công"),n.token&&this.setAuthToken(n.token),{success:!0,user:n.user,token:n.token}}catch(e){throw console.error("❌ Đăng ký thất bại:",e),this.handleWebAuthnError(e)}}async authenticatePasskey(e){if(!e)throw new Error("Email is required");try{console.log("🔐 Starting passkey authentication for:",e);const i=await this.makeRequest("/public-authenticate/start",{email:e});console.log("📋 Authentication options received");const o=await t(i);console.log("✅ WebAuthn authentication completed");const r=await this.makeRequest("/public-authenticate/finish",{email:e,credential:o});return console.log("🎉 Authentication verified successfully"),r.token&&this.setAuthToken(r.token),{success:!0,user:r.user,token:r.token}}catch(e){throw console.error("❌ Authentication failed:",e),this.handleWebAuthnError(e)}}async passwordlessLogin(){try{console.log("🚀 Starting passwordless authentication");const e=await this.makeRequest("/public-authenticate/start",{email:null});console.log("📋 Passwordless options received");const i=await t(e);console.log("✅ Passwordless authentication completed");const o=await this.makeRequest("/public-authenticate/finish",{email:null,credential:i});return console.log("🎉 Passwordless authentication verified"),o.token&&this.setAuthToken(o.token),{success:!0,user:o.user,token:o.token}}catch(e){throw console.error("❌ Passwordless authentication failed:",e),this.handleWebAuthnError(e)}}getAuthToken(){return"undefined"!=typeof localStorage?localStorage.getItem("fas_auth_token"):null}setAuthToken(e){"undefined"!=typeof localStorage&&localStorage.setItem("fas_auth_token",e)}logout(){"undefined"!=typeof localStorage&&localStorage.removeItem("fas_auth_token")}isAuthenticated(){const e=this.getAuthToken();if(!e)return!1;try{return 1e3*JSON.parse(atob(e.split(".")[1])).exp>Date.now()}catch{return!1}}static isWebAuthnSupported(){return!!("undefined"!=typeof window&&window.navigator&&window.navigator.credentials&&window.navigator.credentials.create)}static getBrowserSupport(){const e={webauthn:i.isWebAuthnSupported(),conditionalUI:!1,residentKeys:!1,userVerification:!1};return e.webauthn&&"undefined"!=typeof window&&(e.conditionalUI=void 0!==window.PublicKeyCredential?.isConditionalMediationAvailable,e.residentKeys=!0,e.userVerification=!0),e}handleWebAuthnError(e){const t={NotAllowedError:"User cancelled the operation or timeout occurred",NotSupportedError:"WebAuthn is not supported on this device/browser",InvalidStateError:"A passkey already exists for this account on this device",SecurityError:"Security error - ensure you are using HTTPS",UnknownError:"An unknown error occurred during authentication",AbortError:"Operation was aborted by user or system",ConstraintError:"Constraint error occurred",NotReadableError:"Authenticator data is not readable",DataError:"Data provided is invalid"}[e.name]||e.message||"Unknown authentication error",i=new Error(t);return i.originalError=e,i.code=e.name||"UnknownError",i}}"undefined"!=typeof module&&module.exports&&(module.exports=i),"undefined"!=typeof window&&(window.FaSSDK=i);export{i as default};
//# sourceMappingURL=index.esm.js.map