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:

  1. Jazz generates a unique recovery phrase derived from the user's cryptographic keys
  2. This phrase consists of words from a wordlist
  3. 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 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 loginPassphrase: stringloginPassphrase, 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.
@version16.8.0@see{@link https://react.dev/reference/react/useState}
useState
("");
const
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;
}
auth
=
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.
@example```ts const auth = usePassphraseAuth({ appName, appHostname, wordlist }); ```@categoryAuth Providers
usePassphraseAuth
({ // Must be inside the JazzProvider!
wordlist: string[]wordlist: const wordlist: string[]wordlist, }); if (
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;
}
auth
.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>; } const const handleSignUp: () => Promise<void>handleSignUp = async () => { await
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;
}
auth
.signUp: (name?: string) => Promise<string>signUp();
onOpenChange: (open: boolean) => voidonOpenChange(false); }; const const handleLogIn: () => Promise<void>handleLogIn = async () => { await
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;
}
auth
.logIn: (passphrase: string) => Promise<void>logIn(const loginPassphrase: stringloginPassphrase);
onOpenChange: (open: boolean) => voidonOpenChange(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>textarea React.TextareaHTMLAttributes<HTMLTextAreaElement>.readOnly?: boolean | undefinedreadOnly React.TextareaHTMLAttributes<HTMLTextAreaElement>.value?: string | number | readonly string[] | undefinedvalue={
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;
}
auth
.passphrase: stringpassphrase}
React.TextareaHTMLAttributes<HTMLTextAreaElement>.rows?: number | undefinedrows={5} /> </React.JSX.IntrinsicElements.label: React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>label> <React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={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>textarea React.TextareaHTMLAttributes<HTMLTextAreaElement>.value?: string | number | readonly string[] | undefinedvalue={const loginPassphrase: stringloginPassphrase} React.TextareaHTMLAttributes<HTMLTextAreaElement>.onChange?: React.ChangeEventHandler<HTMLTextAreaElement> | undefinedonChange={(e: React.ChangeEvent<HTMLTextAreaElement>e) => const setLoginPassphrase: (value: React.SetStateAction<string>) => voidsetLoginPassphrase(e: React.ChangeEvent<HTMLTextAreaElement>e.React.ChangeEvent<HTMLTextAreaElement>.target: EventTarget & HTMLTextAreaElementtarget.HTMLTextAreaElement.value: string
Retrieves or sets the text in the entry field of the textArea element.
value
)}
React.TextareaHTMLAttributes<HTMLTextAreaElement>.placeholder?: string | undefinedplaceholder="Enter your passphrase" React.TextareaHTMLAttributes<HTMLTextAreaElement>.rows?: number | undefinedrows={5} React.TextareaHTMLAttributes<HTMLTextAreaElement>.required?: boolean | undefinedrequired /> </React.JSX.IntrinsicElements.label: React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>label> <React.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</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:

  1. Store the passphrase in a secure location (password manager, written down in a safe place)
  2. The passphrase is the only way to recover their account
  3. Anyone with the passphrase can access the account