lens-convex-auth 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,6 +7,7 @@ creation, and **Convex** auth, wired to one session.
7
7
  - An account dialog that lists the wallet's Lens accounts, or creates one (username, name, bio, picture)
8
8
  - Works on mobile wallets: challenges are fetched ahead of time, so the tap that signs can open the wallet app
9
9
  - Lens tokens are kept fresh in the background and shared by the Lens SDK, Convex, and your API routes
10
+ - Switching accounts and logging out revoke the Lens session, so tokens copied from the browser stop working
10
11
  - Convex functions know which Lens account is calling (`requireLensAccount(ctx)`)
11
12
 
12
13
  ```
@@ -161,7 +162,8 @@ export function Navbar() {
161
162
  ```
162
163
 
163
164
  Connecting a wallet opens the account dialog, which stays open until the user signs in to an account
164
- (or disconnects). The button then becomes `Logout`.
165
+ (or disconnects). The button then becomes `Logout`. Switching to another account from the dialog
166
+ revokes the previous account's session (see [Security](#security)).
165
167
 
166
168
  In Convex functions:
167
169
 
@@ -228,6 +230,26 @@ export const ensureCurrentUser = mutation({
228
230
  })
229
231
  ```
230
232
 
233
+ ### Delete your app's data when the account changes
234
+
235
+ If your app keeps anything in the browser for the signed-in account (drafts, caches, keys in IndexedDB),
236
+ delete it in `onAccountChange`. It runs when the signed-in account changes, however that happens:
237
+ signing in, switching accounts, logging out, or the session ending (it expired, or another tab logged
238
+ out). It doesn't run when a stored session is restored on load.
239
+
240
+ ```tsx
241
+ <LensAuthProvider
242
+ auth={auth}
243
+ convex={convex}
244
+ onAccountChange={async (previous, next) => {
245
+ if (previous) await deleteLocalData(previous.address)
246
+ }}
247
+ >
248
+ ```
249
+
250
+ It runs after the change, so it can't call Convex as `previous`. To do something as the account before it
251
+ logs out, do it before calling `signOut()`.
252
+
231
253
  ### Protect your own API routes
232
254
 
233
255
  On the client, `auth.fetch` sends the Lens ID token and retries once with a refreshed token on 401:
@@ -310,7 +332,13 @@ Omit the `convex` prop. Everything else works the same.
310
332
  | `usernameNamespace` | global Lens namespace | Namespace (0x…) new accounts get their username in. The account list shows usernames from it first, then the global one, then any other |
311
333
 
312
334
  Returns `auth` with `thirdwebClient`, `chain`, `lensClient`, `tokens`, `getSession()`,
313
- `resumeSession()`, `requestChallenge()`, `authenticate()`, `logout()`, `fetch()` and `fetchConvexToken()`.
335
+ `resumeSession()`, `requestChallenge()`, `authenticate()`, `startSession()`, `logout()`, `fetch()` and
336
+ `fetchConvexToken()`.
337
+
338
+ - `authenticate(challenge, signature)` and `startSession(credentials)` make a session the app's, and revoke
339
+ the session they replace. `startSession` resolves `false` when that revocation failed.
340
+ - `logout()` clears the session from the browser, then revokes it. Resolves `{ revoked }`, false when the
341
+ Lens API couldn't be reached or refused.
314
342
 
315
343
  ### `<LensAuthProvider>` (`lens-convex-auth/react`)
316
344
 
@@ -319,6 +347,7 @@ Returns `auth` with `thirdwebClient`, `chain`, `lensClient`, `tokens`, `getSessi
319
347
  | `auth` | From `createLensAuth` |
320
348
  | `convex` | Your `ConvexReactClient` (optional) |
321
349
  | `onSignIn(account)` | Called after a sign-in from the dialog, once Convex accepts it |
350
+ | `onAccountChange(previous, next)` | Called when the signed-in account changes: sign-in, switch, log out, or the session ending. See the recipe above |
322
351
  | `accountDialog` | Render the account dialog (default `true`) |
323
352
  | `wallets` | Wallets to reconnect after a reload while a Lens session is stored (default: thirdweb's). Pass the same list as `SignInButton`'s `connectButtonProps.wallets` if you customize it |
324
353
 
@@ -326,6 +355,9 @@ Returns `auth` with `thirdwebClient`, `chain`, `lensClient`, `tokens`, `getSessi
326
355
 
327
356
  `{ status, isLoading, isSignedIn, wallet, account, signOut, openAccountDialog, auth }`
328
357
 
358
+ `signOut()` disconnects the wallet and logs out, revoking the session. It resolves `{ revoked }`, false
359
+ when revoking failed (the session is cleared from the browser either way), so you can tell the user.
360
+
329
361
  `status` is `"loading"` (first render and SSR), `"signed-out"`, `"choosing-account"` (wallet connected,
330
362
  no Lens account yet) or `"signed-in"`. `account` is `{ address, username }`.
331
363
 
@@ -363,6 +395,29 @@ Options: `lensAppAddress` (required), `environment`, and for Convex tokens `priv
363
395
 
364
396
  - `npx lens-convex-auth keys [--kid <id>]`: generates a key pair for Convex tokens
365
397
 
398
+ ## Security
399
+
400
+ What the browser holds while someone is signed in, and what ends it:
401
+
402
+ | Stored | Where | What it allows | Ended by |
403
+ | --- | --- | --- | --- |
404
+ | Lens access, ID and refresh tokens | `localStorage` (`lens_access_token`, `lens_id_token`, `lens_refresh_token`) | Acting as the Lens account through the Lens API, and getting Convex tokens from your token route | Switching accounts or logging out: the session is revoked on the Lens API |
405
+ | Selected account | `localStorage` (`lens_account`) | Nothing: its address and username | Logging out |
406
+ | Convex token | Memory | Calling Convex as the account | Expires with the Lens ID token |
407
+ | thirdweb wallet session | thirdweb's own storage | Email, social and passkey wallets: signing as the wallet, which owns its Lens accounts. Extension and mobile wallets keep their keys to themselves | Logging out disconnects the wallet. thirdweb clears its session from the browser; it doesn't revoke it on its servers |
408
+
409
+ Revoking stops the refresh token right away. An access or ID token already issued stays valid until it
410
+ expires, 10 minutes at most, because your server and Convex verify tokens by signature without asking
411
+ Lens. A copied session is cut off within those 10 minutes.
412
+
413
+ `localStorage` is readable by any script running on your pages, so a cross-site scripting bug, or a
414
+ script you load from a compromised source, can copy these tokens. Revocation limits how long a copy
415
+ works; it doesn't prevent the copy. Keep scripts off your pages with a Content Security Policy, and load
416
+ third-party scripts only from sources you trust.
417
+
418
+ If revoking fails (offline, or the Lens API is down), the session is still cleared from the browser,
419
+ `signOut()` resolves `{ revoked: false }`, and the refresh token works until Lens expires it.
420
+
366
421
  ## Troubleshooting
367
422
 
368
423
  **Turbopack fails to resolve `@x402/*` packages.** Recent `@coinbase/cdp-sdk` versions (a
@@ -37,7 +37,10 @@ declare class TokenService {
37
37
  getFreshIdToken(forceRefresh?: boolean): Promise<string | null>;
38
38
  isAuthenticated(): Promise<boolean>;
39
39
  refreshTokens(): Promise<TokenData | null>;
40
+ revokeSession(tokens: LensCredentials): Promise<boolean>;
40
41
  private performRefresh;
42
+ private requestRefresh;
43
+ private graphql;
41
44
  }
42
45
 
43
46
  type LensAuthOptions = {
@@ -100,7 +103,10 @@ declare function createLensAuth(options: LensAuthOptions): {
100
103
  }) => Promise<LensChallenge>;
101
104
  signChallenge: typeof signLensChallenge;
102
105
  authenticate: (challenge: LensChallenge, signature: string) => Promise<SessionClient>;
103
- logout: () => Promise<void>;
106
+ startSession: (credentials: LensCredentials) => Promise<boolean>;
107
+ logout: () => Promise<{
108
+ revoked: boolean;
109
+ }>;
104
110
  fetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
105
111
  fetchConvexToken: (forceRefresh?: boolean) => Promise<string | null>;
106
112
  };
@@ -118,6 +118,11 @@ var REFRESH_MUTATION = `
118
118
  }
119
119
  }
120
120
  `;
121
+ var REVOKE_MUTATION = `
122
+ mutation RevokeAuthentication($request: RevokeAuthenticationRequest!) {
123
+ revokeAuthentication(request: $request)
124
+ }
125
+ `;
121
126
  function notifyTokensChanged() {
122
127
  try {
123
128
  window.dispatchEvent(new Event(TOKENS_CHANGED_EVENT));
@@ -202,6 +207,27 @@ var TokenService = class {
202
207
  });
203
208
  return this.refreshPromise;
204
209
  }
210
+ // Ends a session on the Lens API, so its tokens stop working even if they were copied out of this
211
+ // browser. Takes the session's tokens rather than the stored ones, because by the time a session is
212
+ // revoked the storage already holds the next one (or nothing). Its refresh token stops working right
213
+ // away; an access or ID token already issued stays valid until it expires (10 minutes at most).
214
+ // Resolves false when the API couldn't be reached or refused.
215
+ async revokeSession(tokens) {
216
+ const authenticationId = decodeJwtPayload(tokens.idToken)?.sid;
217
+ if (typeof authenticationId !== "string") return false;
218
+ try {
219
+ let accessToken = tokens.accessToken;
220
+ if ((jwtExpiresAt(accessToken) ?? 0) <= Date.now() + REFRESH_BUFFER_MS) {
221
+ const refreshed = await this.requestRefresh(tokens.refreshToken);
222
+ if (!refreshed) return true;
223
+ accessToken = refreshed.accessToken;
224
+ }
225
+ const data = await this.graphql(REVOKE_MUTATION, { request: { authenticationId } }, accessToken);
226
+ return !data.errors;
227
+ } catch {
228
+ return false;
229
+ }
230
+ }
205
231
  async performRefresh() {
206
232
  const refreshToken = this.getStoredTokens()?.refreshToken;
207
233
  if (!refreshToken) {
@@ -209,27 +235,31 @@ var TokenService = class {
209
235
  return null;
210
236
  }
211
237
  try {
212
- const response = await fetch(this.options.graphqlUrl, {
213
- method: "POST",
214
- headers: { "Content-Type": "application/json" },
215
- body: JSON.stringify({ query: REFRESH_MUTATION, variables: { request: { refreshToken } } })
216
- });
217
- const data = await response.json();
218
- const result = data?.data?.refresh;
219
- if (data.errors || !result || result.reason) throw new Error(result?.reason ?? "Refresh failed");
220
- const tokens = {
221
- accessToken: result.accessToken,
222
- idToken: result.idToken,
223
- refreshToken: result.refreshToken,
224
- expiresAt: Date.now() + LENS_TOKEN_LIFETIME_MS
225
- };
238
+ const refreshed = await this.requestRefresh(refreshToken);
239
+ if (this.getStoredTokens()?.refreshToken !== refreshToken) return this.getStoredTokens();
240
+ if (!refreshed) throw new Error("Refresh refused");
241
+ const tokens = { ...refreshed, expiresAt: Date.now() + LENS_TOKEN_LIFETIME_MS };
226
242
  this.storeTokens(tokens);
227
243
  return tokens;
228
244
  } catch {
229
- this.clearTokens();
245
+ if (this.getStoredTokens()?.refreshToken === refreshToken) this.clearTokens();
230
246
  return null;
231
247
  }
232
248
  }
249
+ // New tokens for a session, from its refresh token. Doesn't store them. Null when the API refuses (the
250
+ // session has ended); throws when it can't be reached.
251
+ async requestRefresh(refreshToken) {
252
+ const data = await this.graphql(REFRESH_MUTATION, { request: { refreshToken } });
253
+ const result = data?.data?.refresh;
254
+ if (data?.errors || !result?.accessToken || !result.idToken || !result.refreshToken) return null;
255
+ return { accessToken: result.accessToken, idToken: result.idToken, refreshToken: result.refreshToken };
256
+ }
257
+ async graphql(query, variables, accessToken) {
258
+ const headers = { "Content-Type": "application/json" };
259
+ if (accessToken) headers.Authorization = `Bearer ${accessToken}`;
260
+ const response = await fetch(this.options.graphqlUrl, { method: "POST", headers, body: JSON.stringify({ query, variables }) });
261
+ return response.json();
262
+ }
233
263
  };
234
264
 
235
265
  // src/core/wallet.ts
@@ -319,6 +349,7 @@ function createLensAuth(options) {
319
349
  environment: lensEnvironment,
320
350
  storage: typeof window !== "undefined" ? new LensTokenStorage(tokens, `${storagePrefix}custom_`) : void 0
321
351
  });
352
+ const sessionIdOf = (idToken) => decodeJwtPayload(idToken)?.sid;
322
353
  function getSession() {
323
354
  const idToken = tokens.getStoredTokens()?.idToken;
324
355
  return idToken ? lensSessionFromIdToken(idToken) : null;
@@ -339,15 +370,30 @@ function createLensAuth(options) {
339
370
  return challenge.value;
340
371
  }
341
372
  async function authenticate(challenge, signature) {
373
+ const previous = tokens.getStoredTokens();
342
374
  const authenticated = await lensClient.authenticate({ id: challenge.id, signature: signatureFrom(signature) });
343
375
  if (authenticated.isErr()) throw authenticated.error;
344
- tokens.storeCredentials(sessionCredentials(authenticated.value));
376
+ const credentials = sessionCredentials(authenticated.value);
377
+ tokens.storeCredentials(credentials);
378
+ await revokeReplacedSession(previous, credentials);
345
379
  return authenticated.value;
346
380
  }
381
+ async function startSession(credentials) {
382
+ const previous = tokens.getStoredTokens();
383
+ tokens.storeCredentials(credentials);
384
+ return revokeReplacedSession(previous, credentials);
385
+ }
386
+ async function revokeReplacedSession(previous, next) {
387
+ if (!previous || sessionIdOf(previous.idToken) === sessionIdOf(next.idToken)) return true;
388
+ const revoked = await tokens.revokeSession(previous);
389
+ if (!revoked) console.warn("lens-convex-auth: couldn't revoke the previous Lens session");
390
+ return revoked;
391
+ }
347
392
  async function logout() {
393
+ const previous = tokens.getStoredTokens();
348
394
  tokens.clearTokens();
349
- const session = lensClient.currentSession;
350
- if (session && session.isSessionClient()) await session.logout();
395
+ lensClient.currentSession = lensClient;
396
+ return { revoked: previous ? await tokens.revokeSession(previous) : true };
351
397
  }
352
398
  async function authFetch(input, init = {}) {
353
399
  const send = async (forceRefresh) => {
@@ -387,6 +433,7 @@ function createLensAuth(options) {
387
433
  requestChallenge,
388
434
  signChallenge: signLensChallenge,
389
435
  authenticate,
436
+ startSession,
390
437
  logout,
391
438
  fetch: authFetch,
392
439
  fetchConvexToken
@@ -517,7 +564,7 @@ async function fetchAccountUsername(auth, accountAddress) {
517
564
  async function signInToCreatedAccount(auth, session, accountAddress) {
518
565
  const switched = await session.switchAccount({ account: evmAddress2(accountAddress) });
519
566
  if (switched.isErr()) throw switched.error;
520
- auth.tokens.storeCredentials(sessionCredentials(switched.value));
567
+ await auth.startSession(sessionCredentials(switched.value));
521
568
  const resumed = await auth.lensClient.resumeSession();
522
569
  if (resumed.isErr()) throw resumed.error;
523
570
  }
@@ -550,4 +597,4 @@ export {
550
597
  fetchAccountUsername,
551
598
  signInToCreatedAccount
552
599
  };
553
- //# sourceMappingURL=chunk-PJC2S3IU.js.map
600
+ //# sourceMappingURL=chunk-OLO3H3YN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/constants.ts","../src/core/jwt.ts","../src/core/storage.ts","../src/core/tokens.ts","../src/core/wallet.ts","../src/core/auth.ts","../src/core/onboarding.ts"],"sourcesContent":["// Dependency-free values shared by every entry point, including the server and Convex ones.\n\nexport type LensEnvironmentName = \"mainnet\" | \"testnet\"\n\nconst LENS_API_ORIGINS: Record<LensEnvironmentName, string> = {\n mainnet: \"https://api.lens.xyz\",\n testnet: \"https://api.testnet.lens.xyz\",\n}\n\n// Lens signs its tokens as the API origin, and publishes its keys under it.\nexport function lensApiOrigin(environment: LensEnvironmentName = \"mainnet\"): string {\n return LENS_API_ORIGINS[environment]\n}\n\nexport const LENS_CHAIN_IDS: Record<LensEnvironmentName, number> = {\n mainnet: 232,\n testnet: 37111,\n}\n\n// Lens access and ID tokens are valid for 10 minutes.\nexport const LENS_TOKEN_LIFETIME_MS = 10 * 60 * 1000\n\nexport const ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/\n\n// The token the server issues for Convex (server/, convex/, cli/). Both sides must agree on these.\nexport const DEFAULT_CONVEX_TOKEN_ISSUER = \"https://lens-convex-auth.local\"\nexport const DEFAULT_CONVEX_TOKEN_AUDIENCE = \"convex\"\nexport const DEFAULT_CONVEX_TOKEN_KEY_ID = \"convex-auth-1\"\nexport const DEFAULT_CONVEX_TOKEN_ENDPOINT = \"/api/convex-token\"\n","// Claims of a JWT, without verifying it (the server verifies). Null if it isn't a JWT.\nexport function decodeJwtPayload(token: string): Record<string, unknown> | null {\n try {\n const payload = token.split(\".\")[1]\n if (!payload) return null\n const base64 = payload.replace(/-/g, \"+\").replace(/_/g, \"/\")\n const binary = atob(base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), \"=\"))\n const json = new TextDecoder().decode(Uint8Array.from(binary, (c) => c.charCodeAt(0)))\n const claims = JSON.parse(json)\n return claims && typeof claims === \"object\" ? claims : null\n } catch {\n return null\n }\n}\n\n// When a JWT expires (ms since epoch), from its `exp` claim\nexport function jwtExpiresAt(token: string): number | null {\n const exp = Number(decodeJwtPayload(token)?.exp)\n return Number.isFinite(exp) && exp > 0 ? exp * 1000 : null\n}\n\n// The Lens account a Lens ID token acts for (`act.sub`), lowercased, and the session it belongs to.\n// Null for tokens without an account (onboarding users) or that aren't JWTs.\nexport function lensSessionFromIdToken(idToken: string): { account: string; sessionId: string } | null {\n const claims = decodeJwtPayload(idToken)\n const act = claims?.act as { sub?: unknown } | null | undefined\n if (typeof act?.sub !== \"string\") return null\n return { account: act.sub.toLowerCase(), sessionId: String(claims?.sid ?? \"\") }\n}\n","import type { IStorageProvider } from \"@lens-protocol/client\"\nimport type { TokenService } from \"./tokens\"\n\n// Storage for the Lens SDK that keeps its credentials in the TokenService, so the SDK and the app\n// share one copy of the session. Everything else the SDK stores goes to localStorage under a prefix.\nexport class LensTokenStorage implements IStorageProvider {\n constructor(private tokens: TokenService, private prefix: string) {}\n\n private isCredentials(key: string) {\n return key.includes(\"credentials\") || key.includes(\"session\")\n }\n\n // The SDK only accepts a stored credentials item whose metadata is exactly\n // { version, createdAt, updatedAt }, so keep the metadata it wrote and hand it back unchanged.\n private metadataKey(key: string) {\n return `${this.prefix}${key}:metadata`\n }\n\n private credentialsMetadata(key: string) {\n const stored = localStorage.getItem(this.metadataKey(key))\n if (stored) return JSON.parse(stored)\n // Tokens stored before the metadata was kept: 3 is the SDK's current credentials schema version.\n const now = Date.now()\n return { version: 3, createdAt: now, updatedAt: now }\n }\n\n getItem(key: string): string | null {\n if (typeof window === \"undefined\") return null\n try {\n if (!this.isCredentials(key)) return localStorage.getItem(this.prefix + key)\n const tokens = this.tokens.getStoredTokens()\n if (!tokens) return null\n return JSON.stringify({\n data: { accessToken: tokens.accessToken, idToken: tokens.idToken, refreshToken: tokens.refreshToken },\n metadata: this.credentialsMetadata(key),\n })\n } catch {\n return null\n }\n }\n\n setItem(key: string, value: string): void {\n if (typeof window === \"undefined\") return\n try {\n if (!this.isCredentials(key)) return localStorage.setItem(this.prefix + key, value)\n const parsed = JSON.parse(value)\n const data = typeof parsed.data === \"string\" ? JSON.parse(parsed.data) : parsed.data\n if (data?.accessToken && data?.idToken && data?.refreshToken) {\n this.tokens.storeCredentials(data)\n localStorage.setItem(this.metadataKey(key), JSON.stringify(parsed.metadata))\n }\n } catch {}\n }\n\n removeItem(key: string): void {\n if (typeof window === \"undefined\") return\n try {\n if (!this.isCredentials(key)) return localStorage.removeItem(this.prefix + key)\n this.tokens.clearTokens()\n localStorage.removeItem(this.metadataKey(key))\n } catch {}\n }\n}\n","import { LENS_TOKEN_LIFETIME_MS } from \"./constants\"\nimport { decodeJwtPayload, jwtExpiresAt } from \"./jwt\"\n\nexport interface TokenData {\n accessToken: string\n idToken: string\n refreshToken: string\n expiresAt: number // when the access token expires (ms since epoch)\n}\n\nexport type LensCredentials = Pick<TokenData, \"accessToken\" | \"idToken\" | \"refreshToken\">\n\n// Dispatched on window whenever tokens are stored or cleared in this tab\nexport const TOKENS_CHANGED_EVENT = \"lens-convex-auth:tokens-changed\"\n\n// Refresh a minute before expiry for smoother UX\nconst REFRESH_BUFFER_MS = 60 * 1000\n\nconst REFRESH_MUTATION = `\n mutation Refresh($request: RefreshRequest!) {\n refresh(request: $request) {\n ... on AuthenticationTokens {\n accessToken\n idToken\n refreshToken\n }\n ... on ForbiddenError {\n reason\n }\n }\n }\n`\n\nconst REVOKE_MUTATION = `\n mutation RevokeAuthentication($request: RevokeAuthenticationRequest!) {\n revokeAuthentication(request: $request)\n }\n`\n\nfunction notifyTokensChanged() {\n try {\n window.dispatchEvent(new Event(TOKENS_CHANGED_EVENT))\n } catch {}\n}\n\n// Keeps the Lens session's tokens in localStorage, the single place every part of the app (the Lens\n// SDK, Convex, API calls) reads them from, and refreshes them.\nexport class TokenService {\n readonly storageKeys: { accessToken: string; idToken: string; refreshToken: string; expiresAt: string }\n private refreshPromise: Promise<TokenData | null> | null = null\n\n constructor(private options: { storagePrefix: string; graphqlUrl: string }) {\n const p = options.storagePrefix\n this.storageKeys = {\n accessToken: `${p}access_token`,\n idToken: `${p}id_token`,\n refreshToken: `${p}refresh_token`,\n expiresAt: `${p}token_expires_at`,\n }\n }\n\n storeTokens(tokens: TokenData): void {\n if (typeof window === \"undefined\") return\n try {\n localStorage.setItem(this.storageKeys.accessToken, tokens.accessToken)\n localStorage.setItem(this.storageKeys.idToken, tokens.idToken)\n localStorage.setItem(this.storageKeys.refreshToken, tokens.refreshToken)\n localStorage.setItem(this.storageKeys.expiresAt, tokens.expiresAt.toString())\n } catch {}\n notifyTokensChanged()\n }\n\n storeCredentials(credentials: LensCredentials): void {\n this.storeTokens({ ...credentials, expiresAt: Date.now() + LENS_TOKEN_LIFETIME_MS })\n }\n\n getStoredTokens(): TokenData | null {\n if (typeof window === \"undefined\") return null\n try {\n const accessToken = localStorage.getItem(this.storageKeys.accessToken)\n const idToken = localStorage.getItem(this.storageKeys.idToken)\n const refreshToken = localStorage.getItem(this.storageKeys.refreshToken)\n const storedExpiresAt = localStorage.getItem(this.storageKeys.expiresAt)\n if (!accessToken || !idToken || !refreshToken) return null\n // The access token's own expiry. The stored value is only an estimate (store time + 10 min),\n // which marks tokens read back from an old session as fresh long after they expired.\n const expiresAt = jwtExpiresAt(accessToken) ?? (storedExpiresAt ? parseInt(storedExpiresAt, 10) : 0)\n return { accessToken, idToken, refreshToken, expiresAt }\n } catch {\n return null\n }\n }\n\n clearTokens(): void {\n if (typeof window === \"undefined\") return\n try {\n for (const key of Object.values(this.storageKeys)) localStorage.removeItem(key)\n } catch {}\n notifyTokensChanged()\n }\n\n // Current access token, refreshed first if it's about to expire\n async getValidAccessToken(): Promise<string | null> {\n const tokens = this.getStoredTokens()\n if (!tokens) return null\n if (tokens.expiresAt > Date.now() + REFRESH_BUFFER_MS) return tokens.accessToken\n return (await this.refreshTokens())?.accessToken ?? null\n }\n\n // ID token for authenticating to your backend (Convex, API routes), refreshed when it's about to\n // expire (by the token's own `exp` claim). `forceRefresh` is for when the backend rejected it.\n async getFreshIdToken(forceRefresh = false): Promise<string | null> {\n const tokens = this.getStoredTokens()\n if (!tokens) return null\n const expiresAt = jwtExpiresAt(tokens.idToken) ?? 0\n const fresh = expiresAt > Date.now() + REFRESH_BUFFER_MS\n // A forced refresh right after another tab or the background refresher got new tokens reuses them\n const justIssued = expiresAt > Date.now() + 2 * REFRESH_BUFFER_MS\n if (fresh && (!forceRefresh || justIssued)) return tokens.idToken\n return (await this.refreshTokens())?.idToken ?? null\n }\n\n async isAuthenticated(): Promise<boolean> {\n return !!(await this.getValidAccessToken())\n }\n\n // Concurrent calls share one request. Clears the tokens when the session can't be refreshed.\n refreshTokens(): Promise<TokenData | null> {\n this.refreshPromise ??= this.performRefresh().finally(() => {\n this.refreshPromise = null\n })\n return this.refreshPromise\n }\n\n // Ends a session on the Lens API, so its tokens stop working even if they were copied out of this\n // browser. Takes the session's tokens rather than the stored ones, because by the time a session is\n // revoked the storage already holds the next one (or nothing). Its refresh token stops working right\n // away; an access or ID token already issued stays valid until it expires (10 minutes at most).\n // Resolves false when the API couldn't be reached or refused.\n async revokeSession(tokens: LensCredentials): Promise<boolean> {\n const authenticationId = decodeJwtPayload(tokens.idToken)?.sid\n if (typeof authenticationId !== \"string\") return false\n try {\n // Revoking needs a valid access token for the session itself\n let accessToken = tokens.accessToken\n if ((jwtExpiresAt(accessToken) ?? 0) <= Date.now() + REFRESH_BUFFER_MS) {\n const refreshed = await this.requestRefresh(tokens.refreshToken)\n // A session that can't be refreshed has already ended (expired, or revoked elsewhere)\n if (!refreshed) return true\n accessToken = refreshed.accessToken\n }\n const data = await this.graphql(REVOKE_MUTATION, { request: { authenticationId } }, accessToken)\n return !data.errors\n } catch {\n return false\n }\n }\n\n private async performRefresh(): Promise<TokenData | null> {\n const refreshToken = this.getStoredTokens()?.refreshToken\n if (!refreshToken) {\n this.clearTokens()\n return null\n }\n try {\n // Resuming the Lens session only reads these same tokens back, so ask the API for new ones.\n // (Each Lens SDK session keeps its own copy of the tokens and refreshes that copy itself.)\n const refreshed = await this.requestRefresh(refreshToken)\n // Signed out, or signed in again, while the request was in flight: those tokens are for a session\n // that's no longer this browser's\n if (this.getStoredTokens()?.refreshToken !== refreshToken) return this.getStoredTokens()\n if (!refreshed) throw new Error(\"Refresh refused\")\n const tokens: TokenData = { ...refreshed, expiresAt: Date.now() + LENS_TOKEN_LIFETIME_MS }\n this.storeTokens(tokens)\n return tokens\n } catch {\n if (this.getStoredTokens()?.refreshToken === refreshToken) this.clearTokens()\n return null\n }\n }\n\n // New tokens for a session, from its refresh token. Doesn't store them. Null when the API refuses (the\n // session has ended); throws when it can't be reached.\n private async requestRefresh(refreshToken: string): Promise<LensCredentials | null> {\n const data = await this.graphql(REFRESH_MUTATION, { request: { refreshToken } })\n const result = data?.data?.refresh\n if (data?.errors || !result?.accessToken || !result.idToken || !result.refreshToken) return null\n return { accessToken: result.accessToken, idToken: result.idToken, refreshToken: result.refreshToken }\n }\n\n private async graphql(query: string, variables: Record<string, unknown>, accessToken?: string) {\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" }\n if (accessToken) headers.Authorization = `Bearer ${accessToken}`\n const response = await fetch(this.options.graphqlUrl, { method: \"POST\", headers, body: JSON.stringify({ query, variables }) })\n return response.json()\n }\n}\n","import { createWalletClient, custom, defineChain as defineViemChain, getAddress, hexToBigInt, hexToNumber, type Account as ViemAccount, type Chain as ViemChain, type Hex, type Transport, type WalletClient } from \"viem\"\nimport { lens as viemLens, lensTestnet as viemLensTestnet } from \"viem/chains\"\nimport { chainConfig } from \"viem/zksync\"\nimport { defineChain, type Chain } from \"thirdweb/chains\"\nimport { EIP1193, type Wallet } from \"thirdweb/wallets\"\nimport type { ThirdwebClient } from \"thirdweb\"\nimport { LENS_CHAIN_IDS, type LensEnvironmentName } from \"./constants\"\n\n// Lens Chain for thirdweb. Without an RPC URL thirdweb uses its own RPC for the chain.\nexport function lensThirdwebChain(environment: LensEnvironmentName, rpcUrl?: string): Chain {\n const testnet = environment === \"testnet\"\n return defineChain({\n id: LENS_CHAIN_IDS[environment],\n name: testnet ? \"Lens Chain Testnet\" : \"Lens Chain\",\n nativeCurrency: testnet\n ? { name: \"GRASS\", symbol: \"GRASS\", decimals: 18 }\n : { name: \"GHO\", symbol: \"GHO\", decimals: 18 },\n ...(rpcUrl ? { rpc: rpcUrl } : {}),\n ...(testnet ? { testnet: true as const } : {}),\n })\n}\n\n// Lens Chain with the zkSync config (same as `chains.mainnet` from @lens-chain/sdk/viem), so viem can\n// sign the sponsored EIP-712 transactions the Lens API returns. Typed as a plain `Chain` because\n// `handleOperationWith` takes a plain viem `WalletClient`.\nexport function lensViemChain(environment: LensEnvironmentName): ViemChain {\n return defineViemChain({ ...(environment === \"testnet\" ? viemLensTestnet : viemLens), ...chainConfig })\n}\n\n// A viem wallet client for the connected thirdweb wallet, as `handleOperationWith` expects:\n// hoisted account and a Lens chain. Works for every thirdweb wallet, not only injected ones.\nexport function createLensWalletClient(opts: {\n wallet: Wallet\n client: ThirdwebClient\n chain: Chain\n viemChain: ViemChain\n}): WalletClient<Transport, ViemChain, ViemAccount> {\n const account = opts.wallet.getAccount()\n if (!account) throw new Error(\"Connect your wallet\")\n const provider = EIP1193.toProvider({ wallet: opts.wallet, chain: opts.chain, client: opts.client })\n return createWalletClient({\n account: getAddress(account.address),\n chain: opts.viemChain,\n transport: custom({\n request: ({ method, params }) => {\n if (method === \"eth_sendTransaction\") {\n // viem sends hex-encoded fields (e.g. `type: \"0x2\"`) that thirdweb's local signers reject.\n // thirdweb re-estimates gas and fees for Lens Chain anyway, so pass only what it needs.\n const [tx] = params as [{ to: Hex; data?: Hex; value?: Hex; nonce?: Hex }]\n return provider.request({\n method,\n params: [{\n to: tx.to,\n data: tx.data,\n value: tx.value ? hexToBigInt(tx.value) : undefined,\n nonce: tx.nonce ? hexToNumber(tx.nonce) : undefined,\n }],\n })\n }\n return provider.request({ method, params })\n },\n }),\n })\n}\n","import { PublicClient, evmAddress, mainnet, signatureFrom, testnet, type SessionClient, type UUID } from \"@lens-protocol/client\"\nimport { createThirdwebClient, type ThirdwebClient } from \"thirdweb\"\nimport type { Chain } from \"thirdweb/chains\"\nimport type { Chain as ViemChain } from \"viem\"\nimport { ADDRESS_RE, DEFAULT_CONVEX_TOKEN_ENDPOINT, lensApiOrigin, type LensEnvironmentName } from \"./constants\"\nimport { decodeJwtPayload, lensSessionFromIdToken } from \"./jwt\"\nimport { LensTokenStorage } from \"./storage\"\nimport { TokenService, type LensCredentials } from \"./tokens\"\nimport { lensThirdwebChain, lensViemChain } from \"./wallet\"\n\nexport type LensAuthOptions = {\n /** thirdweb client ID (https://thirdweb.com/dashboard) */\n thirdwebClientId: string\n /** Your Lens app address (https://developer.lens.xyz/apps). Tokens are issued for this app. */\n lensAppAddress: string\n /** Defaults to \"mainnet\" */\n environment?: LensEnvironmentName\n /** Lens Chain RPC for thirdweb. Defaults to thirdweb's RPC. */\n lensChainRpcUrl?: string\n /** Prefix of the localStorage keys the session is stored under. Defaults to \"lens_\". */\n storagePrefix?: string\n /** Route that exchanges a Lens ID token for a Convex token. Defaults to \"/api/convex-token\". */\n convexTokenEndpoint?: string\n /**\n * Username namespace (0x…) new accounts get their username in, and whose usernames are shown first.\n * Defaults to the global Lens namespace.\n */\n usernameNamespace?: string\n}\n\nexport type LensLoginRole = \"ACCOUNT_OWNER\" | \"ACCOUNT_MANAGER\"\n\nexport type LensChallenge = { id: UUID; text: string }\n\nexport type MessageSigner = {\n signMessage: (args: { message: string }) => Promise<string>\n}\n\n// The signed-in Lens account (lowercased address) and its session, read from the stored ID token\nexport type LensSessionInfo = { account: string; sessionId: string }\n\nexport type LensAuth = ReturnType<typeof createLensAuth>\n\n// Challenges are fetched ahead of time so a tap can sign right away: how often they're replaced, and how\n// old one can be to still be used.\nexport const CHALLENGE_REFRESH_MS = 2 * 60 * 1000\nexport const CHALLENGE_MAX_AGE_MS = 4 * 60 * 1000\n\n// On phones and tablets thirdweb opens the wallet app with a deep link to sign (same check as its\n// internal isMobile), and that deep link only works right after a tap.\nexport function opensWalletWithDeepLink() {\n if (typeof navigator === \"undefined\") return false\n const ua = navigator.userAgent\n return /iPhone|iPad|iPod|Android/i.test(ua) || (/Macintosh/.test(ua) && navigator.maxTouchPoints > 1)\n}\n\n// Asks the wallet to sign synchronously (no awaits before it), so call it directly from a click handler.\nexport function signLensChallenge(challenge: LensChallenge, signer: MessageSigner): Promise<string> {\n return signer.signMessage({ message: challenge.text })\n}\n\n// A Lens SDK session's tokens\nexport function sessionCredentials(session: SessionClient): LensCredentials {\n const credentials = session.getCredentials()\n if (credentials.isErr()) throw credentials.error\n if (!credentials.value) throw new Error(\"The Lens session has no credentials\")\n return credentials.value\n}\n\n// Create once per app (at module level) and pass to <LensAuthProvider>.\nexport function createLensAuth(options: LensAuthOptions) {\n if (!options.thirdwebClientId) throw new Error(\"lens-convex-auth: thirdwebClientId is required\")\n if (!ADDRESS_RE.test(options.lensAppAddress ?? \"\")) {\n throw new Error(\"lens-convex-auth: lensAppAddress must be your Lens app address (0x…)\")\n }\n if (options.usernameNamespace !== undefined && !ADDRESS_RE.test(options.usernameNamespace)) {\n throw new Error(\"lens-convex-auth: usernameNamespace must be a Lens namespace address (0x…)\")\n }\n\n const environment: LensEnvironmentName = options.environment ?? \"mainnet\"\n const storagePrefix = options.storagePrefix ?? \"lens_\"\n const appAddress = options.lensAppAddress\n const convexTokenEndpoint = options.convexTokenEndpoint ?? DEFAULT_CONVEX_TOKEN_ENDPOINT\n const usernameNamespace = options.usernameNamespace\n\n const tokens = new TokenService({ storagePrefix, graphqlUrl: `${lensApiOrigin(environment)}/graphql` })\n const thirdwebClient: ThirdwebClient = createThirdwebClient({ clientId: options.thirdwebClientId })\n const chain: Chain = lensThirdwebChain(environment, options.lensChainRpcUrl)\n const viemChain: ViemChain = lensViemChain(environment)\n const lensEnvironment = environment === \"testnet\" ? testnet : mainnet\n const lensClient = PublicClient.create({\n environment: lensEnvironment,\n storage: typeof window !== \"undefined\" ? new LensTokenStorage(tokens, `${storagePrefix}custom_`) : undefined,\n })\n\n const sessionIdOf = (idToken: string) => decodeJwtPayload(idToken)?.sid\n\n // The signed-in account, or null when signed out\n function getSession(): LensSessionInfo | null {\n const idToken = tokens.getStoredTokens()?.idToken\n return idToken ? lensSessionFromIdToken(idToken) : null\n }\n\n // True when there's a valid session, refreshing its tokens first if needed (a session that can't be\n // refreshed is cleared). Also resumes it in the Lens client, so Lens SDK calls and hooks act as the\n // signed-in account.\n async function resumeSession(): Promise<boolean> {\n if (!(await tokens.isAuthenticated())) return false\n if (!lensClient.currentSession?.isSessionClient()) await lensClient.resumeSession()\n return true\n }\n\n // Logging in is split in two (challenge, then sign + authenticate) instead of using `lensClient.login`,\n // so the signature can be requested straight from a tap. On mobile, WalletConnect opens the wallet app\n // with a deep link when asked to sign, and iOS only allows that right after a user gesture: after the\n // challenge round trip the link is blocked and the wallet never shows the request.\n async function requestChallenge(opts: { role: LensLoginRole; account: string; signer: string }): Promise<LensChallenge> {\n const app = evmAddress(appAddress)\n const account = evmAddress(opts.account)\n const signer = evmAddress(opts.signer)\n const challenge = await lensClient.challenge(\n opts.role === \"ACCOUNT_MANAGER\"\n ? { accountManager: { account, app, manager: signer } }\n : { accountOwner: { account, app, owner: signer } },\n )\n if (challenge.isErr()) throw challenge.error\n return challenge.value\n }\n\n async function authenticate(challenge: LensChallenge, signature: string): Promise<SessionClient> {\n // Read first: the Lens client stores the new session's tokens itself, through LensTokenStorage\n const previous = tokens.getStoredTokens()\n const authenticated = await lensClient.authenticate({ id: challenge.id, signature: signatureFrom(signature) })\n if (authenticated.isErr()) throw authenticated.error\n const credentials = sessionCredentials(authenticated.value)\n tokens.storeCredentials(credentials)\n await revokeReplacedSession(previous, credentials)\n return authenticated.value\n }\n\n // Makes these the app's session, like a sign-in (used for accounts created on another Lens client).\n async function startSession(credentials: LensCredentials): Promise<boolean> {\n const previous = tokens.getStoredTokens()\n tokens.storeCredentials(credentials)\n return revokeReplacedSession(previous, credentials)\n }\n\n // Switching accounts revokes the session switched away from, so its tokens stop working even if they\n // were copied out of this browser. The new session is stored first: it doesn't wait on the revocation,\n // and a failed one doesn't undo it. False when the previous session couldn't be revoked.\n async function revokeReplacedSession(previous: LensCredentials | null, next: LensCredentials): Promise<boolean> {\n if (!previous || sessionIdOf(previous.idToken) === sessionIdOf(next.idToken)) return true\n const revoked = await tokens.revokeSession(previous)\n if (!revoked) console.warn(\"lens-convex-auth: couldn't revoke the previous Lens session\")\n return revoked\n }\n\n // Signs out: clears the session from this browser, then revokes it on the Lens API, so its tokens\n // stop working even if they were copied. Resolves `revoked: false` when there was a session and the\n // API couldn't be reached or refused; it's cleared from this browser either way.\n async function logout(): Promise<{ revoked: boolean }> {\n const previous = tokens.getStoredTokens()\n tokens.clearTokens()\n // Lens SDK calls and hooks act as the public client again. (The SDK's own logout() would revoke with\n // the session's in-memory copy of the tokens, which the background refresh may have replaced.)\n lensClient.currentSession = lensClient\n return { revoked: previous ? await tokens.revokeSession(previous) : true }\n }\n\n // fetch() with the Lens ID token as `Authorization: Bearer`, retried once with a refreshed token on 401.\n // Verify it on the server with `getLensSession` from \"lens-convex-auth/server\".\n async function authFetch(input: RequestInfo | URL, init: RequestInit = {}): Promise<Response> {\n const send = async (forceRefresh: boolean) => {\n const idToken = await tokens.getFreshIdToken(forceRefresh)\n const headers = new Headers(init.headers)\n if (idToken) headers.set(\"Authorization\", `Bearer ${idToken}`)\n return fetch(input, { ...init, headers })\n }\n const response = await send(false)\n return response.status === 401 && getSession() ? send(true) : response\n }\n\n // Convex can't verify Lens ID tokens itself (they have no `kid` header), so the server verifies the\n // Lens ID token and issues a Convex token for the same account (see \"lens-convex-auth/server\").\n async function fetchConvexToken(forceRefresh = false): Promise<string | null> {\n const idToken = await tokens.getFreshIdToken(forceRefresh)\n if (!idToken) return null\n try {\n const res = await fetch(convexTokenEndpoint, { method: \"POST\", headers: { Authorization: `Bearer ${idToken}` } })\n if (!res.ok) return null\n const { token } = await res.json()\n return typeof token === \"string\" ? token : null\n } catch {\n return null\n }\n }\n\n return {\n environment,\n appAddress,\n usernameNamespace,\n storagePrefix,\n thirdwebClient,\n chain,\n viemChain,\n lensClient,\n lensEnvironment,\n tokens,\n getSession,\n resumeSession,\n requestChallenge,\n signChallenge: signLensChallenge,\n authenticate,\n startSession,\n logout,\n fetch: authFetch,\n fetchConvexToken,\n }\n}\n","import { PageSize, PublicClient, evmAddress, signatureFrom, uri, type Account, type Cursor, type SessionClient } from \"@lens-protocol/client\"\nimport { canCreateUsername, createAccountWithUsername, fetchAccount, fetchUsername, fetchUsernames } from \"@lens-protocol/client/actions\"\nimport { handleOperationWith } from \"@lens-protocol/client/viem\"\nimport { account as accountMetadata } from \"@lens-protocol/metadata\"\nimport { StorageClient, immutable } from \"@lens-chain/storage-client\"\nimport { getTransactionReceipt } from \"viem/actions\"\nimport type { Wallet } from \"thirdweb/wallets\"\nimport type { LensAuth, LensChallenge } from \"./auth\"\nimport { sessionCredentials } from \"./auth\"\nimport { LENS_CHAIN_IDS } from \"./constants\"\nimport { createLensWalletClient } from \"./wallet\"\n\n// An error whose message can be shown to the user as is.\nexport class OnboardingError extends Error {\n name = \"OnboardingError\"\n}\n\n// Creating an account takes a session as an onboarding user (a wallet signed in without a Lens Account).\n// It runs on its own client, whose storage is in memory, so that session never reaches the app's token\n// storage: until the account exists and is signed in to, the app must not treat the wallet as signed in.\nexport function createOnboardingClient(auth: LensAuth) {\n return PublicClient.create({ environment: auth.lensEnvironment })\n}\n\nexport async function requestOnboardingChallenge(auth: LensAuth, client: PublicClient, walletAddress: string): Promise<LensChallenge> {\n const challenge = await client.challenge({\n onboardingUser: { app: evmAddress(auth.appAddress), wallet: evmAddress(walletAddress) },\n })\n if (challenge.isErr()) throw challenge.error\n return challenge.value\n}\n\nexport async function authenticateOnboarding(client: PublicClient, challenge: LensChallenge, signature: string): Promise<SessionClient> {\n const authenticated = await client.authenticate({ id: challenge.id, signature: signatureFrom(signature) })\n if (authenticated.isErr()) throw authenticated.error\n return authenticated.value\n}\n\n// The global Lens namespace takes lowercase letters, numbers, \"-\" and \"_\", and can't start with \"-\" or \"_\".\n// Its other rules (length, reserved names, price) are checked by the Lens API in checkUsername.\nconst USERNAME_PATTERN = /^[a-z0-9][a-z0-9_-]*$/\n\nexport function usernameFormatError(localName: string): string | null {\n if (!localName) return \"Choose a username\"\n if (/^[-_]/.test(localName)) return \"Start with a letter or a number\"\n if (!USERNAME_PATTERN.test(localName)) return \"Use only lowercase letters, numbers, - and _\"\n return null\n}\n\n// The username to create, in the app's namespace when it has one (else the global Lens namespace)\nfunction usernameInput(auth: LensAuth, localName: string) {\n return auth.usernameNamespace ? { localName, namespace: evmAddress(auth.usernameNamespace) } : { localName }\n}\n\n// Needs no session, so it can run while the user types.\nexport async function isUsernameTaken(auth: LensAuth, localName: string): Promise<boolean> {\n const result = await fetchUsername(auth.lensClient, { username: usernameInput(auth, localName) })\n if (result.isErr()) throw result.error\n return result.value !== null\n}\n\ntype NamespaceValidationFailed = {\n reason: string\n unsatisfiedRules: { required: { message: string }[] } | null\n}\n\nfunction validationFailedMessage(failed: NamespaceValidationFailed) {\n return failed.unsatisfiedRules?.required[0]?.message || failed.reason\n}\n\n// Returns why the username can't be created, or null if it can.\nexport async function checkUsername(auth: LensAuth, session: SessionClient, localName: string): Promise<string | null> {\n const result = await canCreateUsername(session, usernameInput(auth, localName))\n if (result.isErr()) throw result.error\n switch (result.value.__typename) {\n case \"NamespaceOperationValidationPassed\":\n return null\n case \"UsernameTaken\":\n return \"This username is taken\"\n case \"NamespaceOperationValidationFailed\":\n return validationFailedMessage(result.value)\n case \"NamespaceOperationValidationUnknown\":\n return \"This username can’t be created here. Try another one.\"\n }\n}\n\nlet storageClient: StorageClient | null = null\n\nexport type AccountProfile = {\n name?: string\n bio?: string\n picture?: File | null\n}\n\n// Uploads the account metadata (and its picture) to Grove, and returns the metadata URI.\nexport async function uploadAccountMetadata(auth: LensAuth, profile: AccountProfile): Promise<string> {\n storageClient ??= StorageClient.create()\n const acl = immutable(LENS_CHAIN_IDS[auth.environment])\n const picture = profile.picture ? (await storageClient.uploadFile(profile.picture, { acl })).uri : undefined\n const metadata = accountMetadata({\n name: profile.name || undefined,\n bio: profile.bio || undefined,\n picture,\n })\n const { uri: metadataUri } = await storageClient.uploadAsJson(metadata, { acl })\n return metadataUri\n}\n\n// How long to wait for a new account to be listed once its transaction is indexed\nconst ACCOUNT_POLL_ATTEMPTS = 15\nconst ACCOUNT_POLL_INTERVAL_MS = 2000\n\n// Creates an account owned by the onboarding user's wallet. Most apps sponsor it; otherwise the wallet\n// is asked to send the transaction.\nexport async function createLensAccount(\n auth: LensAuth,\n session: SessionClient,\n wallet: Wallet,\n request: { localName: string; metadataUri: string },\n): Promise<Account> {\n const created = await createAccountWithUsername(session, {\n username: usernameInput(auth, request.localName),\n metadataUri: uri(request.metadataUri),\n })\n if (created.isErr()) throw created.error\n const result = created.value\n switch (result.__typename) {\n case \"UsernameTaken\":\n throw new OnboardingError(\"This username is taken\")\n case \"NamespaceOperationValidationFailed\":\n throw new OnboardingError(validationFailedMessage(result))\n case \"TransactionWillFail\":\n throw new OnboardingError(result.reason)\n }\n\n const walletClient = createLensWalletClient({ wallet, client: auth.thirdwebClient, chain: auth.chain, viemChain: auth.viemChain })\n const sent = await handleOperationWith(walletClient)(result)\n if (sent.isErr()) throw sent.error\n const hash = sent.value\n\n const indexed = await session.waitForTransaction(hash)\n if (indexed.isErr()) {\n // The Lens API stops waiting for indexing after 10s on mainnet, so the receipt decides whether\n // the transaction failed. If it can't be read either, the account lookup below still settles it.\n const receipt = await getTransactionReceipt(walletClient, { hash }).catch(() => null)\n if (receipt?.status === \"reverted\") throw indexed.error\n }\n\n for (let attempt = 0; attempt < ACCOUNT_POLL_ATTEMPTS; attempt++) {\n const fetched = await fetchAccount(session, { txHash: hash })\n if (fetched.isOk() && fetched.value) return fetched.value\n await new Promise((resolve) => setTimeout(resolve, ACCOUNT_POLL_INTERVAL_MS))\n }\n throw new OnboardingError(\"Your account was created but isn’t ready yet. Sign in to it again in a minute.\")\n}\n\n// An account's username in the namespace it prefers: the app's namespace, then the global Lens one, then\n// any other. So accounts from apps with their own namespace can sign in too. Null if it has none.\nexport async function fetchAccountUsername(auth: LensAuth, accountAddress: string): Promise<string | null> {\n const usernames: { localName: string; namespace: string; value: string }[] = []\n let cursor: Cursor | null = null\n do {\n const result = await fetchUsernames(auth.lensClient, {\n filter: { linkedTo: evmAddress(accountAddress) },\n pageSize: PageSize.Fifty,\n cursor,\n })\n if (result.isErr()) throw result.error\n usernames.push(...result.value.items)\n cursor = (result.value.pageInfo.next as Cursor | null) ?? null\n } while (cursor)\n const preferred = auth.usernameNamespace?.toLowerCase()\n const rank = (username: { namespace: string; value: string }) =>\n username.namespace.toLowerCase() === preferred ? 0 : username.value.startsWith(\"lens/\") ? 1 : 2\n const [best] = usernames.sort((a, b) => rank(a) - rank(b))\n return best?.localName ?? null\n}\n\n// Signs the app in to the new account without another signature: the onboarding session switches to it,\n// and its tokens are handed to the app's Lens client, like a sign-in from the account list (which revokes\n// the session it replaces, when creating an account while signed in to another).\nexport async function signInToCreatedAccount(auth: LensAuth, session: SessionClient, accountAddress: string): Promise<void> {\n const switched = await session.switchAccount({ account: evmAddress(accountAddress) })\n if (switched.isErr()) throw switched.error\n await auth.startSession(sessionCredentials(switched.value))\n const resumed = await auth.lensClient.resumeSession()\n if (resumed.isErr()) throw resumed.error\n}\n"],"mappings":"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","names":["testnet","PublicClient","evmAddress","signatureFrom","PublicClient","evmAddress","signatureFrom"]}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { L as LensChallenge, a as LensAuth, T as TokenService, b as LensEnvironmentName } from './auth-vTn7nb6F.js';
2
- export { C as CHALLENGE_MAX_AGE_MS, c as CHALLENGE_REFRESH_MS, d as LENS_CHAIN_IDS, e as LensAuthOptions, f as LensCredentials, g as LensLoginRole, h as LensSessionInfo, M as MessageSigner, i as TOKENS_CHANGED_EVENT, j as TokenData, k as createLensAuth, l as lensApiOrigin, o as opensWalletWithDeepLink, s as signLensChallenge } from './auth-vTn7nb6F.js';
1
+ import { L as LensChallenge, a as LensAuth, T as TokenService, b as LensEnvironmentName } from './auth-D0mva6fi.js';
2
+ export { C as CHALLENGE_MAX_AGE_MS, c as CHALLENGE_REFRESH_MS, d as LENS_CHAIN_IDS, e as LensAuthOptions, f as LensCredentials, g as LensLoginRole, h as LensSessionInfo, M as MessageSigner, i as TOKENS_CHANGED_EVENT, j as TokenData, k as createLensAuth, l as lensApiOrigin, o as opensWalletWithDeepLink, s as signLensChallenge } from './auth-D0mva6fi.js';
3
3
  import * as _lens_protocol_client from '@lens-protocol/client';
4
4
  import { PublicClient, SessionClient, Account, IStorageProvider } from '@lens-protocol/client';
5
5
  import { Wallet } from 'thirdweb/wallets';
package/dist/index.js CHANGED
@@ -26,7 +26,7 @@ import {
26
26
  signLensChallenge,
27
27
  uploadAccountMetadata,
28
28
  usernameFormatError
29
- } from "./chunk-PJC2S3IU.js";
29
+ } from "./chunk-OLO3H3YN.js";
30
30
  export {
31
31
  CHALLENGE_MAX_AGE_MS,
32
32
  CHALLENGE_REFRESH_MS,
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { Account, Wallet } from 'thirdweb/wallets';
3
3
  import { ConvexReactClient } from 'convex/react';
4
- import { a as LensAuth } from '../auth-vTn7nb6F.js';
4
+ import { a as LensAuth } from '../auth-D0mva6fi.js';
5
5
  import { ConnectButtonProps } from 'thirdweb/react';
6
6
  import '@lens-protocol/client';
7
7
  import 'thirdweb/chains';
@@ -32,8 +32,14 @@ type LensAuthContextValue = {
32
32
  wallet: Account | undefined;
33
33
  /** The signed-in Lens account */
34
34
  account: SignedInAccount | null;
35
- /** Ends the Lens session and disconnects the wallet */
36
- signOut: () => Promise<void>;
35
+ /**
36
+ * Ends the Lens session and disconnects the wallet. The session is revoked on the Lens API, so its tokens
37
+ * stop working even if they were copied; `revoked` is false when that failed (the session is cleared
38
+ * from this browser either way).
39
+ */
40
+ signOut: () => Promise<{
41
+ revoked: boolean;
42
+ }>;
37
43
  /** Opens the account dialog, e.g. to switch to another account */
38
44
  openAccountDialog: () => void;
39
45
  };
@@ -59,7 +65,13 @@ type LensAuthProviderProps = {
59
65
  * Called after the user signs in from the account dialog (picking an account or creating one), once
60
66
  * Convex accepts the session. A good place to create the user's row: `convex.mutation(api.users.ensure)`
61
67
  */
62
- onSignIn?: (account: SignedInAccount) => void | Promise<void>;
68
+ onSignIn?: (account: SignedInAccount) => unknown;
69
+ /**
70
+ * Called when the signed-in Lens account changes, however it happens: signing in, switching accounts,
71
+ * logging out, or the session ending (it expired, or another tab logged out). Not called when a stored
72
+ * session is restored on load. A good place to delete data your app keeps in the browser for `previous`.
73
+ */
74
+ onAccountChange?: (previous: SignedInAccount | null, next: SignedInAccount | null) => unknown;
63
75
  /** Render the account dialog. Set to false to render <AccountDialog /> yourself. Defaults to true. */
64
76
  accountDialog?: boolean;
65
77
  /**
@@ -69,7 +81,7 @@ type LensAuthProviderProps = {
69
81
  wallets?: Wallet[];
70
82
  children: React.ReactNode;
71
83
  };
72
- declare function LensAuthProvider({ auth, convex, onSignIn, accountDialog, wallets, children }: LensAuthProviderProps): React.JSX.Element;
84
+ declare function LensAuthProvider({ auth, convex, onSignIn, onAccountChange, accountDialog, wallets, children }: LensAuthProviderProps): React.JSX.Element;
73
85
 
74
86
  type SignInButtonProps = {
75
87
  /** Defaults to "Sign in" */
@@ -16,7 +16,7 @@ import {
16
16
  signLensChallenge,
17
17
  uploadAccountMetadata,
18
18
  usernameFormatError
19
- } from "../chunk-PJC2S3IU.js";
19
+ } from "../chunk-OLO3H3YN.js";
20
20
 
21
21
  // src/react/provider.tsx
22
22
  import * as React4 from "react";
@@ -423,6 +423,7 @@ var USERNAME_DEBOUNCE_MS = 300;
423
423
  var MAX_PICTURE_BYTES = 10 * 1024 * 1024;
424
424
  function CreateAccountForm({ ownerAddress, onCreated, onBack, actions }) {
425
425
  const { auth } = useLensAuth();
426
+ const { selectAccount } = useAccountDialogState();
426
427
  const activeAccount = useActiveAccount();
427
428
  const wallet = useActiveWallet();
428
429
  const [client] = React.useState(() => createOnboardingClient(auth));
@@ -501,6 +502,7 @@ function CreateAccountForm({ ownerAddress, onCreated, onBack, actions }) {
501
502
  created.current = { address: account.address, localName };
502
503
  }
503
504
  setStep({ status: "signing-in" });
505
+ selectAccount({ address: created.current.address.toLowerCase(), username: created.current.localName });
504
506
  await signInToCreatedAccount(auth, onboarding, created.current.address);
505
507
  await onCreated(created.current);
506
508
  };
@@ -958,7 +960,7 @@ function AccountPicker({ actions }) {
958
960
  }
959
961
  function AccountPickerBody({ wallet, creating, setCreating, actions }) {
960
962
  const { auth } = useLensAuth();
961
- const { completeSignIn, setHeader } = useAccountDialogState();
963
+ const { selectAccount, completeSignIn, setHeader } = useAccountDialogState();
962
964
  const { data, loading: loadingAccounts } = useAccountsAvailable({ managedBy: evmAddress(wallet.address), includeOwned: true });
963
965
  const accounts = data?.items;
964
966
  const usernames = useAccountUsernames(auth, accounts);
@@ -971,8 +973,10 @@ function AccountPickerBody({ wallet, creating, setCreating, actions }) {
971
973
  const showCreate = creating || !loading && !hasAccounts;
972
974
  const signIn = async (account, challenge) => {
973
975
  const signature = await auth.signChallenge(challenge, wallet);
976
+ const signedIn = { address: account.address.toLowerCase(), username: usernameOf(account) };
977
+ selectAccount(signedIn);
974
978
  await auth.authenticate(challenge, signature);
975
- completeSignIn({ address: account.address.toLowerCase(), username: usernameOf(account) });
979
+ completeSignIn(signedIn);
976
980
  };
977
981
  const handleCreated = async (created) => {
978
982
  completeSignIn({ address: created.address.toLowerCase(), username: created.localName });
@@ -1175,9 +1179,9 @@ function createUseConvexAuth(auth) {
1175
1179
  };
1176
1180
  }
1177
1181
  var AutoConnect = React4.lazy(() => import("thirdweb/react").then((m) => ({ default: m.AutoConnect })));
1178
- function LensAuthProvider({ auth, convex, onSignIn, accountDialog = true, wallets, children }) {
1182
+ function LensAuthProvider({ auth, convex, onSignIn, onAccountChange, accountDialog = true, wallets, children }) {
1179
1183
  const [useConvexAuthHook] = React4.useState(() => createUseConvexAuth(auth));
1180
- const content = /* @__PURE__ */ jsxs6(LensAuthState, { auth, onSignIn, withConvex: !!convex, wallets, children: [
1184
+ const content = /* @__PURE__ */ jsxs6(LensAuthState, { auth, onSignIn, onAccountChange, withConvex: !!convex, wallets, children: [
1181
1185
  children,
1182
1186
  accountDialog && /* @__PURE__ */ jsx12(AccountDialog, {})
1183
1187
  ] });
@@ -1199,7 +1203,7 @@ function writeSelectedAccount(auth, account) {
1199
1203
  } catch {
1200
1204
  }
1201
1205
  }
1202
- function LensAuthState({ auth, onSignIn, withConvex, wallets, children }) {
1206
+ function LensAuthState({ auth, onSignIn, onAccountChange, withConvex, wallets, children }) {
1203
1207
  const wallet = useActiveAccount2();
1204
1208
  const activeWallet = useActiveWallet2();
1205
1209
  const { disconnect } = useDisconnect();
@@ -1207,22 +1211,26 @@ function LensAuthState({ auth, onSignIn, withConvex, wallets, children }) {
1207
1211
  const [manualOpen, setManualOpen] = React4.useState(false);
1208
1212
  const [header, setHeader] = React4.useState(DEFAULT_HEADER);
1209
1213
  const [pendingSignIn, setPendingSignIn] = React4.useState(null);
1214
+ const [selected, setSelected] = React4.useState(null);
1210
1215
  const account = React4.useMemo(() => {
1211
1216
  if (!sessionKey) return null;
1212
1217
  const address = sessionKey.slice(sessionKey.lastIndexOf(":") + 1);
1213
- return { address, username: readSelectedAccount(auth, address) };
1214
- }, [auth, sessionKey]);
1218
+ const username = selected?.address === address ? selected.username : readSelectedAccount(auth, address);
1219
+ return { address, username };
1220
+ }, [auth, sessionKey, selected]);
1215
1221
  const status = sessionKey === void 0 ? "loading" : sessionKey ? "signed-in" : wallet ? "choosing-account" : "signed-out";
1216
1222
  useBackgroundRefresh(auth);
1223
+ useAccountChange(status, account, onAccountChange);
1217
1224
  React4.useEffect(() => {
1218
1225
  if (wallet) void auth.resumeSession().catch(() => {
1219
1226
  });
1220
1227
  }, [auth, wallet?.address]);
1221
1228
  const signOut = React4.useCallback(async () => {
1222
1229
  setManualOpen(false);
1230
+ setSelected(null);
1223
1231
  writeSelectedAccount(auth, null);
1224
1232
  if (activeWallet) disconnect(activeWallet);
1225
- await auth.logout();
1233
+ return auth.logout();
1226
1234
  }, [auth, activeWallet, disconnect]);
1227
1235
  const value = React4.useMemo(() => ({
1228
1236
  auth,
@@ -1240,11 +1248,13 @@ function LensAuthState({ auth, onSignIn, withConvex, wallets, children }) {
1240
1248
  required,
1241
1249
  ...header,
1242
1250
  setHeader,
1251
+ selectAccount: setSelected,
1243
1252
  setOpen: (open) => {
1244
1253
  if (open) setManualOpen(true);
1245
1254
  else if (!required) setManualOpen(false);
1246
1255
  },
1247
1256
  completeSignIn: (signedIn) => {
1257
+ setSelected(signedIn);
1248
1258
  writeSelectedAccount(auth, signedIn);
1249
1259
  setManualOpen(false);
1250
1260
  if (onSignIn) setPendingSignIn(signedIn);
@@ -1266,6 +1276,22 @@ function ConvexSignInCallback(props) {
1266
1276
  const { isAuthenticated, isLoading } = useConvexAuth();
1267
1277
  return isAuthenticated && !isLoading ? /* @__PURE__ */ jsx12(SignInCallback, { ...props }) : null;
1268
1278
  }
1279
+ function useAccountChange(status, account, onAccountChange) {
1280
+ const last = React4.useRef(void 0);
1281
+ const callback = React4.useRef(onAccountChange);
1282
+ React4.useEffect(() => {
1283
+ callback.current = onAccountChange;
1284
+ });
1285
+ const loading = status === "loading";
1286
+ const address = account?.address ?? null;
1287
+ React4.useEffect(() => {
1288
+ if (loading) return;
1289
+ const previous = last.current;
1290
+ last.current = account;
1291
+ if (previous === void 0 || (previous?.address ?? null) === address) return;
1292
+ Promise.resolve().then(() => callback.current?.(previous, account)).catch((err) => console.error("lens-convex-auth: onAccountChange failed", err));
1293
+ }, [loading, address]);
1294
+ }
1269
1295
  function useBackgroundRefresh(auth) {
1270
1296
  React4.useEffect(() => {
1271
1297
  let timeout;