llms.txt

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:

  1. Collects all input values from the current page
  2. Builds a payload with the user's FID, inputs, button index, and timestamp
  3. Signs the payload using the user's Farcaster signer key
  4. Sends the signed JFS compact string as the POST body

The snap server then:

  1. Verifies the JFS signature cryptographically
  2. Checks the signing key against hub state for the claimed FID
  3. Validates the timestamp for replay protection
  4. 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:

  1. Header -- metadata (FID, key type, key)
  2. Payload -- the content being signed
  3. 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:

TypeSignature MethodDescription
custodyERC-191Signature from the FID's custody address
authERC-191Signature from a registered auth address
app_keyEdDSASignature 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:

  1. Decode the header and extract the fid, type, and key
  2. Verify the FID is registered and the key is active for that FID
  3. Verify the signature matches the signing input using the declared key
  4. 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.