> ## 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.

# Sign-in with Solana (SIWS)

> Authenticate users by having them sign a message to prove wallet ownership

Verify a user owns a wallet by asking them to sign a message, then validate the signature on your backend.

<Tabs>
  <Tab title="React">
    ```tsx theme={null}
    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>;
    }
    ```
  </Tab>

  <Tab title="Browser SDK">
    ```typescript theme={null}
    import { BrowserSDK, AddressType } from "@phantom/browser-sdk";

    const sdk = new BrowserSDK({
      providers: ["google", "apple", "injected"],
      appId: "your-app-id",
      addressTypes: [AddressType.solana],
    });

    async function signIn() {
      const address = await sdk.solana.getPublicKey();

      const message = `Sign in to MyApp

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

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

      // Send to 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();
      return token;
    }
    ```
  </Tab>

  <Tab title="React Native">
    ```tsx theme={null}
    import { useSolana, useAccounts } from "@phantom/react-native-sdk";
    import { View, Button, Alert, StyleSheet } from "react-native";

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

      const signIn = async () => {
        try {
          const address = addresses?.[0]?.address;
          if (!solana || !address) {
            Alert.alert("Error", "Please connect your wallet first");
            return;
          }

          const message = `Sign in to MyApp

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

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

          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();
          Alert.alert("Success", "Authenticated!");
          console.log("Token:", token);
        } catch (error) {
          Alert.alert("Error", error.message);
        }
      };

      return (
        <View style={styles.container}>
          <Button title="Sign in with Solana" onPress={signIn} />
        </View>
      );
    }

    const styles = StyleSheet.create({
      container: {
        padding: 20,
      },
    });
    ```
  </Tab>
</Tabs>

## Verify on the backend

```typescript theme={null}
// 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 });
}
```
