Parse-server-migration-react-client MagicLink Email Broken

I’m attempting to migrate my server to self hosted, and I’m re-implementing authentication. I’ve gotten metamask login to work properly, but MagicLink returns an error.

Screen Shot 2023-03-16 at 2.42.55 PM

To reproduce, you can clone the example repository from the tutorial (found here) and try the MagicLink login option and see that it fails with an error.

Where do I configure the email?

Hi @snazzx

You have to customize the authentication code in this file of the repo to work with other providers currently it is set to work with default provider.

Hi @johnversus, thanks for the response - I’m still attempting to find out how to add a custom provider for the auth using the new Moralis Auth API and the parse server migration.

I’ve successfully logged in existing users with Metamask, but not MagicLink. There’s a note about authentication in the migration docs, but that doesn’t include custom providers, and the only tutorial was able to find is for NextJS which obviously doesn’t work with the Parse server setup. The V1 version of the SDK had the Magic Connector already built (here’s the GitHub link ), and I can’t find anything for self-hosted.

When I try to write the code for the MagicLink provider myself, I’m using my old (from pre-migration) magic connector like so:

import { ethers } from 'ethers';
import AbstractWeb3Connector from './AbstractWeb3Connector';

export default class MagicWeb3Connector extends AbstractWeb3Connector {
  type = 'MagicLink';
  async activate({
    apiKey,
    newSession,

    // Options passed to loginWithMagicLink
    email,
    phone,
    showUI,
    redirectURI,
    loginCredential, // required for loginWithCredential after an email login callback

    // Options passed to new Magic creation
    network,
    locale,
    extensions,
    testMode,
    endpoint,
  } = {}) {
    let magic = null;
    let ether = null;

    if (!apiKey) {
      throw new Error('"apiKey" not provided, please provide Api Key');
    }
    if (!network) {
      throw new Error('"network" not provided, please provide network');
    }

    let Magic;
    try {
      Magic = require('magic-sdk')?.Magic;
    } catch (error) {
      // Do nothing. User might not need walletconnect
    }

    if (!Magic) {
      Magic = window?.Magic;
    }

    if (!Magic) {
      throw new Error('Cannot enable via MagicLink: dependency "magic-sdk" is missing');
    }

    try {
      magic = new Magic(apiKey, {
        network: network,
        ...(locale && { locale }),
        ...(extensions && { extensions }),
        ...(testMode && { testMode }),
        ...(endpoint && { endpoint }),
      });

      if (newSession) {
        if (magic?.user) {
          try {
            await magic?.user?.logout();
          } catch (error) {
            // Do nothing
          }
        }
      }

      ether = new ethers.providers.Web3Provider(magic.rpcProvider);
      if (loginCredential) {
        console.log("attempting login with credential")
        await magic.auth.loginWithCredential('loginCredential');
      } else if (email) {
        await magic.auth.loginWithMagicLink({
          email: email,
          ...(showUI && { showUI }),
          ...(redirectURI && { redirectURI }),
        });
      } else if (phone) {
        await magic.auth.loginWithSMS({
          phoneNumber: phone,
          ...(showUI && { showUI }),
          ...(redirectURI && { redirectURI }),
        });
      } else {
        throw new Error("Neither phone nor email were provided.")
      }
    } catch (err) {
      throw new Error('Error during enable via MagicLink, please double check network and apikey');
    }

    const loggedIn = await magic.user.isLoggedIn();
    if (loggedIn) {
      const signer = ether.getSigner();
      const { chainId } = await ether.getNetwork();
      const address = (await signer.getAddress()).toLowerCase();
      // Assign Constants
      this.account = address;
      this.provider = ether.provider;
      this.chainId = `0x${chainId.toString(16)}`;
      // Assign magic user for deactivation
      this.magicUser = magic;
      this.subscribeToEvents(this.provider);
      return {
        provider: this.provider,
        account: this.account,
        chainId: this.chainId,
      };
    }
    throw new Error('Error during enable via MagicLink, login to magic failed');
  }

  deactivate = async () => {
    this.unsubscribeToEvents(this.provider);
    if (this.magicUser) {
      await this.magicUser.user.logout();
    }
    this.account = null;
    this.chainId = null;
    this.provider = null;
  };
}

And then attempting to use it in the new handleAuth function that you pointed out:

const loginWithEmail = async () => {
    let magicConnector = new MagicWeb3Connector()
    let magicAccount = await magicConnector.activate({
      email: email,
      apiKey: "", //redacted
      network: "mainnet",
      showUI: true
    })
    console.log(magicAccount)
    const { account, chainId, provider } = magicAccount;

    try {
      // Enable web3 to get user address and chain
      await enableWeb3({ throwOnError: true, provider });

      if (!account) {
        throw new Error('Connecting to chain failed, as no connected account was found');
      }
      if (!chainId) {
        throw new Error('Connecting to chain failed, as no connected chain was found');
      }

      // Get message to sign from the auth api
      const { message } = await Moralis.Cloud.run('requestMessage', {
        address: account,
        chain: parseInt(chainId, 16),
        networkType: 'evm',
      });

      // Authenticate and login via parse
      await authenticate({
        signingMessage: message,
        throwOnError: true,
      });
    } catch (error) {
      console.log(error)
    } finally {
      handleLoginSuccess()
    }
  }

This does login the user from the Magic point of view (because it console logs the logged in account successfully), but Moralis still opens the Metamask window to sign – I think the problem is probably in the requestMessage function from the Auth API.

Any idea the next steps for making the new Moralis Auth API work with MagicLink?

I this authenticate function include the provide param. By default it is set to metamask.

Ok that is working for the email login, thank you! However, now the phone number login still isn’t working. Previously I was able to create my own connector, as it was recommended in these forums, but with the new auth api it doesn’t look like it’s extensible – is there a way to write my own connector or extend the magicLink implementation?

Does the new auth api include custom connectors?

// Get message to sign from the auth api
      const { message } = await Moralis.Cloud.run('requestMessage', {
        address: account,
        provider: "magicLink",
        chain: parseInt(chainId, 16),
        networkType: 'evm',
      });

      // Authenticate and login via parse
      await authenticate({
        signingMessage: message,
        provider: "magicLink",
        email: email,
        apiKey: "",
        network: "mainnet",
        showUI: true,
        throwOnError: true,
      });