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

The SDK returns the signature as raw bytes (`Uint8Array`). The examples encode it as base58 with `bs58` (`npm install bs58`) so the backend can decode it.

**React**

```tsx theme={null}
import { useSolana, useAccounts } from "@phantom/react-sdk";
import bs58 from "bs58";

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: bs58.encode(signature) }),
    });

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

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

**Browser SDK**

```typescript theme={null}
import { BrowserSDK, AddressType } from "@phantom/browser-sdk";
import bs58 from "bs58";

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

async function signIn() {
  const address = sdk.solana.publicKey;
  if (!address) throw new Error("Wallet not connected");

  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: bs58.encode(signature) }),
  });

  const { token } = await response.json();
  return token;
}
```

**React Native**

```tsx theme={null}
import { useSolana, useAccounts } from "@phantom/react-native-sdk";
import bs58 from "bs58";
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: bs58.encode(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,
  },
});
```

## 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 });
}
```
