@passwordless-id/webauthn
Advanced tools
Comparing version 0.0.5 to 0.0.6
@@ -89,8 +89,8 @@ import * as webauthn from '../../dist/webauthn.min.js' | ||
parseAuthData(authData) { | ||
return webauthn.parseAuthenticatorData(authData) | ||
return webauthn.parseAuthenticatorBase64(authData) | ||
}, | ||
parseClientData(clientData) { | ||
return JSON.parse(window.atob(clientData)) | ||
return webauthn.parseClientBase64(clientData) | ||
} | ||
} | ||
}) |
@@ -1,2 +0,2 @@ | ||
var B=Object.defineProperty;var O=(e,a)=>{for(var r in a)B(e,r,{get:a[r],enumerable:!0})};function F(e){return Uint8Array.from(e,a=>a.charCodeAt(0)).buffer}function f(e){return String.fromCharCode(...new Uint8Array(e))}function b(e){return e.match(/^[a-zA-Z0-9\-_]+=*$/)!==null}function c(e){return btoa(f(e)).replaceAll("+","-").replaceAll("/","_")}function o(e){return e=e.replaceAll("-","+").replaceAll("_","/"),F(atob(e))}async function y(e){return await window.crypto.subtle.digest("SHA-256",e)}function p(e){return[...new Uint8Array(e)].map(a=>a.toString(16).padStart(2,"0")).join("")}function g(e,a){var r=new Uint8Array(e.byteLength+a.byteLength);return r.set(new Uint8Array(e),0),r.set(new Uint8Array(a),e.byteLength),r}var m={};O(m,{"08987058-cadc-4b81-b6e1-30de50dcbe96":()=>E,"12ded745-4bed-47d4-abaa-e713f51d6393":()=>te,"149a2021-8ef6-4133-96b8-81f8d5b7f1f5":()=>U,"1c086528-58d5-f211-823c-356786e36140":()=>Be,"2c0df832-92de-4be1-8412-88a8f074df4a":()=>R,"2fc0579f-8113-47ea-b116-bb5a8db9202a":()=>q,"34f5766d-1536-4a24-9033-0e294e510fb0":()=>re,"3789da91-f943-46bc-95c3-50ea2012f03a":()=>I,"39a5647e-1853-446c-a1f6-a79bae9f5bc7":()=>D,"3b1adb99-0dfe-46fd-90b8-7f7614a4de2a":()=>Y,"3e078ffd-4c54-4586-8baa-a77da113aec5":()=>he,"3e22415d-7fdf-4ea4-8a0c-dd60c4249b9d":()=>Z,"4e768f2c-5fab-48b3-b300-220eb487752b":()=>x,"504d7149-4e4c-3841-4555-55445a677357":()=>ye,"516d3969-5a57-5651-5958-4e7a49434167":()=>z,"54d9fee8-e621-4291-8b18-7157b99c5bec":()=>oe,"6028b017-b1d4-4c02-b4b3-afcdafc96bb2":()=>se,"692db549-7ae5-44d5-a1e5-dd20a493b723":()=>Q,"6d44ba9b-f6ec-2e49-b930-0c8fe920cb73":()=>ue,"73402251-f2a8-4f03-873e-3cb6db604b03":()=>fe,"73bb0cd4-e502-49b8-9c6f-b59445bf720b":()=>V,"77010bd7-212a-4fc9-b236-d2ca5e9d4084":()=>Oe,"820d89ed-d65a-409e-85cb-f73f0578f82a":()=>J,"833b721a-ff5f-4d00-bb2e-bdda3ec01e29":()=>Fe,"83c47309-aabb-4108-8470-8be838b573cb":()=>ne,"85203421-48f9-4355-9bc8-8a53846e5083":()=>M,"88bbd2f0-342a-42e7-9729-dd158be5407a":()=>ae,"8c97a730-3f7b-41a6-87d6-1e9b62bda6f0":()=>pe,"931327dd-c89b-406c-a81e-ed7058ef36c6":()=>T,"9c835346-796b-4c27-8898-d6032f515cc5":()=>C,"9ddd1817-af5a-4672-a2b9-3e3dd95000a9":()=>G,"9f0d8150-baa5-4c00-9299-ad62c8bb4e87":()=>ee,"9f77e279-a6e2-4d58-b700-31e5943c6a98":()=>de,"a1f52be5-dfab-4364-b51c-2bd496b14a56":()=>ge,"aeb6569c-f8fb-4950-ac60-24ca2bbe2e52":()=>X,"b6ede29c-3772-412c-8a78-539c1f4c62d2":()=>W,"b92c3f9a-c014-4056-887f-140a2501163b":()=>ce,"b93fd961-f2e6-462f-b122-82002247de78":()=>j,"bc2fe499-0d8e-4ffe-96f3-94a82840cf8c":()=>Se,"be727034-574a-f799-5c76-0929e0430973":()=>ie,"c1f9a0bc-1dd2-404a-b27f-8e29047a43fd":()=>be,"c5703116-972b-4851-a3e7-ae1259843399":()=>k,"c5ef55ff-ad9a-4b9f-b580-adebafe026d0":()=>K,"cb69481e-8ff7-4039-93ec-0a2729a154a8":()=>me,"d41f5a69-b817-4144-a13c-9ebd6d9254d6":()=>we,"d821a7d4-e97c-4cb6-bd82-4237731fd4be":()=>_,"d8522d9f-575b-4866-88a9-ba99fa02f35b":()=>$,"d91c5288-0ef0-49b7-b8ae-21ca0aa6b3f3":()=>H,default:()=>Ce,"e1a96183-5016-4f24-b55b-e3ae23614cc6":()=>N,"e416201b-afeb-41ca-a03d-2281c28322aa":()=>le,"ee041bce-25e5-4cdb-8f86-897fd6418464":()=>v,"ee882879-721c-4913-9775-3dfcce97072a":()=>Ae,"f8a011f3-8c0a-4d15-8006-17111f9edc7d":()=>L,"fa2b99dc-9e39-4257-8f92-4a30d23c4118":()=>P});var C={name:"Cryptnox FIDO2"},K={name:"YubiKey 5Ci"},D={name:"Vancosys Android Authenticator"},I={name:"NEOWAVE Winkeo FIDO2"},P={name:"YubiKey 5 Series with NFC"},x={name:"Hideez Key 4 FIDO2 SDK"},T={name:"Swissbit iShield FIDO2"},N={name:"ATKey.Pro CTAP2.0"},E={name:"Windows Hello Hardware Authenticator"},H={name:"KEY-ID FIDO2 Authenticator"},v={name:"Feitian ePass FIDO2-NFC Authenticator"},V={name:"YubiKey 5 FIPS Series"},U={name:"Security Key by Yubico with NFC"},Y={name:"GoTrust Idem Key FIDO2 Authenticator"},L={name:"Security Key by Yubico"},R={name:"Feitian FIDO Smart Card"},k={name:"NEOWAVE Badgeo FIDO2"},J={name:"Vancosys iOS Authenticator"},W={name:"Feitian BioPass FIDO2 Plus Authenticator"},M={name:"YubiKey 5Ci FIPS"},_={name:"Hyper FIDO Bio Security Key"},z={name:"SmartDisplayer BobeePass (NFC-BLE FIDO2 Authenticator)"},j={name:"Android Authenticator with SafetyNet Attestation"},q={name:"YubiKey 5 Series with NFC"},G={name:"Windows Hello VBS Hardware Authenticator"},$={name:"YubiKey Bio Series"},Q={name:"HID Crescendo Key"},Z={name:"Feitian iePass FIDO Authenticator"},X={name:"HID Crescendo C2300"},ee={name:"GoTrust Idem Card FIDO2 Authenticator"},te={name:"Feitian AllinOne FIDO2 Authenticator"},ae={name:"Precision InnaIT Key FIDO 2 Level 2 certified"},re={name:"YubiKey 5 Series CTAP2.1 Preview 1 "},ne={name:"YubiKey Bio Series (Enterprise Profile)"},ie={name:"Crayonic KeyVault K1 (USB-NFC-BLE FIDO2 Authenticator)"},ce={name:"Security Key by Yubico"},oe={name:"HID Crescendo Enabled"},se={name:"Windows Hello Software Authenticator"},ue={name:"Security Key by Yubico with NFC"},le={name:"ATKey.Pro CTAP2.1"},de={name:"Hyper FIDO Pro"},fe={name:"uTrust FIDO2 Security Key"},be={name:"YubiKey 5 FIPS Series with NFC"},ye={name:"WiSECURE AuthTron USB FIDO2 Authenticator"},me={name:"YubiKey 5 Series"},Ae={name:"YubiKey 5 Series"},pe={name:"FT-JCOS FIDO Fingerprint Card"},ge={name:"OCTATCO EzFinger2 FIDO2 AUTHENTICATOR"},he={name:"Hideez Key 3 FIDO2"},we={name:"ATKey.Card CTAP2.0"},Se={name:"OCTATCO EzQuant FIDO2 AUTHENTICATOR"},Be={name:"Atos CardOS FIDO2"},Oe={name:"Feitian BioPass FIDO2 Authenticator"},Fe={name:"Feitian ePass FIDO2 Authenticator"},Ce={"9c835346-796b-4c27-8898-d6032f515cc5":C,"c5ef55ff-ad9a-4b9f-b580-adebafe026d0":K,"39a5647e-1853-446c-a1f6-a79bae9f5bc7":D,"3789da91-f943-46bc-95c3-50ea2012f03a":I,"fa2b99dc-9e39-4257-8f92-4a30d23c4118":P,"4e768f2c-5fab-48b3-b300-220eb487752b":x,"931327dd-c89b-406c-a81e-ed7058ef36c6":T,"e1a96183-5016-4f24-b55b-e3ae23614cc6":N,"08987058-cadc-4b81-b6e1-30de50dcbe96":E,"d91c5288-0ef0-49b7-b8ae-21ca0aa6b3f3":H,"ee041bce-25e5-4cdb-8f86-897fd6418464":v,"73bb0cd4-e502-49b8-9c6f-b59445bf720b":V,"149a2021-8ef6-4133-96b8-81f8d5b7f1f5":U,"3b1adb99-0dfe-46fd-90b8-7f7614a4de2a":Y,"f8a011f3-8c0a-4d15-8006-17111f9edc7d":L,"2c0df832-92de-4be1-8412-88a8f074df4a":R,"c5703116-972b-4851-a3e7-ae1259843399":k,"820d89ed-d65a-409e-85cb-f73f0578f82a":J,"b6ede29c-3772-412c-8a78-539c1f4c62d2":W,"85203421-48f9-4355-9bc8-8a53846e5083":M,"d821a7d4-e97c-4cb6-bd82-4237731fd4be":_,"516d3969-5a57-5651-5958-4e7a49434167":z,"b93fd961-f2e6-462f-b122-82002247de78":j,"2fc0579f-8113-47ea-b116-bb5a8db9202a":q,"9ddd1817-af5a-4672-a2b9-3e3dd95000a9":G,"d8522d9f-575b-4866-88a9-ba99fa02f35b":$,"692db549-7ae5-44d5-a1e5-dd20a493b723":Q,"3e22415d-7fdf-4ea4-8a0c-dd60c4249b9d":Z,"aeb6569c-f8fb-4950-ac60-24ca2bbe2e52":X,"9f0d8150-baa5-4c00-9299-ad62c8bb4e87":ee,"12ded745-4bed-47d4-abaa-e713f51d6393":te,"88bbd2f0-342a-42e7-9729-dd158be5407a":ae,"34f5766d-1536-4a24-9033-0e294e510fb0":re,"83c47309-aabb-4108-8470-8be838b573cb":ne,"be727034-574a-f799-5c76-0929e0430973":ie,"b92c3f9a-c014-4056-887f-140a2501163b":ce,"54d9fee8-e621-4291-8b18-7157b99c5bec":oe,"6028b017-b1d4-4c02-b4b3-afcdafc96bb2":se,"6d44ba9b-f6ec-2e49-b930-0c8fe920cb73":ue,"e416201b-afeb-41ca-a03d-2281c28322aa":le,"9f77e279-a6e2-4d58-b700-31e5943c6a98":de,"73402251-f2a8-4f03-873e-3cb6db604b03":fe,"c1f9a0bc-1dd2-404a-b27f-8e29047a43fd":be,"504d7149-4e4c-3841-4555-55445a677357":ye,"cb69481e-8ff7-4039-93ec-0a2729a154a8":me,"ee882879-721c-4913-9775-3dfcce97072a":Ae,"8c97a730-3f7b-41a6-87d6-1e9b62bda6f0":pe,"a1f52be5-dfab-4364-b51c-2bd496b14a56":ge,"3e078ffd-4c54-4586-8baa-a77da113aec5":he,"d41f5a69-b817-4144-a13c-9ebd6d9254d6":we,"bc2fe499-0d8e-4ffe-96f3-94a82840cf8c":Se,"1c086528-58d5-f211-823c-356786e36140":Be,"77010bd7-212a-4fc9-b236-d2ca5e9d4084":Oe,"833b721a-ff5f-4d00-bb2e-bdda3ec01e29":Fe};function A(e){console.debug(e);let a=new DataView(e.slice(32,33)).getUint8(0);console.debug(a);let r={rpIdHash:c(e.slice(0,32)),flags:{userPresent:!!(a&1),userVerified:!!(a&4),backupEligibility:!!(a&8),backupState:!!(a&16),attestedData:!!(a&64),extensionsIncluded:!!(a&128)},counter:new DataView(e.slice(33,37)).getUint32(0,!1)};if(e.byteLength>37){let s=new DataView(e.slice(53,55)).getUint16(0,!1);r={...r,aaguid:d(e),credentialId:c(e.slice(55,55+s)),publicKey:c(e.slice(55+s,e.byteLength))}}return r}function d(e){return Ke(e.slice(37,53))}function Ke(e){let a=p(e);return a=a.substring(0,8)+"-"+a.substring(8,12)+"-"+a.substring(12,16)+"-"+a.substring(16,20)+"-"+a.substring(20,24),a}function w(e){let a=d(e);return(De??m)[a]?.name}var De=null;function He(){return!!window.PublicKeyCredential}async function S(){return await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()}function ve(e){return A(o(e))}async function Pe(e){if(e==="local")return"platform";if(e==="extern")return"cross-platform";if(e!=="both")try{return await S()?"platform":"cross-platform"}catch{return}}function xe(e){switch(e){case-7:return"ES256";case-257:return"RS256";default:throw new Error(`Unknown algorithm code: ${e}`)}}async function Ve(e,a,r){if(r||(r={}),!b(a))throw new Error("Provided challenge is not properly encoded in Base64url");let s={challenge:o(a),rp:{id:window.location.hostname,name:window.location.hostname},user:{id:await y(new TextEncoder().encode(e)),name:e,displayName:e},pubKeyCredParams:[{alg:-7,type:"public-key"},{alg:-257,type:"public-key"}],timeout:r.timeout??6e4,authenticatorSelection:{userVerification:r.userVerification??"required",authenticatorAttachment:await Pe(r.authenticatorType??"auto")},attestation:"direct"};console.debug(s);let i=await navigator.credentials.create({publicKey:s});console.debug(i);let n=i.response;return{username:e,challenge:a,credential:{id:i.id,publicKey:c(n.getPublicKey()),algorithm:xe(i.response.getPublicKeyAlgorithm())},authenticator:{isLocal:i.authenticatorAttachment==="platform",aaguid:d(n.getAuthenticatorData()),name:w(n.getAuthenticatorData()),attestation:r.attestation?c(n.attestationObject):null,clientData:r.attestation?c(n.clientDataJSON):null}}}async function Te(e){if(e==="local")return["internal"];if(e==="extern")return["usb","ble","nfc"];if(e==="both")return["internal","usb","ble","nfc"];try{return await S()?["internal"]:["usb","ble","nfc"]}catch{return["internal","usb","ble","nfc"]}}async function Ue(e,a,r){if(r||(r={}),!b(a))throw new Error("Provided challenge is not properly encoded in Base64url");let s=await Te(r.authenticatorType??"auto"),i={challenge:o(a),rpId:window.location.hostname,allowCredentials:e.map(l=>({id:o(l),type:"public-key",transports:s})),userVerification:r.userVerification??"required",timeout:r.timeout??6e4};console.debug(i);let n=await navigator.credentials.get({publicKey:i});console.debug(n);let u=n.response;return{credentialId:n.id,clientJson:JSON.parse(f(u.clientDataJSON)),clientData:c(u.clientDataJSON),signature:c(u.signature),authenticatorJson:A(u.authenticatorData),authenticatorData:c(u.authenticatorData)}}async function Ye({algorithm:e,publicKey:a,authenticatorData:r,clientData:s,signature:i}){let n=await window.crypto.subtle.importKey("spki",o(a),{name:"RSASSA-PKCS1-v1_5",hash:"SHA-256"},!1,["verify"]);console.debug(n);let u=await y(o(s));console.debug(u);let l=g(o(r),u);return console.debug(l),console.debug(o(i)),await window.crypto.subtle.verify({name:"RSASSA-PKCS1-v1_5",hash:"SHA-256"},n,o(i),l)}export{He as isAvailable,S as isLocalAuthenticator,Ue as login,ve as parseAuthenticatorData,Ve as register,Ye as verify}; | ||
function S(e){return Uint8Array.from(e,t=>t.charCodeAt(0)).buffer}function B(e){return String.fromCharCode(...new Uint8Array(e))}function b(e){return e.match(/^[a-zA-Z0-9\-_]+=*$/)!==null}function c(e){return btoa(B(e)).replaceAll("+","-").replaceAll("/","_")}function s(e){return e=e.replaceAll("-","+").replaceAll("_","/"),S(atob(e))}async function p(e){return await window.crypto.subtle.digest("SHA-256",e)}function y(e){return[...new Uint8Array(e)].map(t=>t.toString(16).padStart(2,"0")).join("")}function m(e,t){var a=new Uint8Array(e.byteLength+t.byteLength);return a.set(new Uint8Array(e),0),a.set(new Uint8Array(t),e.byteLength),a}var g={"9c835346-796b-4c27-8898-d6032f515cc5":{name:"Cryptnox FIDO2"},"c5ef55ff-ad9a-4b9f-b580-adebafe026d0":{name:"YubiKey 5Ci"},"39a5647e-1853-446c-a1f6-a79bae9f5bc7":{name:"Vancosys Android Authenticator"},"3789da91-f943-46bc-95c3-50ea2012f03a":{name:"NEOWAVE Winkeo FIDO2"},"fa2b99dc-9e39-4257-8f92-4a30d23c4118":{name:"YubiKey 5 Series with NFC"},"4e768f2c-5fab-48b3-b300-220eb487752b":{name:"Hideez Key 4 FIDO2 SDK"},"931327dd-c89b-406c-a81e-ed7058ef36c6":{name:"Swissbit iShield FIDO2"},"e1a96183-5016-4f24-b55b-e3ae23614cc6":{name:"ATKey.Pro CTAP2.0"},"08987058-cadc-4b81-b6e1-30de50dcbe96":{name:"Windows Hello Hardware Authenticator"},"d91c5288-0ef0-49b7-b8ae-21ca0aa6b3f3":{name:"KEY-ID FIDO2 Authenticator"},"ee041bce-25e5-4cdb-8f86-897fd6418464":{name:"Feitian ePass FIDO2-NFC Authenticator"},"73bb0cd4-e502-49b8-9c6f-b59445bf720b":{name:"YubiKey 5 FIPS Series"},"149a2021-8ef6-4133-96b8-81f8d5b7f1f5":{name:"Security Key by Yubico with NFC"},"3b1adb99-0dfe-46fd-90b8-7f7614a4de2a":{name:"GoTrust Idem Key FIDO2 Authenticator"},"f8a011f3-8c0a-4d15-8006-17111f9edc7d":{name:"Security Key by Yubico"},"2c0df832-92de-4be1-8412-88a8f074df4a":{name:"Feitian FIDO Smart Card"},"c5703116-972b-4851-a3e7-ae1259843399":{name:"NEOWAVE Badgeo FIDO2"},"820d89ed-d65a-409e-85cb-f73f0578f82a":{name:"Vancosys iOS Authenticator"},"b6ede29c-3772-412c-8a78-539c1f4c62d2":{name:"Feitian BioPass FIDO2 Plus Authenticator"},"85203421-48f9-4355-9bc8-8a53846e5083":{name:"YubiKey 5Ci FIPS"},"d821a7d4-e97c-4cb6-bd82-4237731fd4be":{name:"Hyper FIDO Bio Security Key"},"516d3969-5a57-5651-5958-4e7a49434167":{name:"SmartDisplayer BobeePass (NFC-BLE FIDO2 Authenticator)"},"b93fd961-f2e6-462f-b122-82002247de78":{name:"Android Authenticator with SafetyNet Attestation"},"2fc0579f-8113-47ea-b116-bb5a8db9202a":{name:"YubiKey 5 Series with NFC"},"9ddd1817-af5a-4672-a2b9-3e3dd95000a9":{name:"Windows Hello VBS Hardware Authenticator"},"d8522d9f-575b-4866-88a9-ba99fa02f35b":{name:"YubiKey Bio Series"},"692db549-7ae5-44d5-a1e5-dd20a493b723":{name:"HID Crescendo Key"},"3e22415d-7fdf-4ea4-8a0c-dd60c4249b9d":{name:"Feitian iePass FIDO Authenticator"},"aeb6569c-f8fb-4950-ac60-24ca2bbe2e52":{name:"HID Crescendo C2300"},"9f0d8150-baa5-4c00-9299-ad62c8bb4e87":{name:"GoTrust Idem Card FIDO2 Authenticator"},"12ded745-4bed-47d4-abaa-e713f51d6393":{name:"Feitian AllinOne FIDO2 Authenticator"},"88bbd2f0-342a-42e7-9729-dd158be5407a":{name:"Precision InnaIT Key FIDO 2 Level 2 certified"},"34f5766d-1536-4a24-9033-0e294e510fb0":{name:"YubiKey 5 Series CTAP2.1 Preview 1 "},"83c47309-aabb-4108-8470-8be838b573cb":{name:"YubiKey Bio Series (Enterprise Profile)"},"be727034-574a-f799-5c76-0929e0430973":{name:"Crayonic KeyVault K1 (USB-NFC-BLE FIDO2 Authenticator)"},"b92c3f9a-c014-4056-887f-140a2501163b":{name:"Security Key by Yubico"},"54d9fee8-e621-4291-8b18-7157b99c5bec":{name:"HID Crescendo Enabled"},"6028b017-b1d4-4c02-b4b3-afcdafc96bb2":{name:"Windows Hello Software Authenticator"},"6d44ba9b-f6ec-2e49-b930-0c8fe920cb73":{name:"Security Key by Yubico with NFC"},"e416201b-afeb-41ca-a03d-2281c28322aa":{name:"ATKey.Pro CTAP2.1"},"9f77e279-a6e2-4d58-b700-31e5943c6a98":{name:"Hyper FIDO Pro"},"73402251-f2a8-4f03-873e-3cb6db604b03":{name:"uTrust FIDO2 Security Key"},"c1f9a0bc-1dd2-404a-b27f-8e29047a43fd":{name:"YubiKey 5 FIPS Series with NFC"},"504d7149-4e4c-3841-4555-55445a677357":{name:"WiSECURE AuthTron USB FIDO2 Authenticator"},"cb69481e-8ff7-4039-93ec-0a2729a154a8":{name:"YubiKey 5 Series"},"ee882879-721c-4913-9775-3dfcce97072a":{name:"YubiKey 5 Series"},"8c97a730-3f7b-41a6-87d6-1e9b62bda6f0":{name:"FT-JCOS FIDO Fingerprint Card"},"a1f52be5-dfab-4364-b51c-2bd496b14a56":{name:"OCTATCO EzFinger2 FIDO2 AUTHENTICATOR"},"3e078ffd-4c54-4586-8baa-a77da113aec5":{name:"Hideez Key 3 FIDO2"},"d41f5a69-b817-4144-a13c-9ebd6d9254d6":{name:"ATKey.Card CTAP2.0"},"bc2fe499-0d8e-4ffe-96f3-94a82840cf8c":{name:"OCTATCO EzQuant FIDO2 AUTHENTICATOR"},"1c086528-58d5-f211-823c-356786e36140":{name:"Atos CardOS FIDO2"},"77010bd7-212a-4fc9-b236-d2ca5e9d4084":{name:"Feitian BioPass FIDO2 Authenticator"},"833b721a-ff5f-4d00-bb2e-bdda3ec01e29":{name:"Feitian ePass FIDO2 Authenticator"}};function h(e){console.debug(e);let t=new DataView(e.slice(32,33)).getUint8(0);console.debug(t);let a={rpIdHash:c(e.slice(0,32)),flags:{userPresent:!!(t&1),userVerified:!!(t&4),backupEligibility:!!(t&8),backupState:!!(t&16),attestedData:!!(t&64),extensionsIncluded:!!(t&128)},counter:new DataView(e.slice(33,37)).getUint32(0,!1)};if(e.byteLength>37){let n=C(e);a={...a,aaguid:n,name:K(n)}}return a}function C(e){return P(e.slice(37,53))}function P(e){let t=y(e);return t=t.substring(0,8)+"-"+t.substring(8,12)+"-"+t.substring(12,16)+"-"+t.substring(16,20)+"-"+t.substring(20,32),t}function K(e){return(F??g)[e]?.name}var F=null;var x=new TextDecoder("utf-8");function f(e){return JSON.parse(x.decode(e))}function l(e){return h(e)}function D(e){return"Really complex to parse. Good luck with that one!"}function k(){return!!window.PublicKeyCredential}async function w(){return await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()}function J(e){return l(s(e))}async function N(e){if(e==="local")return"platform";if(e==="extern")return"cross-platform";if(e!=="both")try{return await w()?"platform":"cross-platform"}catch{return}}function E(e){switch(e){case-7:return"ES256";case-257:return"RS256";default:throw new Error(`Unknown algorithm code: ${e}`)}}async function W(e,t,a){if(a=a??{},!b(t))throw new Error("Provided challenge is not properly encoded in Base64url");let n={challenge:s(t),rp:{id:window.location.hostname,name:window.location.hostname},user:{id:await p(new TextEncoder().encode(e)),name:e,displayName:e},pubKeyCredParams:[{alg:-7,type:"public-key"},{alg:-257,type:"public-key"}],timeout:a.timeout??6e4,authenticatorSelection:{userVerification:a.userVerification??"required",authenticatorAttachment:await N(a.authenticatorType??"auto")},attestation:"direct"};a.debug&&console.debug(n);let i=await navigator.credentials.create({publicKey:n});a.debug&&console.debug(i);let r=i.response,o={username:e,credential:{id:i.id,publicKey:c(r.getPublicKey()),algorithm:E(i.response.getPublicKeyAlgorithm())},authenticatorData:c(r.getAuthenticatorData()),clientData:c(r.clientDataJSON),attestationData:a.attestation?c(r.attestationObject):null};return a.debug&&(o.debug={client:f(r.clientDataJSON),authenticator:l(r.getAuthenticatorData()),attestation:D(r.attestationObject)}),o}async function H(e){if(e==="local")return["internal"];if(e==="extern")return["usb","ble","nfc"];if(e==="both")return["internal","usb","ble","nfc"];try{return await w()?["internal"]:["usb","ble","nfc"]}catch{return["internal","usb","ble","nfc"]}}async function M(e,t,a){if(a=a??{},!b(t))throw new Error("Provided challenge is not properly encoded in Base64url");let n=await H(a.authenticatorType??"auto"),i={challenge:s(t),rpId:window.location.hostname,allowCredentials:e.map(d=>({id:s(d),type:"public-key",transports:n})),userVerification:a.userVerification??"required",timeout:a.timeout??6e4};a.debug&&console.debug(i);let r=await navigator.credentials.get({publicKey:i});a.debug&&console.debug(r);let o=r.response,u={credentialId:r.id,authenticatorData:c(o.authenticatorData),clientData:c(o.clientDataJSON),signature:c(o.signature)};return a.debug&&(u.debug={client:f(o.clientDataJSON),authenticator:l(o.authenticatorData)}),u}function j(e){return f(s(e))}function z(e){return l(s(e))}function v(e){switch(e){case"RS256":return{name:"RSASSA-PKCS1-v1_5",hash:"SHA-256"};case"ES256":return{name:"ECDSA",namedCurve:"P-256",hash:"SHA-256"};default:throw new Error(`Unknown or unsupported crypto algorithm: ${e}. Only 'RS256' and 'ES256' are supported.`)}}async function V(e,t){let a=s(t);return crypto.subtle.importKey("spki",a,e,!1,["verify"])}async function R(e,t,a,n){let i=s(a);return crypto.subtle.verify(e,t,i,n)}async function _({algorithm:e,publicKey:t,authenticatorData:a,clientData:n,signature:i}){let r=v(e),o=await V(r,t);console.debug(o);let u=await p(s(n));console.debug(u);let d=m(s(a),u);return console.debug(d),R(r,o,i,d)}export{k as isAvailable,w as isLocalAuthenticator,M as login,z as parseAuthenticatorBase64,J as parseAuthenticatorData,j as parseClientBase64,W as register,_ as verify}; | ||
//# sourceMappingURL=webauthn.min.js.map |
{ | ||
"name": "@passwordless-id/webauthn", | ||
"version": "0.0.5", | ||
"version": "0.0.6", | ||
"description": "A small wrapper around the webauthn protocol to make one's life easier.", | ||
@@ -12,3 +12,3 @@ "main": "src/webauthn.ts", | ||
"type": "git", | ||
"url": "git+https://github.com/dagnelies/passwordless-js.git" | ||
"url": "git+https://github.com/passwordless-id/webauthn.git" | ||
}, | ||
@@ -24,5 +24,5 @@ "keywords": [ | ||
"bugs": { | ||
"url": "https://github.com/dagnelies/passwordless-js/issues" | ||
"url": "https://github.com/passwordless-id/webauthn/issues" | ||
}, | ||
"homepage": "https://github.com/dagnelies/passwordless-js#readme", | ||
"homepage": "https://webauthn.passwordless.id", | ||
"devDependencies": { | ||
@@ -29,0 +29,0 @@ "esbuild": "^0.15.8", |
127
README.md
Passwordless.ID / webauthn | ||
========================== | ||
A greatly simplified and opiniated wrapper to invoke the [webauthn protocol](https://w3c.github.io/webauthn/) more conviniently. | ||
A greatly simplified and opiniated wrapper to invoke the [webauthn protocol](https://w3c.github.io/webauthn/) more conviniently. | ||
@@ -10,6 +10,6 @@ <img src="demos/img/banner-biometric-auth.svg" /> | ||
- [Basic Demo](demos/basic.html) | ||
- [Minimal Example (CDN)](demos/example-cdn.html) | ||
- [Minimal Example (repository)](demos/example-raw.html) | ||
- [Testing Playground](demos/playground.html) | ||
- [Basic Demo](https://webauthn.passwordless.id/demos/basic.html) | ||
- [Minimal Example (CDN)](https://webauthn.passwordless.id/demos/example-cdn.html) | ||
- [Minimal Example (repository)](https://webauthn.passwordless.id/demos/example-raw.html) | ||
- [Testing Playground](https://webauthn.passwordless.id/demos/playground.html) | ||
@@ -39,2 +39,19 @@ Installation / Usage | ||
Utilities | ||
--------- | ||
```js | ||
webauthn.isAvailable() | ||
``` | ||
Returns `true` or `false` depending on whether the Webauthn protocol is available on this platform/browser. | ||
```js | ||
await webauthn.isLocalAuthenticator() | ||
``` | ||
This promise returns `true` or `false` depending on whether the device itself can act as authenticator. Otherwise, an "extern" authenticator like a smartphone or usb security key can be used. This information is mainly used for information messages and user guidance. | ||
Registration | ||
@@ -50,3 +67,4 @@ ------------ | ||
"timeout": 60000, | ||
"attestation": false | ||
"attestation": false, | ||
"debug": false | ||
}) | ||
@@ -60,15 +78,10 @@ ``` | ||
"username": "Arnaud", | ||
"challenge": "random-server-challenge", | ||
"credential": { | ||
"id": "RufE-HKYK2...", | ||
"publicKey": "MIIBIjANBg...", | ||
"id": "tIbwHucpNqA5KXeb_7_fXHAZYm5yPiYK1KrTgbie3ZQ", | ||
"publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApVXqTTd7edPEN5E71jta6iE8LlOWDySooLC3qRg31SAJc_FYceD7q_PNVh9UPuedr2OX5DP1GzCP262vp8rJuCqLtR7xPle7iu_rdmQHxPketGtx2O8XkAqwNRO74sNU0J2VJ0Cq3cCPpk53FUZczyhP-gJaOogZh_w05BJtnpM8FtLBFcdWlimveRtZ1QQZhX-bd92mmwA9bFWkbauEdklrg3TdJFmBPyj_6ybqs3ocHqxH4hAsdVFvjp77x0O4oqcupkcKUPfXO3GyNEoMlrVo30oj34r_6ny4F_PeZESyDWCG3i4MR3OrKi8zfCqxjBRtMOdcWKDq2FDEDG4PVQIDAQAB", | ||
"algorithm": "RS256" | ||
}, | ||
"authenticator": { | ||
"isLocal": true, | ||
"aaguid": "08987058-cadc-4b81-b6e1-30de50dcbe96", | ||
"name": "Windows Hello Hardware Authenticator", | ||
"attestation": "o2NmbXRjdH...", | ||
"clientData": "eyJ0eXBlIj..." | ||
} | ||
"authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2NFAAAAAAiYcFjK3EuBtuEw3lDcvpYAILSG8B7nKTagOSl3m_-_31xwGWJucj4mCtSq04G4nt2UpAEDAzkBACBZAQClVepNN3t508Q3kTvWO1rqITwuU5YPJKigsLepGDfVIAlz8Vhx4Pur881WH1Q-552vY5fkM_UbMI_bra-nysm4Kou1HvE-V7uK7-t2ZAfE-R60a3HY7xeQCrA1E7viw1TQnZUnQKrdwI-mTncVRlzPKE_6Alo6iBmH_DTkEm2ekzwW0sEVx1aWKa95G1nVBBmFf5t33aabAD1sVaRtq4R2SWuDdN0kWYE_KP_rJuqzehwerEfiECx1UW-OnvvHQ7iipy6mRwpQ99c7cbI0SgyWtWjfSiPfiv_qfLgX895kRLINYIbeLgxHc6sqLzN8KrGMFG0w51xYoOrYUMQMbg9VIUMBAAE=", | ||
"clientData": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiTXpJd1pEQXpNelF0TkRoak55MDBOMk5oTFRnek5qa3RPVE01TkRjMFl6ZzFaamRpIiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ==", | ||
"attestationData": null | ||
} | ||
@@ -81,3 +94,3 @@ ``` | ||
- `challenge`: A server-side randomly generated string. | ||
- `options`: See below. | ||
- `options`: See [below](#options). | ||
@@ -103,25 +116,6 @@ | ||
{ | ||
"credentialId": "c8VC7q_TY0NvKIhcS_rafPLEvdw8GwePABH81QRNt4Y", | ||
"userHash": "awopRTWFXAQrBPRAbEPFg3WUd4forBvMho7Ie4sxabE=", | ||
"clientJson": { | ||
"type": "webauthn.get", | ||
"challenge": "ZTEyNGE0ZTAtNjg4NS00YzhlLWFhODktNTZkMjJhZDUxNGYz", | ||
"origin": "http://localhost:8080", | ||
"crossOrigin": false | ||
}, | ||
"clientData": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiWlRFeU5HRTBaVEF0TmpnNE5TMDBZemhsTFdGaE9Ea3ROVFprTWpKaFpEVXhOR1l6Iiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ==", | ||
"signature": "J8PbSE9ZgC2JME9r2SYGY7WMDUKVDFby8WPXxSpXYfLpmfjimGed8oEqvUtD4UhvshjKV9FOlS0Dc8N8ILvIDL77gmUPeY6oZbTqrw9+2NgeXONM9hNDnxIjOUxekC8a3LY1HFq7aWy4v9I/gu1vD5NGSouvlzxJXPHcC30Bxu70EMcTwtz3EnRmQ3UGuZXjYO2xd2l2BsUgyI87c/wpquaCThrOPEf1PlzS4Larv5lE/Lfh4gQ2O/1TvmBcjtT/oSFkkb6hAgJp51/QbrUbnzdAtTtbGnSTOukM/HZ6yFY5i4oy3l+cJbwAGxEqFUU7yAdPrmTJdLeLmzimve58RA==", | ||
"authenticatorJson": { | ||
"rpIdHash": "SZYN5YgOjGh0NBcPZHZgW4/krrmihjLHmVzzuoMdl2M=", | ||
"flags": { | ||
"userPresent": true, | ||
"userVerified": true, | ||
"backupEligibility": false, | ||
"backupState": false, | ||
"attestedData": false, | ||
"extensionsIncluded": false | ||
}, | ||
"counter": 1 | ||
}, | ||
"authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4/krrmihjLHmVzzuoMdl2MFAAAAAQ==" | ||
"credentialId": "tIbwHucpNqA5KXeb_7_fXHAZYm5yPiYK1KrTgbie3ZQ", | ||
"authenticatorData": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAAAQ==", | ||
"clientData": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiTnpGbVlUY3dORGN0TUdVeVpTMDBabVZqTFdFMU5HUXRNR1JpTkdVNU5HUmxObVUxIiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ==", | ||
"signature": "aoOiX2zxBCvEzebefHZY8GNudCeERuyly4TiSE5eUDyw3iPOnPBFoj0WniN3nuKwhIw8gmPnGhPTArI0apYxoX2mJQaHtAhMS-AxkTKHR63ysArR0Cpd9XMeJicuOGuY5c_zo_hMq91qioI-Ksr0SUTAMS_lWH2Tebe29iKwcT10l0L7ccueKW3G7U5yYxZq3InAuPA5_aJXHeX2neAvng3CFba8we0eQsD5JKh2otAK6Kgy-nT2EHIsBDtXtACn3Q6GfjFWSaeWPa9vngXKuKbLsnpCQjYvlwHt4PrnkvC5WBzGhEoBCF1L9NcxZbRDw_ksWJFYvJcMNcq9DYhxVg==" | ||
} | ||
@@ -134,4 +128,6 @@ ``` | ||
- `challenge`: A server-side randomly generated string, the base64url encoded version will be signed. | ||
- `options`: See below | ||
- `options`: See [below](#options). | ||
Options | ||
@@ -148,3 +144,52 @@ ------- | ||
- `attestation`: (Only for registration) If enabled, the device attestation and clientData will be provided as base64 encoded binary data. Note that this is not available on some platforms. *(Default: false)* | ||
- `debug`: If enabled, parses the "data" objects and provide it in a "debug" properties. | ||
Parsing data | ||
------------ | ||
**`clientData`** | ||
```js | ||
webauthn.parseAuthenticatorData('eyJ0eXBlIjoid2...') | ||
``` | ||
```json | ||
{ | ||
"type": "webauthn.create", | ||
"challenge": "MzIwZDAzMzQtNDhjNy00N2NhLTgzNjktOTM5NDc0Yzg1Zjdi", | ||
"origin": "http://localhost:8080", | ||
"crossOrigin": false | ||
} | ||
``` | ||
**`authenticatorData`** | ||
```js | ||
webauthn.parseAuthenticatorData('SZYN5YgOjGh0NB...') | ||
``` | ||
```json | ||
{ | ||
"rpIdHash": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M=", | ||
"flags": { | ||
"userPresent": true, | ||
"userVerified": true, | ||
"backupEligibility": false, | ||
"backupState": false, | ||
"attestedData": true, | ||
"extensionsIncluded": false | ||
}, | ||
"counter": 0, | ||
"aaguid": "08987058-cadc-4b81-b6e1-30de50dcbe96", | ||
"name": "Windows Hello Hardware Authenticator" | ||
} | ||
``` | ||
Please note that `aaguid` and `name` are only available during registration. | ||
Notes | ||
@@ -151,0 +196,0 @@ ----- |
@@ -1,5 +0,4 @@ | ||
import * as authenticatorMetadata from './authenticatorMetadata.json' | ||
import authenticatorMetadata from './authenticatorMetadata.json' | ||
import * as utils from './utils' | ||
//console.debug(authenticatorMetadata) | ||
@@ -28,9 +27,16 @@ export function parseAuthData(authData :ArrayBuffer) { | ||
if(authData.byteLength > 37) { | ||
// registration contains additional data | ||
const aaguid = extractAaguid(authData) | ||
// https://w3c.github.io/webauthn/#attested-credential-data | ||
let credentialLength = new DataView(authData.slice(53,55)).getUint16(0, false) // Big-Endian! | ||
//let credentialLength = new DataView(authData.slice(53,55)).getUint16(0, false) // Big-Endian! | ||
parsed = { | ||
...parsed, | ||
aaguid: extractAaguid(authData), | ||
credentialId: utils.toBase64url(authData.slice(55, 55+credentialLength)), | ||
publicKey: utils.toBase64url(authData.slice(55+credentialLength, authData.byteLength)) // probably breaks if extensions are invoked | ||
aaguid, // bytes 37->53 | ||
name: resolveAuthenticatorName(aaguid) | ||
// credentialBytes, // bytes 53->55: credential length | ||
// credentialId: utils.toBase64url(authData.slice(55, 55+credentialLength)), | ||
//publicKey: until where? ...and it's encoded using a strange format, let's better avoid it | ||
//extensions: starting where? | ||
} | ||
@@ -43,3 +49,3 @@ } | ||
export function extractAaguid(authData :ArrayBuffer) :string { | ||
return formatAaguid(authData.slice(37, 53)) | ||
return formatAaguid(authData.slice(37, 53)) // 16 bytes | ||
} | ||
@@ -49,8 +55,7 @@ | ||
let aaguid = utils.bufferToHex(buffer) | ||
aaguid = aaguid.substring(0,8) + '-' + aaguid.substring(8,12) + '-' + aaguid.substring(12,16) + '-' + aaguid.substring(16,20) + '-' + aaguid.substring(20,24) | ||
aaguid = aaguid.substring(0,8) + '-' + aaguid.substring(8,12) + '-' + aaguid.substring(12,16) + '-' + aaguid.substring(16,20) + '-' + aaguid.substring(20,32) | ||
return aaguid // example: "d41f5a69-b817-4144-a13c-9ebd6d9254d6" | ||
} | ||
export function resolveAuthenticatorName(authData :ArrayBuffer) :string { | ||
const aaguid = extractAaguid(authData) | ||
export function resolveAuthenticatorName(aaguid :string) :string { | ||
const aaguidMetadata = updatedAuthenticatorMetadata ?? authenticatorMetadata //await getAaguidMetadata() | ||
@@ -57,0 +62,0 @@ return aaguidMetadata[aaguid]?.name |
import * as utils from './utils' | ||
import * as authenticators from './authenticators' | ||
import * as parsers from './parsers' | ||
@@ -21,3 +21,3 @@ /** | ||
export function parseAuthenticatorData(authData :string) { | ||
return authenticators.parseAuthData(utils.parseBase64url(authData)) | ||
return parsers.parseAuthenticatorData(utils.parseBase64url(authData)) | ||
} | ||
@@ -49,2 +49,4 @@ | ||
// TODO: although algo "-8" is currently only used optionally by a few security keys, | ||
// it would not harm to support it for the sake of completeness | ||
type NumAlgo = -7 | -257 | ||
@@ -63,2 +65,15 @@ type NamedAlgo = 'RS256' | 'ES256' | ||
interface LoginOptions { | ||
userVerification ?:UserVerificationRequirement, | ||
authenticatorType ?:AuthType, | ||
timeout ?:number, | ||
debug ?:boolean | ||
} | ||
interface RegisterOptions extends LoginOptions { | ||
attestation?: boolean | ||
} | ||
/** | ||
@@ -80,6 +95,4 @@ * Creates a cryptographic key pair, in order to register the public key for later passwordless authentication. | ||
*/ | ||
// | ||
export async function register(username :string, challenge :string, options :any) { | ||
if(!options) | ||
options = {} | ||
export async function register(username :string, challenge :string, options? :RegisterOptions) { | ||
options = options ?? {} | ||
@@ -112,11 +125,14 @@ if(!utils.isBase64url(challenge)) | ||
console.debug(creationOptions) | ||
if(options.debug) | ||
console.debug(creationOptions) | ||
const credential = await navigator.credentials.create({publicKey: creationOptions}) as any //PublicKeyCredential | ||
console.debug(credential) | ||
if(options.debug) | ||
console.debug(credential) | ||
const response = credential.response as any //AuthenticatorAttestationResponse | ||
const response = credential.response as any // AuthenticatorAttestationResponse | ||
return { | ||
let registrationResponse :any = { | ||
username: username, | ||
challenge: challenge, | ||
credential: { | ||
@@ -127,11 +143,16 @@ id: credential.id, | ||
}, | ||
authenticator: { | ||
isLocal: (credential.authenticatorAttachment === "platform"), | ||
//transport: response.getTransports()[0], // In the RFC but not implemented by browsers | ||
aaguid: authenticators.extractAaguid(response.getAuthenticatorData()), | ||
name: authenticators.resolveAuthenticatorName(response.getAuthenticatorData()), | ||
attestation: options.attestation ? utils.toBase64url(response.attestationObject) : null, | ||
clientData: options.attestation ? utils.toBase64url(response.clientDataJSON) : null, | ||
authenticatorData: utils.toBase64url(response.getAuthenticatorData()), | ||
clientData: utils.toBase64url(response.clientDataJSON), | ||
attestationData: options.attestation ? utils.toBase64url(response.attestationObject) : null, | ||
} | ||
if(options.debug) { | ||
registrationResponse['debug'] = { | ||
client: parsers.parseClientData(response.clientDataJSON), | ||
authenticator: parsers.parseAuthenticatorData(response.getAuthenticatorData()), | ||
attestation: parsers.parseAttestationData(response.attestationObject) | ||
} | ||
} | ||
return registrationResponse | ||
} | ||
@@ -169,5 +190,4 @@ | ||
*/ | ||
export async function login(credentialIds :string[], challenge :string, options :any) { | ||
if(!options) | ||
options = {} | ||
export async function login(credentialIds :string[], challenge :string, options? :LoginOptions) { | ||
options = options ?? {} | ||
@@ -191,26 +211,45 @@ if(!utils.isBase64url(challenge)) | ||
console.debug(authOptions) | ||
if(options.debug) | ||
console.debug(authOptions) | ||
let auth = await navigator.credentials.get({publicKey: authOptions}) as PublicKeyCredential | ||
console.debug(auth) | ||
if(options.debug) | ||
console.debug(auth) | ||
const response = auth.response as AuthenticatorAssertionResponse | ||
return { | ||
const loginResult :any = { | ||
credentialId: auth.id, | ||
//userHash: utils.toBase64url(response.userHandle), // unreliable, optional for authenticators | ||
clientJson: JSON.parse(utils.parseBuffer(response.clientDataJSON)), | ||
authenticatorData: utils.toBase64url(response.authenticatorData), | ||
clientData: utils.toBase64url(response.clientDataJSON), | ||
signature: utils.toBase64url(response.signature), | ||
authenticatorJson: authenticators.parseAuthData(response.authenticatorData), | ||
authenticatorData: utils.toBase64url(response.authenticatorData) | ||
} | ||
if(options.debug) { | ||
loginResult['debug'] = { | ||
client: parsers.parseClientData(response.clientDataJSON), | ||
authenticator: parsers.parseAuthenticatorData(response.authenticatorData), | ||
} | ||
} | ||
return loginResult | ||
} | ||
export function parseClientBase64(txt :string) { | ||
return parsers.parseClientData( utils.parseBase64url(txt) ) | ||
} | ||
export function parseAuthenticatorBase64(txt :string) { | ||
return parsers.parseAuthenticatorData( utils.parseBase64url(txt) ) | ||
} | ||
type VerifyParams = { | ||
algorithm :'RS256' | 'ES256', | ||
algorithm :NamedAlgo, | ||
publicKey :string, // Base64url encoded | ||
@@ -222,6 +261,37 @@ authenticatorData :string, // Base64url encoded | ||
function getAlgoParams(algorithm :NamedAlgo) :any { | ||
switch (algorithm) { | ||
case 'RS256': | ||
return { | ||
name:'RSASSA-PKCS1-v1_5', | ||
hash:'SHA-256' | ||
}; | ||
case 'ES256': | ||
return { | ||
name: 'ECDSA', | ||
namedCurve: 'P-256', | ||
hash: 'SHA-256', | ||
}; | ||
default: | ||
throw new Error(`Unknown or unsupported crypto algorithm: ${algorithm}. Only 'RS256' and 'ES256' are supported.`) | ||
} | ||
} | ||
type AlgoParams = AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm | ||
async function parseCryptoKey(algoParams :AlgoParams, publicKey :string) :Promise<CryptoKey> { | ||
const buffer = utils.parseBase64url(publicKey) | ||
return crypto.subtle.importKey('spki', buffer, algoParams, false, ['verify']) | ||
} | ||
async function verifySignature(algoParams :AlgoParams, cryptoKey :CryptoKey, signature :string, payload :ArrayBuffer) :Promise<boolean> { | ||
const signatureBuffer = utils.parseBase64url(signature) | ||
return crypto.subtle.verify(algoParams, cryptoKey, signatureBuffer, payload) | ||
} | ||
// https://w3c.github.io/webauthn/#sctn-verifying-assertion | ||
export async function verify({algorithm :NamedAlgo, publicKey, authenticatorData, clientData, signature} :VerifyParams) :Promise<boolean> { | ||
let cryptoKey = await window.crypto.subtle.importKey( | ||
'spki', utils.parseBase64url(publicKey), {name:'RSASSA-PKCS1-v1_5', hash:'SHA-256'}, false, ['verify']) | ||
export async function verify({algorithm, publicKey, authenticatorData, clientData, signature} :VerifyParams) :Promise<boolean> { | ||
const algoParams = getAlgoParams(algorithm) | ||
let cryptoKey = await parseCryptoKey(algoParams, publicKey) | ||
console.debug(cryptoKey) | ||
@@ -232,12 +302,10 @@ | ||
// during "login", the authenticatorData is exactly 37 bytes | ||
let comboBuffer = utils.concatenateBuffers(utils.parseBase64url(authenticatorData), clientHash) | ||
console.debug(comboBuffer) | ||
console.debug(utils.parseBase64url(signature)) | ||
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/verify | ||
let validity = await window.crypto.subtle.verify({name:'RSASSA-PKCS1-v1_5', hash:'SHA-256'}, cryptoKey, utils.parseBase64url(signature), comboBuffer) | ||
let validity = verifySignature(algoParams, cryptoKey, signature, comboBuffer) | ||
return validity | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
388018
27
775
196
0