NERO
Authentication

Social Login Flow

Implement social login with OAuth redirect flows, email OTP, phone OTP, and custom JWT

Social Login Flow

React Implementation

The useNeroConnect hook provides the simplest way to add social login in React:

import { useNeroConnect } from "@nerochain/mpc-sdk/react";

function LoginButton() {
  const { connect, handleCallback, isConnecting, error } = useNeroConnect();

  return (
    <div>
      <button
        onClick={() => connect("google")}
        disabled={isConnecting}
      >
        {isConnecting ? "Connecting..." : "Login with Google"}
      </button>
      {error && <p>{error.message}</p>}
    </div>
  );
}

Available useNeroConnect Methods

MethodPurpose
connect(provider, redirectUri?)Initiates OAuth flow for a provider
handleCallback(provider, code, state, redirectUri?)Processes the OAuth redirect
loginWithEmail(email, type?)Starts email OTP or magic link flow
verifyEmailLogin(email, code)Completes email verification
loginWithPhone(phoneNumber)Initiates SMS OTP
verifyPhoneLogin(phoneNumber, code)Completes phone verification
loginWithCustomJwt(options)Authenticates with an external JWT
isConnectingBoolean indicating active authentication
errorContains the last authentication error

Email OTP Flow

function EmailLogin() {
  const { loginWithEmail, verifyEmailLogin, isConnecting } = useNeroConnect();
  const [step, setStep] = useState<"email" | "code">("email");
  const [email, setEmail] = useState("");
  const [code, setCode] = useState("");

  const handleSendOTP = async () => {
    await loginWithEmail(email);
    setStep("code");
  };

  const handleVerify = async () => {
    await verifyEmailLogin(email, code);
  };

  if (step === "email") {
    return (
      <div>
        <input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" />
        <button onClick={handleSendOTP} disabled={isConnecting}>Send Code</button>
      </div>
    );
  }

  return (
    <div>
      <input value={code} onChange={(e) => setCode(e.target.value)} placeholder="Verification Code" />
      <button onClick={handleVerify} disabled={isConnecting}>Verify</button>
    </div>
  );
}

Phone OTP Flow

function PhoneLogin() {
  const { loginWithPhone, verifyPhoneLogin, isConnecting } = useNeroConnect();
  const [phone, setPhone] = useState("");
  const [code, setCode] = useState("");
  const [otpSent, setOtpSent] = useState(false);

  const handleSend = async () => {
    await loginWithPhone(phone);
    setOtpSent(true);
  };

  const handleVerify = async () => {
    await verifyPhoneLogin(phone, code);
  };

  if (!otpSent) {
    return (
      <div>
        <input value={phone} onChange={(e) => setPhone(e.target.value)} placeholder="+1234567890" />
        <button onClick={handleSend} disabled={isConnecting}>Send OTP</button>
      </div>
    );
  }

  return (
    <div>
      <input value={code} onChange={(e) => setCode(e.target.value)} placeholder="OTP Code" />
      <button onClick={handleVerify} disabled={isConnecting}>Verify</button>
    </div>
  );
}

Vanilla JS Implementation

Use the core SDK directly for non-React applications:

import { NeroMpcSDK } from "@nerochain/mpc-sdk";

const sdk = new NeroMpcSDK({
  backendUrl: "https://your-backend.example.com",
  chainId: 689,
});

await sdk.init();

OAuth Redirect Flow

await sdk.loginWithGoogle();

After the provider redirects back to your application, handle the callback:

const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get("code");
const state = urlParams.get("state");

if (code && state) {
  const result = await sdk.handleOAuthCallback({
    provider: "google",
    code,
    state,
  });

  if (result.requiresDKG) {
    await sdk.generateWallet();
  }
}

Email OTP (Vanilla JS)

await sdk.loginWithEmail("user@example.com");

const result = await sdk.verifyEmailLogin("user@example.com", "123456");

Custom JWT

await sdk.loginWithCustomJwt({
  verifierId: "your-verifier-id",
  idToken: "your-existing-jwt-token",
});

Post-Login: Wallet Generation

After authentication, check the requiresDKG flag. When true, the user has no stored key material on the backend (typically on first login) and you must call generateWallet():

const result = await sdk.handleOAuthCallback({ provider, code, state });

if (result.requiresDKG) {
  const wallet = await sdk.generateWallet();
  console.log("Wallet address:", wallet.eoaAddress);
}

Handling the Redirect

After OAuth authentication, the provider redirects back to your application with code and state query parameters. Ensure your OAuth redirect URI is registered with the provider and points back to your application URL.

The handleOAuthCallback() method:

  1. Collects a device fingerprint from navigator.userAgent
  2. Exchanges the authorization code via POST /api/v2/auth/social-login
  3. Encrypts and stores access/refresh tokens
  4. Initializes ClientKeyManager for key share storage
  5. Emits the "login" event with user profile data

Pre-built Modal

For a complete login UI with all providers preconfigured, use the modal component:

import { NeroMpcAuthProvider } from "@nerochain/mpc-sdk/react";
import { LoginButton } from "@nerochain/mpc-sdk/modal";

function App() {
  return (
    <NeroMpcAuthProvider config={{ backendUrl: "https://your-backend.example.com", chainId: 689 }}>
      <LoginButton />
    </NeroMpcAuthProvider>
  );
}

The modal displays all configured providers and handles the full authentication flow including redirect handling.

On this page