or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdconfiguration.mdcore-components.mdindex.mdmodal-controls.mdtheming.mdwallet-connectors.md

authentication.mddocs/

0

# Authentication

1

2

SIWE (Sign-In with Ethereum) authentication integration for secure user authentication in RainbowKit applications.

3

4

## Capabilities

5

6

### RainbowKitAuthenticationProvider

7

8

Provider component that wraps your application to enable authentication functionality.

9

10

```typescript { .api }

11

/**

12

* Authentication provider component for SIWE integration

13

* @param props - Authentication provider configuration

14

* @returns React provider component for authentication

15

*/

16

function RainbowKitAuthenticationProvider<T>(

17

props: RainbowKitAuthenticationProviderProps<T>

18

): JSX.Element;

19

20

interface RainbowKitAuthenticationProviderProps<T> extends AuthenticationConfig<T> {

21

/** Whether authentication is enabled */

22

enabled?: boolean;

23

/** React children to wrap */

24

children: ReactNode;

25

}

26

27

interface AuthenticationConfig<T> {

28

/** Authentication adapter implementation */

29

adapter: AuthenticationAdapter<T>;

30

/** Current authentication status */

31

status: AuthenticationStatus;

32

}

33

34

type AuthenticationStatus = 'loading' | 'unauthenticated' | 'authenticated';

35

```

36

37

**Usage Examples:**

38

39

```typescript

40

import {

41

RainbowKitAuthenticationProvider,

42

createAuthenticationAdapter,

43

AuthenticationStatus,

44

} from '@rainbow-me/rainbowkit';

45

import { useState } from 'react';

46

47

function App() {

48

const [authenticationStatus, setAuthenticationStatus] = useState<AuthenticationStatus>('loading');

49

50

const authenticationAdapter = createAuthenticationAdapter({

51

getNonce: async () => {

52

const response = await fetch('/api/nonce');

53

return await response.text();

54

},

55

56

createMessage: ({ nonce, address, chainId }) => {

57

return `Sign this message to authenticate: ${nonce}`;

58

},

59

60

verify: async ({ message, signature }) => {

61

const response = await fetch('/api/verify', {

62

method: 'POST',

63

headers: { 'Content-Type': 'application/json' },

64

body: JSON.stringify({ message, signature }),

65

});

66

67

const verified = await response.json();

68

setAuthenticationStatus(verified ? 'authenticated' : 'unauthenticated');

69

return verified;

70

},

71

72

signOut: async () => {

73

await fetch('/api/logout', { method: 'POST' });

74

setAuthenticationStatus('unauthenticated');

75

},

76

});

77

78

return (

79

<RainbowKitAuthenticationProvider

80

adapter={authenticationAdapter}

81

status={authenticationStatus}

82

>

83

{/* Your app */}

84

</RainbowKitAuthenticationProvider>

85

);

86

}

87

```

88

89

### createAuthenticationAdapter

90

91

Factory function for creating authentication adapters with SIWE integration.

92

93

```typescript { .api }

94

/**

95

* Creates an authentication adapter for SIWE integration

96

* @param adapter - Authentication adapter implementation

97

* @returns Configured authentication adapter

98

*/

99

function createAuthenticationAdapter<T>(

100

adapter: AuthenticationAdapter<T>

101

): AuthenticationAdapter<T>;

102

103

interface AuthenticationAdapter<T> {

104

/** Fetches a nonce from your authentication server */

105

getNonce: () => Promise<string>;

106

/** Creates a message to be signed by the user */

107

createMessage: (args: CreateMessageArgs) => T;

108

/** Verifies the signed message on your server */

109

verify: (args: VerifyArgs<T>) => Promise<boolean>;

110

/** Signs out the user */

111

signOut: () => Promise<void>;

112

}

113

114

interface CreateMessageArgs {

115

/** Random nonce from server */

116

nonce: string;

117

/** User's wallet address */

118

address: string;

119

/** Current chain ID */

120

chainId: number;

121

}

122

123

interface VerifyArgs<T> {

124

/** The message that was signed */

125

message: T;

126

/** The signature from the user's wallet */

127

signature: string;

128

}

129

```

130

131

**Usage Examples:**

132

133

```typescript

134

import { createAuthenticationAdapter } from '@rainbow-me/rainbowkit';

135

import { SiweMessage } from 'siwe';

136

137

// SIWE (Sign-In with Ethereum) adapter

138

const siweAuthenticationAdapter = createAuthenticationAdapter({

139

getNonce: async () => {

140

const response = await fetch('/api/nonce');

141

return await response.text();

142

},

143

144

createMessage: ({ nonce, address, chainId }) => {

145

return new SiweMessage({

146

domain: window.location.host,

147

address,

148

statement: 'Sign in with Ethereum to the app.',

149

uri: window.location.origin,

150

version: '1',

151

chainId,

152

nonce,

153

});

154

},

155

156

verify: async ({ message, signature }) => {

157

const verifyRes = await fetch('/api/verify', {

158

method: 'POST',

159

headers: {

160

'Content-Type': 'application/json',

161

},

162

body: JSON.stringify({ message, signature }),

163

});

164

165

return Boolean(verifyRes.ok);

166

},

167

168

signOut: async () => {

169

await fetch('/api/logout', { method: 'POST' });

170

},

171

});

172

173

// Simple message adapter

174

const simpleAuthenticationAdapter = createAuthenticationAdapter({

175

getNonce: async () => {

176

return Math.random().toString(36).substring(2);

177

},

178

179

createMessage: ({ nonce, address }) => {

180

return `Please sign this message to authenticate: ${nonce}\nAddress: ${address}`;

181

},

182

183

verify: async ({ message, signature }) => {

184

// Implement your verification logic

185

return true;

186

},

187

188

signOut: async () => {

189

console.log('Signed out');

190

},

191

});

192

```

193

194

### useAddRecentTransaction

195

196

Hook for adding transactions to the recent transactions list (useful for authenticated flows).

197

198

```typescript { .api }

199

/**

200

* Hook for adding transactions to recent transactions list

201

* @returns Function to add transactions

202

*/

203

function useAddRecentTransaction(): (transaction: NewTransaction) => void;

204

205

interface NewTransaction {

206

/** Transaction hash */

207

hash: string;

208

/** Human-readable description */

209

description: string;

210

/** Number of confirmations (optional) */

211

confirmations?: number;

212

}

213

214

interface Transaction extends NewTransaction {

215

/** Transaction status */

216

status: 'pending' | 'confirmed' | 'failed';

217

}

218

```

219

220

**Usage Examples:**

221

222

```typescript

223

import { useAddRecentTransaction } from '@rainbow-me/rainbowkit';

224

import { useContractWrite, useWaitForTransaction } from 'wagmi';

225

226

function TokenTransfer() {

227

const addRecentTransaction = useAddRecentTransaction();

228

229

const { data, write } = useContractWrite({

230

// ... contract configuration

231

onSuccess(data) {

232

addRecentTransaction({

233

hash: data.hash,

234

description: 'Transfer tokens',

235

});

236

},

237

});

238

239

return (

240

<button onClick={() => write?.()}>

241

Transfer Tokens

242

</button>

243

);

244

}

245

```

246

247

## Advanced Authentication Patterns

248

249

### Full SIWE Implementation

250

251

```typescript

252

import {

253

RainbowKitAuthenticationProvider,

254

createAuthenticationAdapter,

255

AuthenticationStatus,

256

} from '@rainbow-me/rainbowkit';

257

import { SiweMessage } from 'siwe';

258

import { useState, useEffect } from 'react';

259

import { useAccount } from 'wagmi';

260

261

function AuthenticationWrapper({ children }) {

262

const [authenticationStatus, setAuthenticationStatus] = useState<AuthenticationStatus>('loading');

263

const { isConnected } = useAccount();

264

265

useEffect(() => {

266

const fetchStatus = async () => {

267

if (!isConnected) {

268

setAuthenticationStatus('unauthenticated');

269

return;

270

}

271

272

try {

273

const response = await fetch('/api/me');

274

setAuthenticationStatus(response.ok ? 'authenticated' : 'unauthenticated');

275

} catch {

276

setAuthenticationStatus('unauthenticated');

277

}

278

};

279

280

fetchStatus();

281

}, [isConnected]);

282

283

const authenticationAdapter = createAuthenticationAdapter({

284

getNonce: async () => {

285

const response = await fetch('/api/nonce');

286

return await response.text();

287

},

288

289

createMessage: ({ nonce, address, chainId }) => {

290

return new SiweMessage({

291

domain: window.location.host,

292

address,

293

statement: 'Sign in with Ethereum to access protected features.',

294

uri: window.location.origin,

295

version: '1',

296

chainId,

297

nonce,

298

expirationTime: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), // 24 hours

299

});

300

},

301

302

verify: async ({ message, signature }) => {

303

const response = await fetch('/api/verify', {

304

method: 'POST',

305

headers: { 'Content-Type': 'application/json' },

306

body: JSON.stringify({

307

message: message.prepareMessage(),

308

signature,

309

}),

310

});

311

312

const success = response.ok;

313

setAuthenticationStatus(success ? 'authenticated' : 'unauthenticated');

314

return success;

315

},

316

317

signOut: async () => {

318

await fetch('/api/logout', { method: 'POST' });

319

setAuthenticationStatus('unauthenticated');

320

},

321

});

322

323

return (

324

<RainbowKitAuthenticationProvider

325

adapter={authenticationAdapter}

326

status={authenticationStatus}

327

>

328

{children}

329

</RainbowKitAuthenticationProvider>

330

);

331

}

332

```

333

334

### Server-Side SIWE Verification

335

336

```typescript

337

// API route: /api/verify

338

import { SiweMessage } from 'siwe';

339

import { NextApiRequest, NextApiResponse } from 'next';

340

341

export default async function handler(

342

req: NextApiRequest,

343

res: NextApiResponse

344

) {

345

const { method } = req;

346

347

switch (method) {

348

case 'POST':

349

try {

350

const { message, signature } = req.body;

351

const siweMessage = new SiweMessage(message);

352

353

const fields = await siweMessage.verify({ signature });

354

355

if (fields.success) {

356

// Store authentication state (session, JWT, etc.)

357

req.session.siwe = fields.data;

358

req.session.save();

359

res.json({ success: true });

360

} else {

361

res.status(422).json({ message: 'Invalid signature.' });

362

}

363

} catch (error) {

364

res.status(400).json({ message: error.message });

365

}

366

break;

367

default:

368

res.setHeader('Allow', ['POST']);

369

res.status(405).end(`Method ${method} Not Allowed`);

370

}

371

}

372

```

373

374

### Conditional Rendering Based on Authentication

375

376

```typescript

377

import { useAccount } from 'wagmi';

378

import { ConnectButton } from '@rainbow-me/rainbowkit';

379

380

function ProtectedContent() {

381

const { isConnected } = useAccount();

382

383

// This would typically use a custom hook to check authentication status

384

// For example: const { isAuthenticated } = useAuthentication();

385

386

if (!isConnected) {

387

return (

388

<div>

389

<h2>Connect Your Wallet</h2>

390

<p>Please connect your wallet to access this content.</p>

391

<ConnectButton />

392

</div>

393

);

394

}

395

396

// In a real app, you'd check authentication status here

397

return (

398

<div>

399

<h2>Protected Content</h2>

400

<p>This content is only visible to authenticated users.</p>

401

{/* Protected content here */}

402

</div>

403

);

404

}

405

```

406

407

### Custom Authentication Status Hook

408

409

```typescript

410

import { useState, useEffect } from 'react';

411

import { useAccount } from 'wagmi';

412

413

export function useAuthenticationStatus() {

414

const [isAuthenticated, setIsAuthenticated] = useState(false);

415

const [isLoading, setIsLoading] = useState(true);

416

const { isConnected, address } = useAccount();

417

418

useEffect(() => {

419

const checkAuthStatus = async () => {

420

if (!isConnected || !address) {

421

setIsAuthenticated(false);

422

setIsLoading(false);

423

return;

424

}

425

426

try {

427

const response = await fetch('/api/me');

428

setIsAuthenticated(response.ok);

429

} catch {

430

setIsAuthenticated(false);

431

} finally {

432

setIsLoading(false);

433

}

434

};

435

436

checkAuthStatus();

437

}, [isConnected, address]);

438

439

const signOut = async () => {

440

try {

441

await fetch('/api/logout', { method: 'POST' });

442

setIsAuthenticated(false);

443

} catch (error) {

444

console.error('Sign out failed:', error);

445

}

446

};

447

448

return {

449

isAuthenticated,

450

isLoading,

451

signOut,

452

};

453

}

454

```