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.Element
AuthModal({open: boolean
open,onOpenChange: (open: boolean) => void
onOpenChange }:AuthModalProps) { const [
type AuthModalProps = { open: boolean; onOpenChange: (open: boolean) => void; }
const username: string
username,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.useState(""); constauth =
const auth: { readonly state: "signedIn" | "anonymous"; readonly logIn: () => Promise<void>; readonly signUp: (username: string) => Promise<void>; }
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.usePasskeyAuth({ // Must be inside the JazzProvider!appName: string
appName: "My super-cool web app", }); if (auth.
const auth: { readonly state: "signedIn" | "anonymous"; readonly logIn: () => Promise<void>; readonly signUp: (username: string) => Promise<void>; }
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>; } constconst handleSignUp: () => Promise<void>
handleSignUp = async () => { awaitauth.
const auth: { readonly state: "signedIn" | "anonymous"; readonly logIn: () => Promise<void>; readonly signUp: (username: string) => Promise<void>; }
signUp: (username: string) => Promise<void>
signUp(const username: string
username);onOpenChange: (open: boolean) => void
onOpenChange(false); }; constconst handleLogIn: () => Promise<void>
handleLogIn = async () => { awaitauth.
const auth: { readonly state: "signedIn" | "anonymous"; readonly logIn: () => Promise<void>; readonly signUp: (username: string) => Promise<void>; }
logIn: () => Promise<void>
logIn();onOpenChange: (open: boolean) => void
onOpenChange(false); }; return ( <JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div> <JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
buttonReact.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick={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>
inputReact.InputHTMLAttributes<HTMLInputElement>.type?: React.HTMLInputTypeAttribute | undefined
type="text"React.InputHTMLAttributes<HTMLInputElement>.value?: string | number | readonly string[] | undefined
value={const username: string
username}React.InputHTMLAttributes<HTMLInputElement>.onChange?: React.ChangeEventHandler<HTMLInputElement> | undefined
onChange={(e: React.ChangeEvent<HTMLInputElement>
e) =>const setUsername: (value: React.SetStateAction<string>) => void
setUsername(e: React.ChangeEvent<HTMLInputElement>
e.React.ChangeEvent<HTMLInputElement>.target: EventTarget & HTMLInputElement
target.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>
buttonReact.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick={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: