Authentication
Every POST request from the client to a snap server MUST be authenticated with JSON Farcaster Signatures (JFS).
How It Works
When a user taps a post button, the Farcaster client:
- Collects all input values from the current page
- Builds a payload with the user's FID, inputs, button index, and timestamp
- Signs the payload using the user's Farcaster signer key
- Sends the signed JFS compact string as the POST body
The snap server then:
- Verifies the JFS signature cryptographically
- Checks the signing key against hub state for the claimed FID
- Validates the timestamp for replay protection
- Processes the request and returns a new page
JFS Payload Shape
The decoded JFS payload (signed inside JFS, not sent as bare JSON):
{
"fid": 12345,
"inputs": {
"guess": "CLASS",
"vote": "Tabs"
},
"button_index": 0,
"timestamp": 1710864000
}
JSON Farcaster Signatures (JFS) Format
JFS is a standardized way for Farcaster identities to sign arbitrary payloads. It consists of three components:
- Header -- metadata (FID, key type, key)
- Payload -- the content being signed
- Signature -- the cryptographic signature
Compact Serialization
JFS uses a dot-separated format similar to JWT:
BASE64URL(header) . BASE64URL(payload) . BASE64URL(signature)
The signing input is constructed as:
ASCII(BASE64URL(UTF8(Header)) || '.' || BASE64URL(Payload))
Key Types
JFS supports three key types:
| Type | Signature Method | Description |
|---|---|---|
custody | ERC-191 | Signature from the FID's custody address |
auth | ERC-191 | Signature from a registered auth address |
app_key | EdDSA | Signature from a registered App Key |
For snaps, the client typically uses app_key (EdDSA signature from the user's registered signer key).
Verification
To verify a JFS:
- Decode the header and extract the
fid,type, andkey - Verify the FID is registered and the key is active for that FID
- Verify the signature matches the signing input using the declared key
- Query a Farcaster Hub to confirm the key is currently associated with the FID
Reference Implementation
The official Node.js package is @farcaster/jfs.
Replay Protection
The request payload MUST contain a timestamp field (Unix seconds).
Servers SHOULD reject requests with timestamps outside an allowed skew (for example, 5 minutes) to limit replay attacks.
Requirements
- The client MUST send a valid JFS for every authenticated POST
- The server MUST verify the JFS cryptographically and MUST verify the signing key against hub (or equivalent) state for the FID
- The server SHOULD enforce replay protection using
timestamp(and any other policy you require)
Server-Side Verification with @farcaster/snap-hono
The @farcaster/snap-hono package handles JFS verification automatically:
import { registerSnapHandler } from "@farcaster/snap-hono";
registerSnapHandler(
app,
async (ctx) => {
// ctx.action.fid is verified — the JFS signature was checked
// ctx.action.inputs contains the user's input values
// ctx.action.button_index is which button was tapped
},
{
skipJFSVerification: false, // true for local dev
},
);
Set SKIP_JFS_VERIFICATION=1 in your environment for local development to skip JFS verification.