Skip to main content

Next.js (App Router)

Use the @imtbl/auth-nextjs package for the simplest integration:
npm install @imtbl/auth-nextjs

Setup

// app/layout.tsx
import { ImmutableProvider } from '@imtbl/auth-nextjs';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <ImmutableProvider
          clientId={process.env.NEXT_PUBLIC_IMMUTABLE_CLIENT_ID!}
          redirectUri={`${process.env.NEXT_PUBLIC_URL}/callback`}
          logoutRedirectUri={process.env.NEXT_PUBLIC_URL!}
        >
          {children}
        </ImmutableProvider>
      </body>
    </html>
  );
}

Using the Hooks

// app/page.tsx
'use client';

import { useAuth, useWallet } from '@imtbl/auth-nextjs';

export default function Home() {
  const { user, login, logout, isLoading } = useAuth();
  const { address, connect } = useWallet();

  if (isLoading) return <div>Loading...</div>;

  if (!user) {
    return <button onClick={login}>Login with Passport</button>;
  }

  return (
    <div>
      <p>Welcome, {user.email}</p>
      {address ? (
        <p>Wallet: {address}</p>
      ) : (
        <button onClick={connect}>Connect Wallet</button>
      )}
      <button onClick={logout}>Logout</button>
    </div>
  );
}

Callback Route

// app/callback/page.tsx
'use client';

import { AuthCallback } from '@imtbl/auth-nextjs';

export default function Callback() {
  return <AuthCallback redirectTo="/" errorRedirectTo="/?error=auth" />;
}

Environment Variables

NEXT_PUBLIC_IMMUTABLE_CLIENT_ID=your_client_id
NEXT_PUBLIC_URL=http://localhost:3000

Vite

npm install @imtbl/auth @imtbl/wallet

Initialization

// src/lib/immutable.ts
import { Auth } from '@imtbl/auth';
import { connectWallet, ZkEvmProvider } from '@imtbl/wallet';

export const auth = new Auth({
  clientId: import.meta.env.VITE_CLIENT_ID,
  redirectUri: `${window.location.origin}/callback`,
  logoutRedirectUri: window.location.origin,
  audience: 'platform_api',
  scope: 'openid offline_access email transact',
});

let providerPromise: Promise<ZkEvmProvider> | null = null;

export function getProvider() {
  if (!providerPromise) {
    providerPromise = connectWallet({ auth });
  }
  return providerPromise;
}

Node.js (Backend)

For server-side operations like minting:
import { Environment } from '@imtbl/config';

const API_BASE = Environment.SANDBOX === 'sandbox' 
  ? 'https://api.sandbox.immutable.com'
  : 'https://api.immutable.com';

async function mintNFT(address: string, tokenId: string, metadata: object) {
  const response = await fetch(
    `${API_BASE}/v1/chains/imtbl-zkevm-testnet/collections/${CONTRACT}/nfts/mint-requests`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-immutable-api-key': process.env.IMMUTABLE_SECRET_KEY!,
      },
      body: JSON.stringify({
        assets: [{
          owner_address: address,
          token_id: tokenId,
          metadata,
        }],
      }),
    }
  );
  
  if (!response.ok) throw new Error('Mint failed');
  return response.json();
}

wagmi Integration

Use Passport with wagmi for React hooks:
import { createConfig, http } from 'wagmi';
import { immutableZkEvmTestnet } from 'wagmi/chains';
import { injected } from 'wagmi/connectors';

// Passport injects as EIP-1193 provider
export const wagmiConfig = createConfig({
  chains: [immutableZkEvmTestnet],
  connectors: [injected()],
  transports: {
    [immutableZkEvmTestnet.id]: http(),
  },
});
// In your component
import { useAccount, useConnect, useSendTransaction } from 'wagmi';

function WalletButton() {
  const { address, isConnected } = useAccount();
  const { connect, connectors } = useConnect();
  
  if (isConnected) {
    return <span>{address?.slice(0, 8)}...</span>;
  }
  
  return (
    <button onClick={() => connect({ connector: connectors[0] })}>
      Connect
    </button>
  );
}

Next Steps