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
Install the SDK
npm i userbase-js
Create a Userbase project
Visit https://userbase.com and sign up
After login, you’ll see a default Starter App → copy its App ID
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.
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
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
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:
Generate a private key for the user
Derive their deterministic smart-contract wallet address
Store both in Userbase (plaintext only for demo!)
Last updated