Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.phantom.com/llms.txt

Use this file to discover all available pages before exploring further.

Verify a user owns a wallet by asking them to sign a message, then validate the signature on your backend.
import { useSolana, useAccounts } from "@phantom/react-sdk";

function SignInWithSolana() {
  const { solana } = useSolana();
  const addresses = useAccounts();

  const signIn = async () => {
    const address = addresses?.[0]?.address;
    if (!solana || !address) return;

    // Create a sign-in message
    const message = `Sign in to MyApp

Address: ${address}
Timestamp: ${new Date().toISOString()}
Nonce: ${crypto.randomUUID()}`;

    // Request signature
    const { signature } = await solana.signMessage(message);

    // Send to your backend for verification
    const response = await fetch("/api/auth/verify", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ address, message, signature }),
    });

    const { token } = await response.json();
    console.log("Authenticated! Token:", token);
  };

  return <button onClick={signIn}>Sign in with Solana</button>;
}

Verify on the backend

// api/auth/verify.ts
import { PublicKey } from "@solana/web3.js";
import nacl from "tweetnacl";
import bs58 from "bs58";
import jwt from "jsonwebtoken";

export async function POST(request: Request) {
  const { address, message, signature } = await request.json();

  // Verify the signature
  const publicKey = new PublicKey(address);
  const messageBytes = new TextEncoder().encode(message);
  const signatureBytes = bs58.decode(signature);

  const isValid = nacl.sign.detached.verify(
    messageBytes,
    signatureBytes,
    publicKey.toBytes()
  );

  if (!isValid) {
    return Response.json({ error: "Invalid signature" }, { status: 401 });
  }

  // Create a session token
  const token = jwt.sign({ address }, process.env.JWT_SECRET, { expiresIn: "7d" });

  return Response.json({ token });
}