Open auth for Zcash

Sign in with Zcash.

The auth primitive Zcash didn't have. Non-custodial, drop-in, and built on what wallets already do.

$ pnpm add @siwz/core @siwz/react @siwz/next-auth
3
npm packages
59
core tests
5–15s
avg sign-in
MIT
licensed
§ 01  Wallet compatibility

What works where, today.

The memo-challenge column is the floor. It lights up everywhere ZIP 321 does, which is every modern Zcash wallet. The signed-message column is what your favourite wallet implements today.

WalletMemo challengeSigned messageMetaMask Snap
ZodlYesNoNo
YWalletYesTransparent onlyNo
ZingoYesTransparent onlyNo
zcash-cliYesYesNo
Cake, Zenith, eZcashYesNoNo
MetaMask + ChainSafe SnapNoNoYes

Wallet support data based on the full matrix in docs/wallets.md. Memo-challenge coverage is universal; the long tail of shielded wallets is rolled up into one row.

§ 02  Flows

One verifier, three ways in.

Every flow ends in a single session. The user picks whichever their wallet supports. The server does not care which.

01
Memo challenge
Recommended
Send a tiny payment with a one-time memo or unique amount. <MemoSignIn /> on the client, issueMemoHandler + pollMemoHandler on the server, SiwzMemoProvider for NextAuth. No signmessage feature needed in the wallet; works with every ZIP 321 wallet.
02
Signed message
Power users
SIWE-style flow with a magic-prefixed challenge. verifyTransparentSignature handles t-addrs via secp256k1 recovery; verifySaplingSignature is a plug-point for a future ZIP 304 verifier.
03
MetaMask Snap
One click
The ChainSafe Zcash Snap returns a viewing key and a stable account id. No QR, no fee. Subject to the Snap manifest's origin allowlist; treat it as progressive enhancement.
§ 03  Quickstart

Drop it into a Next.js app.

Two route handlers, one NextAuth provider, one sign-in component, one sign-out component. That is the whole integration.

app/api/auth/memo/{issue,poll}/route.ts · app/api/auth/[...nextauth]/route.ts · app/SignIn.tsx
// app/api/auth/memo/issue/route.ts
import { issueMemoHandler } from "@siwz/next-auth/memo";
export const POST = issueMemoHandler({
  secret: process.env.NEXTAUTH_SECRET!,
  serviceAddress: process.env.SIWZ_SERVICE_ADDRESS!,
  network: "mainnet",
});

// app/api/auth/memo/poll/route.ts
import { pollMemoHandler } from "@siwz/next-auth/memo";
export const POST = pollMemoHandler({ secret: process.env.NEXTAUTH_SECRET! });

// app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth";
import { SiwzMemoProvider } from "@siwz/next-auth";
const handler = NextAuth({
  providers: [ SiwzMemoProvider({ secret: process.env.NEXTAUTH_SECRET! }) ],
});
export { handler as GET, handler as POST };

// app/SignIn.tsx  (client)
import { MemoSignIn, SignOut } from "@siwz/react";
import { signIn, signOut } from "next-auth/react";

<MemoSignIn
  onSuccess={({ identity, envelope }) =>
    signIn("memo", { identity, envelope, redirect: false })}
/>;

<SignOut onSignOut={() => signOut({ callbackUrl: "/" })} />;
§ 04  Reference apps

Two apps, one primitive.

Both consume the same @siwz/* packages. The point: SIWZ is a primitive, not a framework.

§ 05  Packages

Three layers. Use one or all.

Independently usable. Pure TypeScript core, no Node-only deps, 59 tests.

@siwz/core
Message format, ZIP 321 builder, address parsing, memo-challenge, and pure-TS verification.
@siwz/react
<MemoSignIn />, <SignInWithZcash />, <SignOut />, the useSiwz() hook, and MetaMask Snap helpers.
@siwz/next-auth
SiwzProvider, SiwzMemoProvider, drop-in route handlers, stateless HMAC nonces. Free transparent explorer chain by default.