Closed robvanvolt closed 1 week ago
Try console.log(error)
in your .catch()
to see what the full stack trace is. That'll help identify what might be causing the error as name
and message
aren't enough for this one.
function u(e){let t=new Uint8Array(e),n="";for(let r of t)n+=String.fromCharCode(r);return btoa(n).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}function p(e){let t=e.replace(/-/g,"+").replace(/_/g,"/"),n=(4-t.length%4)%4,a=t.padEnd(t.length+n,"="),r=atob(a),c=new ArrayBuffer(r.length),d=new Uint8Array(c);for(let o=0;o<r.length;o++)d[o]=r.charCodeAt(o);return c}function A(){return window?.PublicKeyCredential!==void 0&&typeof window.PublicKeyCredential=="function"}function m(e){let{id:t}=e;return{...e,id:p(t),transports:e.transports}}function y(e){return e==="localhost"||/^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(e)}var i=class extends Error{constructor({message:t,code:n,cause:a,name:r}){super(t,{cause:a}),this.name=r??a.name,this.code=n}};function I({error:e,options:t}){let{publicKey:n}=t;if(!n)throw Error("options was missing required publicKey property");if(e.name==="AbortError"){if(t.signal instanceof AbortSignal)return new i({message:"Registration ceremony was sent an abort signal",code:"ERROR_CEREMONY_ABORTED",cause:e})}else if(e.name==="ConstraintError"){if(n.authenticatorSelection?.requireResidentKey===!0)return new i({message:"Discoverable credentials were required but no available authenticator supported it",code:"ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT",cause:e});if(t.mediation==="conditional"&&n.authenticatorSelection?.userVerification==="required")return new i({message:"User verification was required during automatic registration but it could not be performed",code:"ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE",cause:e});if(n.authenticatorSelection?.userVerification==="required")return new i({message:"User verification was required but no available authenticator supported it",code:"ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT",cause:e})}else{if(e.name==="InvalidStateError")return new i({message:"The authenticator was previously registered",code:"ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED",cause:e});if(e.name==="NotAllowedError")return new i({message:e.message,code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:e});if(e.name==="NotSupportedError")return n.pubKeyCredParams.filter(r=>r.type==="public-key").length===0?new i({message:'No entry in pubKeyCredParams was of type "public-key"',code:"ERROR_MALFORMED_PUBKEYCREDPARAMS",cause:e}):new i({message:"No available authenticator supported any of the specified pubKeyCredParams algorithms",code:"ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG",cause:e});if(e.name==="SecurityError"){let a=window.location.hostname;if(y(a)){if(n.rp.id!==a)return new i({message:`The RP ID "${n.rp.id}" is invalid for this domain`,code:"ERROR_INVALID_RP_ID",cause:e})}else return new i({message:`${window.location.hostname} is an invalid domain`,code:"ERROR_INVALID_DOMAIN",cause:e})}else if(e.name==="TypeError"){if(n.user.id.byteLength<1||n.user.id.byteLength>64)return new i({message:"User ID was not between 1 and 64 characters",code:"ERROR_INVALID_USER_ID_LENGTH",cause:e})}else if(e.name==="UnknownError")return new i({message:"The authenticator was unable to process the specified options, or could not create a new credential",code:"ERROR_AUTHENTICATOR_GENERAL_ERROR",cause:e})}return e}var b=class{createNewAbortSignal(){if(this.controller){let n=new Error("Cancelling existing WebAuthn API call for new one");n.name="AbortError",this.controller.abort(n)}let t=new AbortController;return this.controller=t,t.signal}cancelCeremony(){if(this.controller){let t=new Error("Manually cancelling existing WebAuthn API call");t.name="AbortError",this.controller.abort(t),this.controller=void 0}}},_=new b,S=["cross-platform","platform"];function O(e){if(e&&!(S.indexOf(e)<0))return e}async function T(e){let{optionsJSON:t,useAutoRegister:n=!1}=e;if(!A())throw new Error("WebAuthn is not supported in this browser");let a={...t,challenge:p(t.challenge),user:{...t.user,id:p(t.user.id)},excludeCredentials:t.excludeCredentials?.map(m)},r={};n&&(r.mediation="conditional"),r.publicKey=a,r.signal=_.createNewAbortSignal();let c;try{c=await navigator.credentials.create(r)}catch(l){throw I({error:l,options:r})}if(!c)throw new Error("Registration was not completed");let{id:d,rawId:o,response:s,type:g}=c,f;typeof s.getTransports=="function"&&(f=s.getTransports());let w;if(typeof s.getPublicKeyAlgorithm=="function")try{w=s.getPublicKeyAlgorithm()}catch(l){E("getPublicKeyAlgorithm()",l)}let R;if(typeof s.getPublicKey=="function")try{let l=s.getPublicKey();l!==null&&(R=u(l))}catch(l){E("getPublicKey()",l)}let h;if(typeof s.getAuthenticatorData=="function")try{h=u(s.getAuthenticatorData())}catch(l){E("getAuthenticatorData()",l)}return{id:d,rawId:u(o),response:{attestationObject:u(s.attestationObject),clientDataJSON:u(s.clientDataJSON),transports:f,publicKeyAlgorithm:w,publicKey:R,authenticatorData:h},type:g,clientExtensionResults:c.getClientExtensionResults(),authenticatorAttachment:O(c.authenticatorAttachment)}}function E(e,t){console.warn(`The browser extension that intercepted this WebAuthn API call incorrectly implemented ${e}. You should report this error to them.
`,t)}function P(){if(!A())return new Promise(t=>t(!1));let e=window.PublicKeyCredential;return e.isConditionalMediationAvailable===void 0?new Promise(t=>t(!1)):e.isConditionalMediationAvailable()}function C({error:e,options:t}){let{publicKey:n}=t;if(!n)throw Error("options was missing required publicKey property");if(e.name==="AbortError"){if(t.signal instanceof AbortSignal)return new i({message:"Authentication ceremony was sent an abort signal",code:"ERROR_CEREMONY_ABORTED",cause:e})}else{if(e.name==="NotAllowedError")return new i({message:e.message,code:"ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",cause:e});if(e.name==="SecurityError"){let a=window.location.hostname;if(y(a)){if(n.rpId!==a)return new i({message:`The RP ID "${n.rpId}" is invalid for this domain`,code:"ERROR_INVALID_RP_ID",cause:e})}else return new i({message:`${window.location.hostname} is an invalid domain`,code:"ERROR_INVALID_DOMAIN",cause:e})}else if(e.name==="UnknownError")return new i({message:"The authenticator was unable to process the specified options, or could not create a new assertion signature",code:"ERROR_AUTHENTICATOR_GENERAL_ERROR",cause:e})}return e}async function N(e){let{optionsJSON:t,useBrowserAutofill:n=!1,verifyBrowserAutofillInput:a=!0}=e;if(!A())throw new Error("WebAuthn is not supported in this browser");let r;t.allowCredentials?.length!==0&&(r=t.allowCredentials?.map(m));let c={...t,challenge:p(t.challenge),allowCredentials:r},d={};if(n){if(!await P())throw Error("Browser does not support WebAuthn autofill");if(document.querySelectorAll("input[autocomplete$='webauthn']").length<1&&a)throw Error('No <input> with "webauthn" as the only or last value in its `autocomplete` attribute was detected');d.mediation="conditional",c.allowCredentials=[]}d.publicKey=c,d.signal=_.createNewAbortSignal();let o;try{o=await navigator.credentials.get(d)}catch(h){throw C({error:h,options:d})}if(!o)throw new Error("Authentication was not completed");let{id:s,rawId:g,response:f,type:w}=o,R;return f.userHandle&&(R=u(f.userHandle)),{id:s,rawId:u(g),response:{authenticatorData:u(f.authenticatorData),clientDataJSON:u(f.clientDataJSON),signature:u(f.signature),userHandle:R},type:w,clientExtensionResults:o.getClientExtensionResults(),authenticatorAttachment:O(o.authenticatorAttachment)}}export{A as a,T as b,N as c};
It's this file
The raw error is:
Object { name: "TypeError", message: "t is undefined", code: undefined, options: {…}, errorRaw: TypeError }
code: undefined
errorRaw: TypeError: t is undefined
columnNumber: 3867
fileName: "https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/chunk-6IR4Q7Z4.js"
lineNumber: 1
message: "t is undefined"
stack: "T@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/chunk-6IR4Q7Z4.js:1:3867\nonClick@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/island-registerisland.js:1:1314\nasync*K/<@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/chunk-RS4YOACS.js:1:4680\nEventListener.handleEvent*M@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/chunk-RS4YOACS.js:1:4141\npn@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/chunk-RS4YOACS.js:1:8099\nV@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/chunk-RS4YOACS.js:1:6886\ntn@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/chunk-RS4YOACS.js:1:1828\npn@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/chunk-RS4YOACS.js:1:8233\nV@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/chunk-RS4YOACS.js:1:6886\ntn@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/chunk-RS4YOACS.js:1:1828\nV@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/chunk-RS4YOACS.js:1:6552\ntn@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/chunk-RS4YOACS.js:1:1828\nV@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/chunk-RS4YOACS.js:1:6552\nan@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/chunk-RS4YOACS.js:1:9130\ns@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/main.js:1:2378\nsetTimeout handler*Me@https://mydomain.deno.dev/_frsh/js/ff7b3f1d7363578066fdbc5cdd468ec93df5f266/main.js:1:2428\n@https://mydomain.deno.dev/:1:3218\n"
<prototype>: TypeError.prototype { stack: "", … }
message: "t is undefined"
name: "TypeError"
options: Object { challenge: true, rpId: "mydomain.deno.dev", userId: true }
challenge: true
rpId: "mydomain.deno.dev"
I think it might be the following line:
p(e){let t=e.replace(/-/g,"+").replace(/_/g,"/")
That leads to t being undefined --> e is not correctly a string even though it should be, and it is clearly a string as shown in the options I have posted earlier:
🔐 Starting WebAuthn registration with validated options
...
challenge: string
...
rp.id: string
...
Edit: I was running the code on firefox.
For Safari, I get this error:
Cannot read properties of undefined (reading 'challenge')
And in Chrome, i get:
Cannot read properties of undefined (reading 'challenge')
So it is weird that options.challenge is clearly there (as after the generate-registration-options), but gets lost in the startregistration(options) process...
Hmm, can you share an example of your options
argument?
And is startRegistration()
out of @simplewebauthn/browser, or your own that's wrapping it?
Thank you very much! I have solved it by a weird hack:
options.optionsJSON = options; // without this line, it does not work
const registrationResponse = await startRegistration(options)
I thought one could use the options directly without having to nest it again within itself... Have I overread something in the documentation where it is stated or is it a bug / unintentional? I am using 11.0.0.
import {
browserSupportsWebAuthn,
startRegistration,
} from "npm:@simplewebauthn/browser";
Sample options (without nesting that does not work):
🔑 Received options:
Object { challenge: "3sI-mx3heP...", rp: {…}, user: {…}, pubKeyCredParams: (2) […], timeout: 60000, attestation: "none", excludeCredentials: (1) […], authenticatorSelection: {…}, extensions: {…} }
attestation: "none"
authenticatorSelection: Object { residentKey: "discouraged", userVerification: "preferred", authenticatorAttachment: "platform", … }
challenge: "3sI-mx3heP..."
excludeCredentials: Array [ {…} ]
extensions: Object { credProps: true }
credProps: true
<prototype>: Object { … }
pubKeyCredParams: Array [ {…}, {…} ]
rp: Object { name: "App Example", id: "localhost" }
timeout: 60000
user: Object { id: "aW50ZXJuYW...", name: "user@localhost", displayName: "" }
Ah, maybe that's the confusion, Breaking Changes for v11 in the CHANGELOG detail the API change to startRegistration()
and startAuthentication()
that requires refactoring calls to pass in a single object with properties, instead of using positional arguments:
Sorry for the confusion. I'm seeing an opportunity here to make this experience a little nicer (e.g. checking for the presence of optionsJSON
and nesting automatically, or perhaps erroring more descriptively when optionsJSON
is missing.)
If there are any docs I failed to update to account for the v11 changes can you please let me know? It'll give me a chance to update those to save other developers from your pain 😂 😭
No worries, my bad, I have reread the documentation and now it makes sense - i think i have overread the naming convention (that it needs to explicitly be called {optionsJSON} instead of just {options} and the error confused me at the beginning!:)
The following part was not too clear for me:
// GET registration options from the endpoint that calls
// @simplewebauthn/server -> generateRegistrationOptions()
const resp = await fetch('/generate-registration-options');
const optionsJSON = await resp.json();
let attResp;
try {
// Pass the options to the authenticator and wait for a response
attResp = await startRegistration({ optionsJSON });
} catch (error) {
// Some basic error handling
if (error.name === 'InvalidStateError') {
elemError.innerText = 'Error: Authenticator was probably already registered by user';
} else {
elemError.innerText = error;
}
throw error;
}
https://simplewebauthn.dev/docs/packages/browser
I have just named my resp.json() options instead of optionsJSON.
Who would have thought that the naming convention plays a role a few lines further down, and I was not seeing that a few the name of the variable is not arbitrarilly chosen but needs to be exactl optionsJSON, because it is not just deconstructing { ...optionsJSON } but { optionsJSON } ---> { optionsJSON: optionsJSON }
Best,
RVV
Edit:
The example is better to understand:
const opts = await resp.json();
printDebug(elemDebug, 'Registration Options', JSON.stringify(opts, null, 2));
hideAuthForm();
attResp = await startRegistration({ optionsJSON: opts });
https://github.com/MasterKale/SimpleWebAuthn/blob/master/example/public/index.html
I get the following error on production only:
I have already tried to pinpoint the undefined variable.
Does anyone see an issue here?