Passkey Authentication

Passkey authentication is fully local-first and the most secure of the auth methods that Jazz provides because keys are managed by the device/operating system itself.

How it works

Passkey authentication is based on the Web Authentication API and uses familiar FaceID/TouchID flows that users already know how to use.

Key benefits

  • Most secure: Keys are managed by the device/OS
  • User-friendly: Uses familiar biometric verification (FaceID/TouchID)
  • Cross-device: Works across devices with the same biometric authentication
  • No password management: Users don't need to remember or store anything
  • Wide support: Available in most modern browsers

Implementation

Using passkeys in Jazz is as easy as this:

export function function AuthModal({ open, onOpenChange }: AuthModalProps): React.JSX.ElementAuthModal({ open: booleanopen, onOpenChange: (open: boolean) => voidonOpenChange }: 
type AuthModalProps = {
    open: boolean;
    onOpenChange: (open: boolean) => void;
}
AuthModalProps
) {
const [const username: stringusername, const setUsername: React.Dispatch<React.SetStateAction<string>>setUsername] = useState<string>(initialState: string | (() => string)): [string, React.Dispatch<React.SetStateAction<string>>] (+1 overload)
Returns a stateful value, and a function to update it.
@version16.8.0@see{@link https://react.dev/reference/react/useState}
useState
("");
const
const auth: {
    readonly state: "signedIn" | "anonymous";
    readonly logIn: () => Promise<void>;
    readonly signUp: (username: string) => Promise<void>;
}
auth
=
function usePasskeyAuth({ appName, appHostname, }: {
    appName: string;
    appHostname?: string;
}): {
    readonly state: "signedIn" | "anonymous";
    readonly logIn: () => Promise<void>;
    readonly signUp: (username: string) => Promise<void>;
}
`usePasskeyAuth` hook provides a `JazzAuth` object for passkey authentication.
@example```ts const auth = usePasskeyAuth({ appName, appHostname }); ```@categoryAuth Providers
usePasskeyAuth
({ // Must be inside the JazzProvider!
appName: stringappName: "My super-cool web app", }); if (
const auth: {
    readonly state: "signedIn" | "anonymous";
    readonly logIn: () => Promise<void>;
    readonly signUp: (username: string) => Promise<void>;
}
auth
.state: "signedIn" | "anonymous"state === "signedIn") { // You can also use `useIsAuthenticated()`
return <JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>You are already signed in</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>; } const const handleSignUp: () => Promise<void>handleSignUp = async () => { await
const auth: {
    readonly state: "signedIn" | "anonymous";
    readonly logIn: () => Promise<void>;
    readonly signUp: (username: string) => Promise<void>;
}
auth
.signUp: (username: string) => Promise<void>signUp(const username: stringusername);
onOpenChange: (open: boolean) => voidonOpenChange(false); }; const const handleLogIn: () => Promise<void>handleLogIn = async () => { await
const auth: {
    readonly state: "signedIn" | "anonymous";
    readonly logIn: () => Promise<void>;
    readonly signUp: (username: string) => Promise<void>;
}
auth
.logIn: () => Promise<void>logIn();
onOpenChange: (open: boolean) => voidonOpenChange(false); }; return ( <JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div> <JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={const handleLogIn: () => Promise<void>handleLogIn}>Log in</JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button> <JSX.IntrinsicElements.input: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>input React.InputHTMLAttributes<HTMLInputElement>.type?: React.HTMLInputTypeAttribute | undefinedtype="text" React.InputHTMLAttributes<HTMLInputElement>.value?: string | number | readonly string[] | undefinedvalue={const username: stringusername} React.InputHTMLAttributes<HTMLInputElement>.onChange?: React.ChangeEventHandler<HTMLInputElement> | undefinedonChange={(e: React.ChangeEvent<HTMLInputElement>e) => const setUsername: (value: React.SetStateAction<string>) => voidsetUsername(e: React.ChangeEvent<HTMLInputElement>e.React.ChangeEvent<HTMLInputElement>.target: EventTarget & HTMLInputElementtarget.HTMLInputElement.value: string
Returns the value of the data at the cursor's current position. [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLInputElement/value)
value
)} />
<JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={const handleSignUp: () => Promise<void>handleSignUp}>Sign up</JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button> </JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div> ); }

Examples

You can try passkey authentication using our passkey example or the music player demo.

When to use Passkeys

Passkeys are ideal when:

  • Security is a top priority
  • You want the most user-friendly authentication experience
  • You're targeting modern browsers and devices
  • You want to eliminate the risk of password-based attacks

Limitations and considerations

  • Requires hardware/OS support for biometric authentication
  • Not supported in older browsers (see browser support below)
  • Requires a fallback method for unsupported environments

Browser Support

Passkeys are supported in most modern browsers.

For older browsers, we recommend using passphrase authentication as a fallback.

Additional resources

For more information about the Web Authentication API and passkeys: