Integrated Sign-In (Single-Sign-On)

Many dApps already use Sign-In With Ethereum (SIWE) to authenticate their users. The WalletChat widget supports integrated sign-in so users do not have to sign-in to both the main site and widget.

Integrated Sign-In is a Paid Tier Option

Paid Tier customers can set the username and email address of the user within WalletChat.fun if already entered on the customer site. Reach out to contact@walletchat.fun to learn more and get an API Key. Paid Tier API key enables the /name functions outlined in more details at the API documentation: https://api.v2.walletchat.fun/docs/index.html

Paid Tier customers also get access to create users within WalletChat, to streamline new user experience. Terms and conditions apply.

Integration Steps

0) Create the WalletChat User via your Backend (Optional)

If you are using Integrated/Single Sign On - Streamlining the user login to WalletChat upon account creation is possible by creating the WalletChat user prior to performing the SSO steps. This way, when a user creates a profile on your site, they can be fully setup within WalletChat without having to enter the same username/email details in WalletChat!

Caveat: if users enter an ENS name like vitalik.eth, the wallet address used must match. WalletChat doesn't allow usernames to imitate ENS named not actually owned by the wallet.

This step is optional, and requires an API key from WalletChat Labs, Inc. Its only required once during profile creation, or profile updates. By using this you agree to the Terms and conditions to only modify users signed up on your site, and only use email addresses you have verified. Any abuse of these terms will result in revoking the API key without warning.

This step only is required for new/update profile changes and must only be done via the customer backend. Any use of this API key in a Front-End will result in WalletChat having to revoke the API key to prevent any unauthorized access.

Here is an example of a curl POST request which will create a new user in WalletChat, with the given details. Curl is used to here simplify the example, use fetch or your favorite HTTP request library and ensure the Content-Type, Authorization and POST body (data-raw below) are set accordingly.

curl --location 'https://api.v2.walletchat.fun/v1/name'
--header 'Content-Type: application/json'
--header 'Authorization: Bearer <Paid_API_Key>'
--data-raw '{ "address": "0x8077EAceF1695548902Bc1dE0e62DA1f1bAf92e8", 
              "email": "billAndTed@universe.com",
              "signupsite": "app.excellent.com", 
              "domain": "excellent.com", 
              "name": "CyberTed" }'

1) Store the Message to Sign and Signature

After the SIWE message has been signed by the user, the integrating site must store the new message being signed, and signature. For example:

const [messageToSign, setMessageToSign] = useState('')
const [messageSignature, setMesssageSignature] = useState('')

//in event handler for your main site Sign-In
setMessageToSign(message.prepareMessage())
setMesssageSignature(signature)

Currently WalletChat only supports SIWE based signatures for Integrated/Single Sign On. If you have a use case you would like us to support, reach out to contact@walletchat.fun

2) Pass Signature Parameters to Widget

Then pass the following parameters to the widget within the :

<WalletChatProvider>
    <FunctionalComponent {...pageProps} />

    <...your wallet handler>

    <WalletChatWidget
      connectedWallet={
        address && activeConnector && chainId
          ? {
              walletName: activeConnector.name,
              account: address,
              chainId: chain.id,
            }
          : undefined
      }
      signedMessageData={
        messageToSign && messageSignature
          ? {
              signature: messageSignature,
              msgToSign: messageToSign,
            }
          : undefined
      }
    />
</WalletChatProvider>

After this state is set, the user which has signed a message on the customer site, should be auto-logged into WalletChat and can begin conversations!

Example Integration (React)

Example used live at https://sso.walletchat.fun using RainbowKit: (source: https://github.com/Wallet-Chat/nft-marketplace-demo/tree/TestingSIWEonMainPage)

//using RainbowKit (see github for full example)
const { address, connector: activeConnector } = useAccount()
const { chain } = useNetwork()
const [ authStatus, setAuthStatus ] = useState<AuthenticationStatus>('unauthenticated')
const chainId = chain?.id
const [messageToSign, setMessageToSign] = useState('')
const [messageSignature, setMesssageSignature] = useState('')

const authenticationAdapter = createAuthenticationAdapter({
  //using WalletChat nonce is optional currently (we accept SIWE nonce)
  getNonce: async () => {
    let _nonce = ""
    const response = await fetch(`https://api.v2.walletchat.fun/users/${address}/nonce`, {
      method: 'GET',
      headers: { 'Content-Type': 'application/json' },
    })
      .then((response) => response.json())
      .then(async (usersData: { Nonce: string }) => {
        console.log('✅[GET][Nonce]:', usersData)
        _nonce = usersData.Nonce
      })
      .catch((error) => {
        console.log('🚨[GET][Nonce]:', error)
      })
    return _nonce;
  },

  createMessage: ({ nonce, address, chainId }) => {
    setAuthStatus('loading')
    return new SiweMessage({
      domain: window.location.host,
      address,
      statement: 'Sign in with Ethereum to the app.',
      uri: window.location.origin,
      version: '1',
      chainId,
      nonce,
    });
  },

  getMessageBody: ({ message }) => {
    return message.prepareMessage();
  },

  verify: async ({ message, signature }) => {
    // const verifyRes = await fetch('/api/verify', {
    //   method: 'POST',
    //   headers: { 'Content-Type': 'application/json' },
    //   body: JSON.stringify({ message, signature }),
    // });

    //TODO: customer needs to set these two items
    setMessageToSign(message.prepareMessage())
    setMesssageSignature(signature)

    setAuthStatus('authenticated')

    return true;
  },

  signOut: async () => {
    //await fetch('/api/logout');
    setAuthStatus('unauthenticated')
  },
});

  return (
    <HotkeysProvider>
      <ThemeProvider
        attribute="class"
        defaultTheme="dark"
        value={{
          dark: darkTheme.className,
          light: 'light',
        }}
      >
        <snip...>
          <CartProvider>
            <Tooltip.Provider>
              <RainbowKitProvider
                chains={chains}
                theme={rainbowKitTheme}
                modalSize="compact"
              >
                <ToastContextProvider>
                <WalletChatProvider>
                    <FunctionalComponent {...pageProps} />

                    <WagmiConfig client={wagmiClient}>
                      <RainbowKitAuthenticationProvider
                        adapter={authenticationAdapter}
                        status={authStatus}
                      >
                        <RainbowKitProvider chains={chains}>
                          <Component {...pageProps} />
                        </RainbowKitProvider>
                      </RainbowKitAuthenticationProvider>
                    </WagmiConfig>

                    <WalletChatWidget
                      connectedWallet={
                        address && activeConnector && chainId
                          ? {
                              walletName: activeConnector.name,
                              account: address,
                              chainId: chain.id,
                            }
                          : undefined
                      }
                      signedMessageData={
                        messageToSign && messageSignature
                          ? {
                              signature: messageSignature,
                              msgToSign: messageToSign,
                            }
                          : undefined
                      }
                    />
                  </WalletChatProvider>
                </ToastContextProvider>
              </RainbowKitProvider>
            </Tooltip.Provider>
          </CartProvider>
        </ReservoirKitProvider>
      </ThemeProvider>
    </HotkeysProvider>
  )

Last updated