App Explained

Repository Open Source

Sign In Front End

function SignIn() {
  const [userDetails, setUserDetails] = useContext(AppContext);
  const [isUserWelcomed, setIsUserWelcomed] = useState(null);
  const [wallet, setWallet] = useState(userDetails || null);

  const authenticate = async () => {
    const token = uuidv4();
    openSignatureRequestPopup({
      message: token,
      network: new StacksTestnet(), // for mainnet, `new StacksMainnet()`
      appDetails: {
        name: "My Message Signing App",
        icon: window.location.origin + "/my-app-logo.svg",
      },
      onFinish: async (data) => {
        console.log("Signature of the message", data.signature);
        console.log("Use public key:", data.publicKey);
        console.log("To send", data);
        // console.log("Addr", publicKeyToBtcAddress(data.publicKey), publicKeyToAddress(AddressVersion.TestnetMultiSig, data.publicKey));
        console.log("Addr2", getAddressFromPublicKey(data.publicKey, TransactionVersion.Testnet))
          try {
            await axios.post('/signin', {token, publicKey: data.publicKey, signature: data.signature})

            const updatedWallet = getAddressFromPublicKey(data.publicKey, TransactionVersion.Testnet);
            setWallet(updatedWallet);

            localStorage.setItem('userDetails', updatedWallet);
            setUserDetails(updatedWallet);
            setIsUserWelcomed(true);
          } catch (e) {
            console.error("Failed to login", e);
          }
      },
  });
}

openSignatureRequestPopup creates the necessary data after the pop-up is confirmed. The data contains the publicKey and signature, both for the previous token created.

Then it saves the Stacks address locally and sends to the backend the token, publicKey and signature to the route signin.

Sign In Handler Back End

Gets the token, signature and publicKey from the client, then verifies the message signature using verifyMessageSignatureRsv from the @stacks/encryption library.

If it fails, sends a 401 error status back, else create the cookie session which will be available for 120 seconds.

const {verifyMessageSignatureRsv} = require("@stacks/encryption");

const signinHandler = (req, res) => {
    const {token, signature, publicKey} = req.body
    console.log(req.body);
    if (!verifyMessageSignatureRsv({message: token, publicKey, signature})) {
        // If the username isn't present, return an HTTP unauthorized code
        res.status(401).end()
        return
    }

    // set the expiry time as 120s after the current time
    const now = new Date()
    const expiresAt = new Date(+now + 120 * 1000)
    const wallet = getAddressFromPublicKey(publicKey, TransactionVersion.Testnet);

    // create a session containing information about the user and the expiry time
    const session = new Session(wallet, expiresAt)
    // add the session information to the sessions map
    sessions[token] = session;

    // In the response set a cookie on the client with the name "session_cookie"
    // and the value as the UUID we generated. We also set the expiry time
    res.cookie("session_token", token, {expires: expiresAt})
    res.end()
}

Welcome and Refresh Handlers

After the user gets a cookie session attributed, if he reloads the page or comes back later to the page, the /welcome request will be made. The server checks that there are cookies, the session_token cookie exists, the userSession exists and it didn't expire and sends as confirmation, if all of them are alright, a message Welcome address. If any of the conditions was not met, the server returns the 401 error status.

This can be placed on any action done on the client side. If the client app has an action to collect resources, it will send a message to the server checking if the session is valid and if it is, will collect the resources. If not, will ask the user to confirm his identity again.

Last updated