Step 3 – Add an Off-Chain Identity Layer (Magmar Auth)

Magmar Smart Accounts are wallet-agnostic, so we need a conventional method to map a real-world user to an on-chain account before we can mint NFTs on their behalf. For this tutorial we’ll use Userbase, a quick, server-less identity service to keep things simple. Remember: this is fine for demos, not for production.

3.1 Install and Configure Userbase

  1. Install the SDK

npm i userbase-js
  1. Create a Userbase project

  • Visit https://userbase.com and sign up

  • After login, you’ll see a default Starter App → copy its App ID

  1. Add environment variables

# .env.local
NEXT_PUBLIC_USERBASE_APP_ID=<YOUR_APP_ID>

NEXT_PUBLIC_ exposes the value to the browser—required by the SDK.

  1. Generate an access token

  • In the dashboard, open Account → Access Tokens

  • Label it magmar-get-user and click Generate

Add the token to .env.local:

USERBASE_ACCESS_TOKEN=<YOUR_ACCESS_TOKEN>

3.2 Create an AuthProvider

Inside /common, add AuthProvider.tsx:

import { createContext, useContext, useEffect, useState, ReactNode } from "react";
import userbase from "userbase-js";

interface User {
  username: string;
  isLoggedIn: boolean;
  userId: string;
  scwAddress?: string;     // Smart-contract wallet address
}

interface AuthContextType {
  user: User | null;
  login: (u: User) => void;
  logout: () => void;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);
export const useAuth = () => {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error("useAuth must be used inside AuthProvider");
  return ctx;
};

export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null>(null);

  /* Restore session if browser already has a valid token */
  useEffect(() => {
    userbase
      .init({ appId: process.env.NEXT_PUBLIC_USERBASE_APP_ID! })
      .then((session) => {
        if (session.user) {
          const u: User = {
            username: session.user.username,
            userId: session.user.userId,
            scwAddress: session.user.profile.scwAddress,
            isLoggedIn: true,
          };
          setUser(u);
        }
      })
      .catch(console.error);
  }, []);

  return (
    <AuthContext.Provider
      value={{
        user,
        login: (u) => setUser(u),
        logout: () => setUser(null),
      }}
    >
      {children}
    </AuthContext.Provider>
  );
}

3.3 Wrap the App with AuthProvider

Modify app/layout.tsx (Next 13 App Router):

"use client";

import { WagmiConfig, createConfig, sepolia } from "wagmi";
import { ConnectKitProvider } from "connectkit";
import { AuthProvider } from "@common/AuthProvider";

const config = createConfig({
  appName: "Magmar Gasless NFT Minter",
  chains: [sepolia],
  // … add connectors & providers here
});

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <AuthProvider>
        <WagmiConfig config={config}>
          <ConnectKitProvider mode="dark">
            <body>{children}</body>
          </ConnectKitProvider>
        </WagmiConfig>
      </AuthProvider>
    </html>
  );
}

We restrict chains to Sepolia to keep testnet usage safe.


3.4 Result

Any component can now call useAuth() to read or update the current user:

const { user } = useAuth();
console.log(user?.scwAddress);

You’ve connected an off-chain identity system to Magmar Smart Accounts. Next we’ll create Sign-Up and Login routes that:

  1. Generate a private key for the user

  2. Derive their deterministic smart-contract wallet address

  3. Store both in Userbase (plaintext only for demo!)

Last updated