Skip to content

Commit 9f6f511

Browse files
authored
Merge pull request #59 from authorizerdev/feat/passkey-autofill
feat(login): passkey autofill (conditional mediation) on the login form
2 parents 066f404 + 5ac383d commit 9f6f511

3 files changed

Lines changed: 45 additions & 7 deletions

File tree

package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@
9191
"typescript": "^5.7.2"
9292
},
9393
"dependencies": {
94-
"@authorizerdev/authorizer-js": "3.3.0-rc.0",
94+
"@authorizerdev/authorizer-js": "^3.3.0-rc.1",
9595
"@storybook/preset-scss": "^1.0.3",
9696
"validator": "^13.11.0"
9797
}

src/components/AuthorizerBasicAuthLogin.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,40 @@ export const AuthorizerBasicAuthLogin: FC<{
182182
}
183183
}, [formData.password]);
184184

185+
// Passkey autofill (WebAuthn conditional mediation): when the browser and the
186+
// user's device support it, discoverable passkeys are offered inline in the
187+
// email/username field's autofill dropdown. Best-effort and silent - the
188+
// explicit "Sign in with a passkey" button remains the primary path, and any
189+
// unsupported/cancelled/aborted ceremony is ignored. Started once on mount
190+
// and aborted on unmount.
191+
useEffect(() => {
192+
let active = true;
193+
authorizerRef
194+
.loginWithPasskeyAutofill()
195+
.then(({ data, errors }) => {
196+
if (!active || (errors && errors.length) || !data) {
197+
return;
198+
}
199+
setAuthData({
200+
user: data.user || null,
201+
token: data,
202+
config,
203+
loading: false,
204+
});
205+
if (onLogin) {
206+
onLogin(data);
207+
}
208+
})
209+
.catch(() => {
210+
// Autofill is best-effort; ignore unsupported/cancelled ceremonies.
211+
});
212+
return () => {
213+
active = false;
214+
authorizerRef.cancelPasskeyAutofill();
215+
};
216+
// eslint-disable-next-line react-hooks/exhaustive-deps
217+
}, []);
218+
185219
if (totpData.is_screen_visible) {
186220
return (
187221
<AuthorizerTOTPScanner
@@ -238,6 +272,10 @@ export const AuthorizerBasicAuthLogin: FC<{
238272
}`}
239273
placeholder={getEmailPhonePlaceholder(config)}
240274
type="text"
275+
// Enables WebAuthn passkey autofill: browsers offer discoverable
276+
// passkeys inline in this field's autofill dropdown. Paired with
277+
// the loginWithPasskeyAutofill() ceremony started on mount below.
278+
autoComplete="username webauthn"
241279
value={formData.email_or_phone_number || ''}
242280
onChange={e =>
243281
onInputChange('email_or_phone_number', e.target.value)

0 commit comments

Comments
 (0)