nearprotocol

how do I verify NEAR login was successful?


Once user logs in with their NEAR account from app, I want to save the wallet address to their account document.

The successurl is one of my API endpoint. It gets populated with account_id and all_keys query param.

How do I verify that the login was successful? Because anyone can add those param it seems, and link another person's wallet.

on client side:

async function connectWallet(uid: string) {
  // connect to NEAR
  const nearConnection = await connectNear();

  // create wallet connection
  const walletConnection = new WalletConnection(
    nearConnection,
    appConfig.title
  );

  // const walletConnection = new WalletConnection(nearConnection);
  walletConnection.requestSignIn({
    methodNames: [], // optional
    successUrl: `${getApplicationUrl()}/api/user/${uid}/connectNearWallet?success=1`, // optional redirect URL on success
    failureUrl: `${getApplicationUrl()}/dashboard/profile?tab=web3&success=0`, // optional redirect URL on failure
  });
}

export default connectWallet;

my API (successurl)

import { string } from 'yup';

import { getApplicationUrl } from '@/config/urls';
import withHandler from '@/server/api/withHandler';
import { getUser } from '@/server/user';

const REDIRECT_URL = `${getApplicationUrl()}/dashboard/profile?tab=web3`;

export default withHandler({
  get: async (req, res) => {
    const { uid, account_id, all_keys } = req.query;

    try {
      await string().validate(uid);
      await string().validate(account_id);
      await string().validate(all_keys);
    } catch (error) {
      return res.redirect(`${REDIRECT_URL}&success=0&error=invalid_params`);
    }

    // get the user;
    const user = await getUser(uid as string);

    if (!user?.uid) {
      return res.redirect(`${REDIRECT_URL}&success=0&error=user_not_found`);
    }

    // how do i verify?

    // add the wallet to the user account;
  },
});

Solution

  • "SignIn" in NEAR does the following thing:

    1. A new keypair (public and private ed25519 keys) is generated
    2. The private key is stored locally (e.g. in the browser's local storage)
    3. Redirect the user to some wallet with the request to add the public key to their account
    4. Wallet constructs a transaction with ADD_KEY action to the user-owned account and confirms it with the user
    5. Once the user confirms it, Wallet submits the transaction, and once it is successful, it redirects to the success_url with the information to which account the public key was added
    6. Your app now has the account id, the public key from the URL, and private key in the local storage

    Thus, you should be able to check if you actually have the private key, and if so, it is a legit user. To check if you have the private key, you should be able to use Signer.getPublicKey. Here is a pseudocode (I don't remember what is the type of all_keys):

    if (walletConnection.connection.signer.getPublicKey(accountId, network) == all_keys[0]) {
      console.log("All good");
    }