{"id":"GHSA-gv75-j3v8-3crm","kind":"vulnerability","aliases":["GHSA-gv75-j3v8-3crm"],"related":[],"title":"@better-auth/passkey: Passkey verification does not enforce origin or user verification","description":"## Am I affected?\n\nYou are affected if you use `@better-auth/passkey` 1.4.0-beta.16 or later, or the passkey plugin built into `better-auth` before 1.4.0, and **any** of these is true:\n\n- You set `authenticatorSelection.userVerification` to `\"required\"`.\n- Your relying party ID also covers origins you do not control, such as other subdomains.\n\n## Summary\n\n**User verification.** With `userVerification: \"required\"`, the server asked the authenticator to verify the user, for example with a PIN or fingerprint, but did not confirm that it happened. The default `\"preferred\"` setting allows responses without this check by design.\n\n**Origin.** The server took the expected WebAuthn origin from the request instead of from its configuration.\n\n## Impact\n\nA user can register or use a passkey without the PIN or biometric check your application requires. A passkey response created on another origin under the same relying party ID could be accepted. Neither problem lets anyone register a passkey for another user's account or skip the sign-in needed to register one.\n\n## Patches\n\nUpgrade `@better-auth/passkey` to 1.7.16, or to 1.8.0-beta.5 on the beta line.\n\nThe expected origin now comes from the `origin` option, or from the `baseURL` origin plus a frontend origin listed exactly in `trustedOrigins`. Set `origin` when `rpID` is set and a wildcard in `baseURL.allowedHosts` covers it. `userVerification` now also applies to authentication.\n\n## Workarounds\n\nNone verified beyond upgrading.\n\n## Technical details\n\nThe registration and authentication handlers passed the request's origin to WebAuthn verification and did not set the required user verification flag. They now use the configured origin and require the user verification flag when `userVerification` is `\"required\"`.","summary":"The passkey plugin in @better-auth/passkey and better-auth took the expected WebAuthn origin from the request rather than server configuration, and did not confirm PIN or biometric checks when userVerification is required. An attacker on another origin under the same relying party ID could have a passkey response accepted, and a user could register or use a passkey without the required user verification.","severity":"low","cvss":3.1,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"@better-auth/passkey","fixed":["1.7.16"],"affected":">= 1.4.0-beta.16, < 1.7.16","ecosystem":"npm"},{"name":"@better-auth/passkey","fixed":["1.8.0-beta.5"],"affected":">= 1.8.0-beta.0, < 1.8.0-beta.5","ecosystem":"npm"},{"name":"better-auth","fixed":["1.4.0"],"affected":">= 1.2.0-beta.18, < 1.4.0","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["better-auth","cwe-346","cwe-602","node","package","passkeys","webauthn"],"not_affected_if":["default-config","no-passkey-plugin"],"how_to_check":"- Check the installed version of `@better-auth/passkey` (for example `npm ls @better-auth/passkey`) and of `better-auth`.\n- Look for `authenticatorSelection.userVerification` set to `\"required\"` in the passkey configuration.\n- Check whether the relying party ID (`rpID`) covers origins you do not control, such as other subdomains.","fix":"- `@better-auth/passkey`: `1.7.16` on the stable line, or `1.8.0-beta.5` on the beta line.\n- `better-auth`: `1.4.0` (the built-in passkey plugin before 1.4.0 is affected).\n- Configuration: set the `origin` option when `rpID` is set and a wildcard in `baseURL.allowedHosts` covers it.","status":"open","published_at":"2026-11-03T21:40:10+00:00","first_seen_at":"2026-11-09T22:53:20+00:00","triaged_at":"2026-11-09T22:57:28+00:00","updated_at":"2026-11-09T22:57:28+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T22:57:28Z","model":"claude-haiku-5-5","output":{"fix":"- `@better-auth/passkey`: `1.7.16` on the stable line, or `1.8.0-beta.5` on the beta line.\n- `better-auth`: `1.4.0` (the built-in passkey plugin before 1.4.0 is affected).\n- Configuration: set the `origin` option when `rpID` is set and a wildcard in `baseURL.allowedHosts` covers it.","reach":"internet","title":"better-auth passkey: origin and user verification checks not enforced","impact":"low","summary":"The passkey plugin in @better-auth/passkey and better-auth took the expected WebAuthn origin from the request rather than server configuration, and did not confirm PIN or biometric checks when userVerification is required. An attacker on another origin under the same relying party ID could have a passkey response accepted, and a user could register or use a passkey without the required user verification.","audience":"developers","category":"package","nicknames":[],"who_cares":["better-auth","node","passkeys","webauthn"],"confidence":0.8,"fix_status":"available","needs_auth":"unknown","worst_case":"A passkey is registered or used without the required PIN or biometric check","analyst_note":"","how_to_check":"- Check the installed version of `@better-auth/passkey` (for example `npm ls @better-auth/passkey`) and of `better-auth`.\n- Look for `authenticatorSelection.userVerification` set to `\"required\"` in the passkey configuration.\n- Check whether the relying party ID (`rpID`) covers origins you do not control, such as other subdomains.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["default-config","no-passkey-plugin"],"who_is_affected":"Applications that use the better-auth passkey plugin and either require user verification (userVerification set to \"required\") or use a relying party ID that covers origins the team does not control. The sources describe the request-origin problem as applying where the RP ID spans other subdomains; apps on the default \"preferred\" setting with a tightly scoped RP ID are not described as exposed to the verification bypass.","user_interaction":"unknown","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"low"},"cost_usd":0.000894,"input_tokens":5224,"output_tokens":743,"severity_before":"low"},"analyst_status":null,"analyst_note":null,"search":"'-346':39B '-602':41B '/passkey':4A,27A,31A,53B,125C,284C '1.4.0':126C,140C '1.7.16':286C '1.8.0':289C '3crm':18A,23A 'accept':98B,256C 'account':268C 'affect':115C,118C 'allow':200C 'also':156C,332C 'anoth':85B,246C,265C 'anyon':260C 'appli':333C 'applic':239C 'ask':175C 'attack':83B 'auth':3A,26A,30A,34A,37B,52B,57B,124C,138C,283C 'authent':177C,335C,346C 'authenticatorselection.userverification':149C 'baseurl':307C 'baseurl.allowedhosts':327C 'beta':293C 'beta.16':127C 'beta.5':290C 'better':2A,25A,29A,33A,36B,51B,56B,123C,137C,282C 'better-auth':1A,24A,28A,32A,35B,50B,55B,122C,136C,281C 'beyond':339C 'biometr':76B,236C 'built':134C 'check':77B,204C,237C 'come':299C 'configur':69B,222C,369C 'confirm':73B,192C 'control':162C 'could':93B,102B,254C 'cover':157C,328C 'creat':244C 'cwe':38B,40B 'default':197C 'design':206C 'detail':342C 'enforc':9A 'exact':314C 'exampl':183C 'expect':60B,212C,296C 'fingerprint':188C 'flag':364C,376C 'frontend':311C 'ghsa':15A,20A 'ghsa-gv75-j3v8-3crm':14A,19A 'gv75':16A,21A 'handler':347C 'happen':195C 'id':92B,155C,253C 'impact':223C 'instead':218C 'j3v8':17A,22A 'later':129C 'let':259C 'line':294C 'list':313C 'need':275C 'neither':257C 'node':42B 'none':337C 'one':278C 'option':303C 'origin':10A,62B,86B,158C,207C,214C,247C,297C,302C,308C,312C,318C,352C,370C 'packag':43B 'parti':91B,154C,252C 'pass':348C 'passkey':5A,44B,47B,96B,107B,132C,231C,242C,263C 'patch':279C 'pin':74B,186C,234C 'plugin':48B,133C 'plus':309C 'prefer':198C 'problem':258C 'rather':66B 'regist':103B,227C,261C,277C 'registr':344C 'reli':90B,153C,251C 'request':65B,217C,350C 'requir':81B,110B,151C,172C,240C,361C,372C,380C 'respons':97B,201C,243C 'rpid':320C 'server':68B,174C,209C 'set':148C,199C,317C,322C,359C 'sign':273C 'sign-in':272C 'skip':270C 'subdomain':166C 'summari':167C 'technic':341C 'took':58B,210C 'true':146C 'trustedorigin':316C 'upgrad':280C,340C 'use':105B,121C,229C,367C 'user':12A,101B,111B,168C,181C,225C,266C,362C,374C 'userverif':79B,171C,330C,378C 'verif':6A,13A,112B,169C,355C,363C,375C 'verifi':179C,338C 'webauthn':45B,61B,213C,354C 'wildcard':325C 'without':108B,202C,232C 'workaround':336C","nicknames":[],"mitigations":[],"who_affected":"Applications that use the better-auth passkey plugin and either require user verification (userVerification set to \"required\") or use a relying party ID that covers origins the team does not control. The sources describe the request-origin problem as applying where the RP ID spans other subdomains; apps on the default \"preferred\" setting with a tightly scoped RP ID are not described as exposed to the verification bypass.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["betterauth","passkey"],"ptokens":["auth","better","passkey"],"labels":["betterauth","cwe346","cwe602","low","node","npm","package","passkeys","webauthn"],"campaign_type":null,"search_a":"'/passkey':4A,27A,31A '3crm':18A,23A 'auth':3A,26A,30A,34A 'better':2A,25A,29A,33A 'better-auth':1A,24A,28A,32A 'enforc':9A 'ghsa':15A,20A 'ghsa-gv75-j3v8-3crm':14A,19A 'gv75':16A,21A 'j3v8':17A,22A 'origin':10A 'passkey':5A 'user':12A 'verif':6A,13A","search_b":"'-346':39B '-602':41B '/passkey':53B 'accept':98B 'anoth':85B 'attack':83B 'auth':37B,52B,57B 'better':36B,51B,56B 'better-auth':35B,50B,55B 'biometr':76B 'check':77B 'configur':69B 'confirm':73B 'could':93B,102B 'cwe':38B,40B 'expect':60B 'id':92B 'node':42B 'origin':62B,86B 'packag':43B 'parti':91B 'passkey':44B,47B,96B,107B 'pin':74B 'plugin':48B 'rather':66B 'regist':103B 'reli':90B 'request':65B 'requir':81B,110B 'respons':97B 'server':68B 'took':58B 'use':105B 'user':101B,111B 'userverif':79B 'verif':112B 'webauthn':45B,61B 'without':108B","search_c":"'/passkey':125C,284C '1.4.0':126C,140C '1.7.16':286C '1.8.0':289C 'accept':256C 'account':268C 'affect':115C,118C 'allow':200C 'also':156C,332C 'anoth':246C,265C 'anyon':260C 'appli':333C 'applic':239C 'ask':175C 'auth':124C,138C,283C 'authent':177C,335C,346C 'authenticatorselection.userverification':149C 'baseurl':307C 'baseurl.allowedhosts':327C 'beta':293C 'beta.16':127C 'beta.5':290C 'better':123C,137C,282C 'better-auth':122C,136C,281C 'beyond':339C 'biometr':236C 'built':134C 'check':204C,237C 'come':299C 'configur':222C,369C 'confirm':192C 'control':162C 'could':254C 'cover':157C,328C 'creat':244C 'default':197C 'design':206C 'detail':342C 'exact':314C 'exampl':183C 'expect':212C,296C 'fingerprint':188C 'flag':364C,376C 'frontend':311C 'handler':347C 'happen':195C 'id':155C,253C 'impact':223C 'instead':218C 'later':129C 'let':259C 'line':294C 'list':313C 'need':275C 'neither':257C 'none':337C 'one':278C 'option':303C 'origin':158C,207C,214C,247C,297C,302C,308C,312C,318C,352C,370C 'parti':154C,252C 'pass':348C 'passkey':132C,231C,242C,263C 'patch':279C 'pin':186C,234C 'plugin':133C 'plus':309C 'prefer':198C 'problem':258C 'regist':227C,261C,277C 'registr':344C 'reli':153C,251C 'request':217C,350C 'requir':151C,172C,240C,361C,372C,380C 'respons':201C,243C 'rpid':320C 'server':174C,209C 'set':148C,199C,317C,322C,359C 'sign':273C 'sign-in':272C 'skip':270C 'subdomain':166C 'summari':167C 'technic':341C 'took':210C 'true':146C 'trustedorigin':316C 'upgrad':280C,340C 'use':121C,229C,367C 'user':168C,181C,225C,266C,362C,374C 'userverif':171C,330C,378C 'verif':169C,355C,363C,375C 'verifi':179C,338C 'webauthn':213C,354C 'wildcard':325C 'without':202C,232C 'workaround':336C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-03T21:40:10+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-gv75-j3v8-3crm","url":"https://github.com/better-auth/better-auth/security/advisories/GHSA-gv75-j3v8-3crm","severity":"low","published_at":"2026-11-03T21:40:10+00:00","modified_at":"2026-11-03T21:40:11+00:00","first_seen_at":"2026-11-09T22:53:20+00:00","fetched_at":"2026-11-09T22:53:20+00:00","note":null}],"documents":[{"id":110500,"url":"https://github.com/better-auth/better-auth/security/advisories/GHSA-gv75-j3v8-3crm","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa-repo","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}