|
| 1 | +import CTABanner from '@site/src/components/CTABanner'; |
| 2 | + |
| 3 | +# Passwordless Authentication with MetaMask & Nillion |
| 4 | + |
| 5 | + |
| 6 | +## Introduction |
| 7 | + |
| 8 | +This guide demonstrates how to build a passwordless authentication system using **MetaMask** for identity and **Nillion's Network User Credentials (NUC)** for secure, decentralized authentication. Instead of traditional passwords, users authenticate using their Ethereum wallet signatures. |
| 9 | + |
| 10 | + |
| 11 | +## MetaMask for Decentralized Identity (DID) |
| 12 | + |
| 13 | +### What is a DID? |
| 14 | + |
| 15 | +A **Decentralized Identifier (DID)** is a globally unique identifier that doesn't require a centralized authority. In our implementation, MetaMask wallet signatures are used to generate a DID that uniquely identifies users across the Nillion network. |
| 16 | + |
| 17 | +### What are NUCs (Network User Credentials)? |
| 18 | + |
| 19 | +**NUCs** are Nillion's cryptographic credentials that enable: |
| 20 | +- Passwordless authentication via wallet signatures |
| 21 | +- Secure access to Nillion's privacy-preserving infrastructure |
| 22 | +- Identity verification without storing passwords or sensitive data |
| 23 | + |
| 24 | +The NUC SDK abstracts the complexity of cryptographic operations, providing a simple interface for Web3 authentication. |
| 25 | + |
| 26 | +--- |
| 27 | +Full code on github [Here](https://github.com/geniusyinka/nillion-mm-demo) |
| 28 | + |
| 29 | +``` |
| 30 | +cd nillion-mm-demo |
| 31 | +pnpm install |
| 32 | +pnpm dev |
| 33 | +``` |
| 34 | + |
| 35 | + |
| 36 | +## Step By Step Implementation |
| 37 | + |
| 38 | +### Prerequisites |
| 39 | + |
| 40 | + |
| 41 | +This guide assumes you have a nextjs app already set up. Otherwise install NextJS: |
| 42 | +```bash |
| 43 | +npx create-next-app@latest my-app --yes |
| 44 | +``` |
| 45 | + |
| 46 | +Install the required dependencies: |
| 47 | + |
| 48 | +```bash |
| 49 | +npm install @nillion/nuc @nillion/secretvaults viem @tanstack/react-query |
| 50 | +``` |
| 51 | + |
| 52 | +# Active NilDB Subcription |
| 53 | +Head over to [subscription.nillion.com](https://subscription.nillion.com) and subscribe to Nildb. |
| 54 | + |
| 55 | +--- |
| 56 | + |
| 57 | +### 1. Configure Nillion Network |
| 58 | + |
| 59 | +Create a configuration file for Nillion network endpoints: |
| 60 | + |
| 61 | +```typescript |
| 62 | +// src/config.ts |
| 63 | +export const NETWORK_CONFIG = { |
| 64 | + chainId: "nillion-chain-testnet-1", |
| 65 | + nilchain: "http://rpc.testnet.nilchain-rpc-proxy.nilogy.xyz", |
| 66 | + nilauth: "https://nilauth.sandbox.app-cluster.sandbox.nilogy.xyz", |
| 67 | + nildb: [ |
| 68 | + "https://nildb-stg-n1.nillion.network", |
| 69 | + "https://nildb-stg-n2.nillion.network", |
| 70 | + "https://nildb-stg-n3.nillion.network", |
| 71 | + ], |
| 72 | +}; |
| 73 | +``` |
| 74 | + |
| 75 | +### 2. Connect MetaMask & Create NUC Signer |
| 76 | + |
| 77 | +Set up the MetaMask connection and NUC signer: |
| 78 | + |
| 79 | +```typescript |
| 80 | +// src/context/NillionContext.tsx |
| 81 | +import { Signer } from "@nillion/nuc"; |
| 82 | +import { createWalletClient, custom } from "viem"; |
| 83 | +import { mainnet } from "viem/chains"; |
| 84 | + |
| 85 | +const connectMetaMask = async () => { |
| 86 | + if (!window.ethereum) { |
| 87 | + throw new Error("MetaMask not installed"); |
| 88 | + } |
| 89 | + |
| 90 | + // Create wallet client with viem |
| 91 | + const walletClient = createWalletClient({ |
| 92 | + chain: mainnet, |
| 93 | + transport: custom(window.ethereum), |
| 94 | + }); |
| 95 | + |
| 96 | + const [account] = await walletClient.requestAddresses(); |
| 97 | + |
| 98 | + // Create Nillion NUC Signer from MetaMask |
| 99 | + const nucSigner = Signer.fromWeb3({ |
| 100 | + getAddress: async () => account, |
| 101 | + signTypedData: async (domain, types, message) => { |
| 102 | + return walletClient.signTypedData({ |
| 103 | + account, |
| 104 | + domain, |
| 105 | + types, |
| 106 | + primaryType: Object.keys(types).find(k => k !== "EIP712Domain"), |
| 107 | + message, |
| 108 | + }); |
| 109 | + }, |
| 110 | + }); |
| 111 | + |
| 112 | + // Generate DID from signer |
| 113 | + const did = await nucSigner.getDid(); |
| 114 | + console.log("User DID:", did.didString); |
| 115 | + |
| 116 | + return { signer: nucSigner, did: did.didString, address: account }; |
| 117 | +}; |
| 118 | +``` |
| 119 | + |
| 120 | +### 3. Initialize Nillion Session |
| 121 | + |
| 122 | +Use the NUC signer to authenticate with Nillion: |
| 123 | + |
| 124 | +```typescript |
| 125 | +// src/hooks/useNillionClient.ts |
| 126 | +import { NillionClient } from "@nillion/secretvaults"; |
| 127 | +import { NETWORK_CONFIG } from "@/config"; |
| 128 | + |
| 129 | +async function initializeSession(signer: Signer) { |
| 130 | + const client = new NillionClient({ |
| 131 | + signer, |
| 132 | + nilchain: NETWORK_CONFIG.nilchain, |
| 133 | + nilauth: NETWORK_CONFIG.nilauth, |
| 134 | + nildb: NETWORK_CONFIG.nildb, |
| 135 | + }); |
| 136 | + |
| 137 | + // Initialize session - this authenticates the user |
| 138 | + await client.initialize(); |
| 139 | + |
| 140 | + return client; |
| 141 | +} |
| 142 | +``` |
| 143 | + |
| 144 | +### 4. Register & Subscribe User |
| 145 | + |
| 146 | +Complete the authentication flow by registering the user: |
| 147 | + |
| 148 | +```typescript |
| 149 | +// Register builder profile |
| 150 | +async function registerBuilder(client: NillionClient) { |
| 151 | + await client.registerBuilder({ |
| 152 | + name: "User", |
| 153 | + description: "Authenticated via MetaMask", |
| 154 | + }); |
| 155 | +} |
| 156 | + |
| 157 | +// Subscribe to Nillion services |
| 158 | +async function subscribe(client: NillionClient) { |
| 159 | + await client.subscribe({ |
| 160 | + name: "notes_subscription", |
| 161 | + subscriptionType: "free", |
| 162 | + }); |
| 163 | +} |
| 164 | +``` |
| 165 | + |
| 166 | +### 5. Login Flow (Returning Users) |
| 167 | + |
| 168 | +For returning users with stored sessions: |
| 169 | + |
| 170 | +```typescript |
| 171 | +// src/hooks/useLoginMutation.ts |
| 172 | +async function login(signer: Signer, rootToken: string, nildbTokens: object) { |
| 173 | + const client = new NillionClient({ |
| 174 | + signer, |
| 175 | + nilchain: NETWORK_CONFIG.nilchain, |
| 176 | + nilauth: NETWORK_CONFIG.nilauth, |
| 177 | + nildb: NETWORK_CONFIG.nildb, |
| 178 | + }); |
| 179 | + |
| 180 | + // Restore previous session |
| 181 | + await client.login({ |
| 182 | + rootToken, |
| 183 | + nildbTokens, |
| 184 | + }); |
| 185 | + |
| 186 | + return client; |
| 187 | +} |
| 188 | +``` |
| 189 | + |
| 190 | +### 6. Complete Authentication Flow |
| 191 | + |
| 192 | +Wire everything together: |
| 193 | + |
| 194 | +```typescript |
| 195 | +// Simplified flow |
| 196 | +async function authenticateUser() { |
| 197 | + // 1. Connect MetaMask & get NUC signer |
| 198 | + const { signer, did } = await connectMetaMask(); |
| 199 | + |
| 200 | + // 2. Check if user has existing session |
| 201 | + const hasSession = checkLocalStorage(); |
| 202 | + |
| 203 | + if (hasSession) { |
| 204 | + // Login with existing session |
| 205 | + await login(signer, storedRootToken, storedNildbTokens); |
| 206 | + } else { |
| 207 | + // Initialize new session |
| 208 | + const client = await initializeSession(signer); |
| 209 | + |
| 210 | + // Register and subscribe |
| 211 | + await registerBuilder(client); |
| 212 | + await subscribe(client); |
| 213 | + |
| 214 | + // Store session tokens |
| 215 | + saveToLocalStorage(client.tokens); |
| 216 | + } |
| 217 | + |
| 218 | + // User is now authenticated! |
| 219 | + return { did, authenticated: true }; |
| 220 | +} |
| 221 | +``` |
| 222 | + |
| 223 | +--- |
| 224 | + |
| 225 | + |
| 226 | + |
| 227 | + |
| 228 | +## Key Benefits |
| 229 | + |
| 230 | +✅ **No Password Storage** - Users authenticate with cryptographic signatures |
| 231 | +✅ **Decentralized Identity** - DIDs are portable across applications |
| 232 | +✅ **Enhanced Security** - Leverages battle-tested wallet security |
| 233 | +✅ **Better UX** - One-click authentication for Web3 users |
| 234 | +✅ **Privacy-First** - Built on Nillion's privacy-preserving infrastructure |
| 235 | + |
| 236 | +--- |
| 237 | + |
| 238 | +## Conclusion |
| 239 | + |
| 240 | +This implementation demonstrates how **MetaMask** and **Nillion's NUC SDK** enable passwordless authentication in Web3 applications. By leveraging wallet signatures and decentralized identifiers, you can build secure, user-friendly authentication without the risks of traditional password systems. |
| 241 | + |
| 242 | +The NUC abstraction handles all cryptographic complexity, allowing developers to focus on building great user experiences while maintaining the highest security standards. |
| 243 | + |
| 244 | + |
0 commit comments