> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reown.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sign In With Ethereum

<Warning>
  **SIWE is being migrated to SIWX.** We recommend using [SIWX (Sign In With X)](/appkit/authentication/siwx/default) for new implementations as it provides multichain authentication support. For existing SIWE implementations, see the [migration guide](/appkit/authentication/siwx/default#migrating-from-siwe-to-siwx).
</Warning>

AppKit provides a simple solution for integrating with "Sign In With Ethereum" (SIWE), a new form of authentication that enables users to control their digital identity with their Ethereum account.
SIWE is a standard also known as [EIP-4361](https://docs.login.xyz/general-information/siwe-overview/eip-4361).

## One-Click Auth

**One-Click Auth** represents a key advancement within WalletConnect v2, streamlining the user authentication process in AppKit by enabling them to seamlessly connect with a wallet and sign a SIWE message with just one click.

Connecting a wallet, proving control of an address with an off-chain signature, authorizing specific actions. These are the kinds of authorizations that can be encoded as ["ReCaps"](https://eips.ethereum.org/EIPS/eip-5573). ReCaps are permissions for a specific website or dapp that can be compactly encoded as a long string in the message you sign and translated by any wallet into a straight-forward one-sentence summary.
WalletConnect uses permissions expressed as ReCaps to enable a One-Click Authentication.

## Installation

<Tabs>
  <Tab title="One-Click Auth">
    <CodeGroup>
      ```bash npm theme={null}
      npm install @reown/appkit-siwe siwe
      ```

      ```bash Yarn theme={null}
      yarn add @reown/appkit-siwe siwe
      ```

      ```bash Bun theme={null}
      bun a @reown/appkit-siwe siwe
      ```

      ```bash pnpm theme={null}
      pnpm add @reown/appkit-siwe siwe
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Legacy">
    <Warning>For a better UX we recommend using One-Click Auth.</Warning>

    Install the AppKit SIWE package, additionally we also recommend installing [siwe](https://docs.login.xyz/) which will abstract a lot of the required logic.

    <CodeGroup>
      ```bash npm theme={null}
      npm install @reown/appkit-siwe siwe
      ```

      ```bash Yarn theme={null}
      yarn add @reown/appkit-siwe siwe
      ```

      ```bash Bun theme={null}
      bun a @reown/appkit-siwe siwe
      ```

      ```bash pnpm theme={null}
      pnpm add @reown/appkit-siwe siwe
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## Configure your SIWE Client

<Tabs>
  <Tab title="One-Click Auth">
    <Note>
      If you are not using our library on the server-side, please normalize the address with eip-55 in the createMessage function. You can check our example for that Functionality.
    </Note>

    ```ts theme={null}
    import { SiweMessage } from "siwe";
    import {
      type SIWESession,
      type SIWEVerifyMessageArgs,
      type SIWECreateMessageArgs,
      createSIWEConfig,
      formatMessage,
    } from "@reown/appkit-siwe";

    const BASE_URL = "http://localhost:8080";

    /* Function that returns the user's session - this should come from your SIWE backend */
    async function getSession() {
      const res = await fetch(BASE_URL + "/session", {
        method: "GET",
        headers: {
          "Content-Type": "application/json",
        },
        credentials: "include",
      });
      if (!res.ok) {
        throw new Error("Network response was not ok");
      }

      const data = await res.json();

      const isValidData =
        typeof data === "object" &&
        typeof data.address === "string" &&
        typeof data.chainId === "number";

      return isValidData ? (data as SIWESession) : null;
    }

    /* Use your SIWE server to verify if the message and the signature are valid */
    const verifyMessage = async ({ message, signature }: SIWEVerifyMessageArgs) => {
      try {
        const response = await fetch(BASE_URL + "/verify", {
          method: "POST",
          headers: {
            Accept: "application/json",
            "Content-Type": "application/json",
          },
          mode: "cors",
          body: JSON.stringify({ message, signature }),
          credentials: "include",
        });

        if (!response.ok) {
          return false;
        }

        const result = await response.json();
        return result === true;
      } catch (error) {
        return false;
      }
    };

    // Check the full example for signOut and getNonce functions ...

    /* Create a SIWE configuration object */
    export const siweConfig = createSIWEConfig({
      getMessageParams: async () => ({
        domain: window.location.host,
        uri: window.location.origin,
        chains: [1, 2020],
        statement: "Please sign with your account",
      }),
      createMessage: ({ address, ...args }: SIWECreateMessageArgs) =>
        formatMessage(args, address),

      getNonce: async () => {
        //This is only an example, substitute it with your actual nonce getter.
        const nonce = "YOUR_NONCE_GETTER";
        if (!nonce) {
          throw new Error("Failed to get nonce!");
        }
        return nonce;
      },
      getSession,
      verifyMessage,
      signOut: async () => {
        //Example
        // Implement your Sign out function
      },
    });
    ```

    ## Server Side

    Setting up a backend server using Express for a web application that interacts with the Siwe protocol.

    ### Routes:

    * GET '/nonce': Generates and returns a nonce (single-use random number).
    * POST '/verify': Uses the Siwe protocol to verify the message, requiring a signature (the one you are going to approve throw the UX) and a nonce stored in the session.
    * GET '/session': Retrieves the stored Siwe object from the session.
    * GET '/signout': Clears the session.

    ```ts theme={null}
    import cors from "cors";
    import express from "express";
    import Session from "express-session";
    import { generateNonce } from "siwe";
    import {
      /*verifySignature,*/
      getAddressFromMessage,
      getChainIdFromMessage,
    } from "@reown/appkit-siwe";
    import { createPublicClient, http } from "viem";

    const app = express();

    const projectId = "YOUR_PROJECT_ID";

    // configure cors and sessions
    app.use(
      cors({
        origin: "http://localhost:5173", // frontend URL
        credentials: true,
      })
    );
    app.use(express.json());
    app.use(
      Session({
        name: "siwe-quickstart",
        secret: "siwe-quickstart-secret",
        resave: true,
        saveUninitialized: true,
        cookie: { secure: false, sameSite: true },
      })
    );

    app.get("/nonce", function (_, res) {
      res.setHeader("Content-Type", "text/plain");
      res.send(generateNonce());
    });

    // verify the message
    app.post("/verify", async (req, res) => {
      try {
        if (!req.body.message) {
          return res.status(400).json({ error: "SiweMessage is undefined" });
        }

        // save the session with the address and chainId (SIWESession)
        req.session.siwe = { address, chainId };
        req.session.save(() => res.status(200).send(true));

        const message = req.body.message;
        const signature = req.body.signature;
        const address = getAddressFromMessage(message);
        let chainId = getChainIdFromMessage(message);

        // for the moment, the verifySignature is not working with social logins and emails  with non deployed smart accounts
        // for this reason we recommend using the viem to verify the signature
        const publicClient = createPublicClient({
          transport: http(
            `https://rpc.walletconnect.org/v1/?chainId=${chainId}&projectId=${projectId}`
          ),
        });
        const isValid = await publicClient.verifyMessage({
          message,
          address,
          signature,
        });
        if (!isValid) {
          // throw an error if the signature is invalid
          throw new Error("Invalid signature");
        }
        if (chainId.includes(":")) {
          chainId = chainId.split(":")[1];
        }

        // Convert chainId to a number
        chainId = Number(chainId);

        if (isNaN(chainId)) {
          throw new Error("Invalid chainId");
        }

        // save the session with the address and chainId (SIWESession)
        req.session.siwe = { address, chainId };
        req.session.save(() => res.status(200).send(true));
      } catch (e) {
        // clean the session
        req.session.siwe = null;
        req.session.nonce = null;
        req.session.save(() => res.status(500).json({ message: e.message }));
      }
    });

    /// ... check the github repository for the others endpoints

    // get the session
    app.get("/session", (req, res) => {
      res.setHeader("Content-Type", "application/json");
      res.send(req.session.siwe);
    });
    ```

    Check the github full example to see the full flow working: [siwe-quickstart](https://github.com/WalletConnect/web-examples/tree/main/dapps/appkit-siwe/react/)

    ### `verifySignature`

    Verify a SIWE signature.

    ```ts theme={null}
    import { createPublicClient, http } from "viem";

    const publicClient = createPublicClient({
      transport: http(
        `https://rpc.walletconnect.org/v1/?chainId=${chainId}&projectId=${projectId}`
      ),
    });
    const isValid = await publicClient.verifyMessage({
      message,
      address: address as `0x${string}`,
      signature: signature as `0x${string}`,
    });

    // The verifySignature is not working with social logins and emails with non deployed smart accounts
    // for this reason we recommend using the viem to verify the signature
    // import { verifySignature } from '@reown/appkit-siwe'
    // const isValid = await verifySignature({ address, message, signature, chainId, projectId })
    ```
  </Tab>

  <Tab title="Legacy">
    With help of the [siwe package](https://docs.login.xyz/sign-in-with-ethereum/quickstart-guide/implement-the-frontend) we will create the required configuration for AppKit.

    <Info>
      The nonce and verification process will be implemented in your backend. [Read more.](https://docs.login.xyz/sign-in-with-ethereum/quickstart-guide/implement-the-backend)
    </Info>

    ```ts theme={null}
    import { SiweMessage } from 'siwe'
    import type { SIWECreateMessageArgs, SIWEVerifyMessageArgs } from '@reown/appkit-siwe'

    /* Function that creates a SIWE message */
    function createMessage({ nonce, address, chainId }: SIWECreateMessageArgs){
      const message = new SiweMessage({
        version: '1',
        domain: window.location.host,
        uri: window.location.origin,
        address,
        chainId,
        nonce,
        statement: 'Sign in With Ethereum.'
      })

      return message.prepareMessage()
    }

    /* Function that returns the user's session */
    async function getSession(){
      //...
    }

    /* Use your SIWE server to verify if the message and the signature are valid */
    async function verifyMessage({ message, signature }: SIWEVerifyMessageArgs){
      try {
        const isValid = await validateMessage({ message, signature })

        return isValid
      } catch (error) {
        return false
      }
    },

    /* Create a SIWE configuration object */
    export const siweConfig = createSIWEConfig({
      createMessage,
      getNonce: async () => { //This is only an example, substitute it with your actual nonce getter.
        const nonce = "YOUR_NONCE_GETTER"
        if (!nonce) {
          throw new Error('Failed to get nonce!')
        }
        return nonce
      },
      getSession,
      verifyMessage,
      signOut: async () => { //Example
        // Implement your Sign out function
      }
    })
    ```
  </Tab>
</Tabs>

## Initialize AppKit with your `siweConfig`

```ts theme={null}
// Pass your siweConfig inside the createAppKit() function
const modal = createAppKit({
  adapters: [wagmiAdapter], //or your Ethers adapter
  projectId,
  networks: [mainnet, arbitrum],
  defaultNetwork: mainnet,
  features: {
    analytics: true, // Optional - defaults to your Cloud configuration
  },
  siweConfig: siweConfig, // pass your siweConfig
});
```

## SIWE Config Parameters

<Tabs>
  <Tab title="One-Click Auth">
    #### getMessageParams `() => Promise<{ domain: string, uri: string, chains: number[], statement: string }>`

    Parameters to create the SIWE message internally.

    #### getNonce `() => Promise<string>`

    The getNonce method functions as a safeguard against spoofing, akin to a CSRF token. The siwe package provides a generateNonce() helper, or you can utilize an existing CSRF token from your backend if available.

    #### createMessage `(args: SIWECreateMessageArgs) => string`

    The official siwe package offers a straightforward method for generating an EIP-4361-compatible message, which can subsequently be authenticated using the same package. The nonce parameter is derived from your getNonce endpoint, while the address and chainId variables are sourced from the presently connected wallet.

    #### verifyMessage `(args: SIWEVerifyMessageArgs) => Promise<boolean>`

    The function to ensure the message is valid, has not been tampered with, and has been appropriately signed by the wallet address.

    #### getSession `() => Promise<SIWESession | null>`

    The backend session should store the associated address and chainId and return it via the `getSession` method.

    #### signOut `() => Promise<boolean>`

    The users session can be destroyed calling `signOut`.

    #### onSignIn `(session?: SIWESession) => void`

    Callback when user signs in (Optional).

    #### onSignOut `() => void`

    Callback when user signs out (Optional).

    #### signOutOnDisconnect `boolean`

    * defaults to `true`

    Whether or not to sign out when the user disconnects their wallet (Optional).
  </Tab>

  <Tab title="Legacy">
    #### getNonce `() => Promise<string>`

    The getNonce method functions as a safeguard against spoofing, akin to a CSRF token. The siwe package provides a generateNonce() helper, or you can utilize an existing CSRF token from your backend if available.

    #### createMessage `(args: SIWECreateMessageArgs) => string`

    The official siwe package offers a straightforward method for generating an EIP-4361-compatible message, which can subsequently be authenticated using the same package. The nonce parameter is derived from your getNonce endpoint, while the address and chainId variables are sourced from the presently connected wallet.

    #### verifyMessage `(args: SIWEVerifyMessageArgs) => Promise<boolean>`

    The `verifyMessage` method should lean on the siwe package's new

    ```js theme={null}
    SiweMessage(message).validate(signature);
    ```

    to ensure the message is valid, has not been tampered with, and has been appropriately signed by the wallet address.

    #### getSession `() => Promise<SIWESession | null>`

    The backend session should store the associated address and chainId and return it via the `getSession` method.

    #### signOut `() => Promise<boolean>`

    The users session can be destroyed calling `signOut`.

    #### onSignIn `(session?: SIWESession) => void`

    Callback when user signs in (Optional).

    #### onSignOut `() => void`

    Callback when user signs out (Optional).

    #### signOutOnDisconnect `boolean`

    * defaults to `true`

    Whether or not to sign out when the user disconnects their wallet (Optional).
  </Tab>
</Tabs>
