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 identity (user.fid), timestamp, audience, and other payload fields that are unrelated to authentication.
  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 that audience matches the server's origin
  4. Validates timestamp for replay protection
  5. 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"
  },
  "timestamp": 1710864000,
  "audience": "https://snap.example.com",
  "user": {
    "fid": 12345
  },
  "surface": {
    "type": "standalone"
  }
}

Audience

The payload MUST contain an audience field set to the origin of the snap server the request is intended for. Origin = scheme + host + port (port only needed if it is not the default port for that scheme)

Servers MUST reject requests where audience does not match the server's own origin. This prevents a signed payload meant for one snap from being replayed against a different snap server.

Replay protection

The payload MUST contain a timestamp field (Unix seconds).

Servers MUST reject requests with timestamps outside an allowed skew (default 5 minutes).

If strict replay protection is needed beyond that, clients should add a nonce field (unique per request) to the signed payload.

Requirements

  • The client MUST send a valid JFS for every authenticated POST
  • The client MUST include the user's FID, audience (server origin) and timestamp (Unix seconds) in every payload
  • The server MUST verify the JFS cryptographically and MUST verify the signing key against hub (or equivalent) state for the FID
  • The server MUST verify that audience matches its own origin
  • The server MUST enforce timestamp skew checks

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 JFS Node.js package is @farcaster/jfs.

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
    // Distinguish buttons via distinct submit target URLs on each button (ctx.request.url)
  },
  {
    skipJFSVerification: false, // set to `true` for local dev
  },
);

Set SKIP_JFS_VERIFICATION=1 in your environment to skip JFS verification for local development.