Passphrase Authentication
Passphrase authentication lets users log into any device using a recovery phrase consisting of multiple words (similar to cryptocurrency wallets). Users are responsible for storing this passphrase safely.
How it works
When a user creates an account with passphrase authentication:
- Jazz generates a unique recovery phrase derived from the user's cryptographic keys
- This phrase consists of words from a wordlist
- Users save this phrase and enter it when logging in on new devices
You can use one of the ready-to-use wordlists from the BIP39 repository or create your own. If you do decide to create your own wordlist, it's recommended to use at least 2048 unique words (or some higher power of two).
Key benefits
- Portable: Works across any device, even without browser or OS support
- User-controlled: User manages their authentication phrase
- Flexible: Works with any wordlist you choose
- Offline capable: No external dependencies
Implementation
import {
const wordlist: string[]
wordlist } from "./wordlist" export functionfunction 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 loginPassphrase: string
loginPassphrase,const setLoginPassphrase: React.Dispatch<React.SetStateAction<string>>
setLoginPassphrase] =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: "anonymous" | "signedIn"; readonly logIn: (passphrase: string) => Promise<void>; readonly signUp: (name?: string) => Promise<string>; readonly registerNewAccount: (passphrase: string, name: string) => Promise<string>; readonly generateRandomPassphrase: () => string; readonly passphrase: string; }
function usePassphraseAuth({ wordlist }: { wordlist: string[]; }): { readonly state: "anonymous" | "signedIn"; readonly logIn: (passphrase: string) => Promise<void>; readonly signUp: (name?: string) => Promise<string>; readonly registerNewAccount: (passphrase: string, name: string) => Promise<string>; readonly generateRandomPassphrase: () => string; readonly passphrase: string; }
`usePassphraseAuth` hook provides a `JazzAuth` object for passphrase authentication.usePassphraseAuth({ // Must be inside the JazzProvider!wordlist: string[]
wordlist:const wordlist: string[]
wordlist, }); if (auth.
const auth: { readonly state: "anonymous" | "signedIn"; readonly logIn: (passphrase: string) => Promise<void>; readonly signUp: (name?: string) => Promise<string>; readonly registerNewAccount: (passphrase: string, name: string) => Promise<string>; readonly generateRandomPassphrase: () => string; readonly passphrase: string; }
state: "anonymous" | "signedIn"
state === "signedIn") { // You can also use `useIsAuthenticated()` return <React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div>You are already signed in</React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div>; } constconst handleSignUp: () => Promise<void>
handleSignUp = async () => { awaitauth.
const auth: { readonly state: "anonymous" | "signedIn"; readonly logIn: (passphrase: string) => Promise<void>; readonly signUp: (name?: string) => Promise<string>; readonly registerNewAccount: (passphrase: string, name: string) => Promise<string>; readonly generateRandomPassphrase: () => string; readonly passphrase: string; }
signUp: (name?: string) => Promise<string>
signUp();onOpenChange: (open: boolean) => void
onOpenChange(false); }; constconst handleLogIn: () => Promise<void>
handleLogIn = async () => { awaitauth.
const auth: { readonly state: "anonymous" | "signedIn"; readonly logIn: (passphrase: string) => Promise<void>; readonly signUp: (name?: string) => Promise<string>; readonly registerNewAccount: (passphrase: string, name: string) => Promise<string>; readonly generateRandomPassphrase: () => string; readonly passphrase: string; }
logIn: (passphrase: string) => Promise<void>
logIn(const loginPassphrase: string
loginPassphrase);onOpenChange: (open: boolean) => void
onOpenChange(false); }; return ( <React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div> <React.JSX.IntrinsicElements.label: React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>
label> Your current passphrase <React.JSX.IntrinsicElements.textarea: React.DetailedHTMLProps<React.TextareaHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>
textareaReact.TextareaHTMLAttributes<HTMLTextAreaElement>.readOnly?: boolean | undefined
readOnlyReact.TextareaHTMLAttributes<HTMLTextAreaElement>.value?: string | number | readonly string[] | undefined
value={auth.
const auth: { readonly state: "anonymous" | "signedIn"; readonly logIn: (passphrase: string) => Promise<void>; readonly signUp: (name?: string) => Promise<string>; readonly registerNewAccount: (passphrase: string, name: string) => Promise<string>; readonly generateRandomPassphrase: () => string; readonly passphrase: string; }
passphrase: string
passphrase}React.TextareaHTMLAttributes<HTMLTextAreaElement>.rows?: number | undefined
rows={5} /> </React.JSX.IntrinsicElements.label: React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>
label> <React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
buttonReact.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick={const handleSignUp: () => Promise<void>
handleSignUp}>I have stored my passphrase</React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button> <React.JSX.IntrinsicElements.label: React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>
label> Log in with your passphrase <React.JSX.IntrinsicElements.textarea: React.DetailedHTMLProps<React.TextareaHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>
textareaReact.TextareaHTMLAttributes<HTMLTextAreaElement>.value?: string | number | readonly string[] | undefined
value={const loginPassphrase: string
loginPassphrase}React.TextareaHTMLAttributes<HTMLTextAreaElement>.onChange?: React.ChangeEventHandler<HTMLTextAreaElement> | undefined
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>
e) =>const setLoginPassphrase: (value: React.SetStateAction<string>) => void
setLoginPassphrase(e: React.ChangeEvent<HTMLTextAreaElement>
e.React.ChangeEvent<HTMLTextAreaElement>.target: EventTarget & HTMLTextAreaElement
target.HTMLTextAreaElement.value: string
Retrieves or sets the text in the entry field of the textArea element.value)}React.TextareaHTMLAttributes<HTMLTextAreaElement>.placeholder?: string | undefined
placeholder="Enter your passphrase"React.TextareaHTMLAttributes<HTMLTextAreaElement>.rows?: number | undefined
rows={5}React.TextareaHTMLAttributes<HTMLTextAreaElement>.required?: boolean | undefined
required /> </React.JSX.IntrinsicElements.label: React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>
label> <React.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</React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button> </React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div> ); }
Examples
You can see passphrase authentication in our passphrase example or the todo list demo.
When to use Passphrases
Passphrase authentication is ideal when:
- You need to support older browsers without WebAuthn capabilities
- Your users need to access the app on many different devices
- You want a fallback authentication method alongside passkeys
Limitations and considerations
- User responsibility: Users must securely store their passphrase
- Recovery concerns: If a user loses their passphrase, they cannot recover their account
- Security risk: Anyone with the passphrase can access the account
- User experience: Requires users to enter a potentially long phrase
Make sure to emphasize to your users:
- Store the passphrase in a secure location (password manager, written down in a safe place)
- The passphrase is the only way to recover their account
- Anyone with the passphrase can access the account