Skip to content

feat: Add Farcaster miniapp support for identity verification flow #10#14

Open
vortex-hue wants to merge 43 commits intoGoodDollar:mainfrom
vortex-hue:add-farcaster-support-for-the-identity-flow
Open

feat: Add Farcaster miniapp support for identity verification flow #10#14
vortex-hue wants to merge 43 commits intoGoodDollar:mainfrom
vortex-hue:add-farcaster-support-for-the-identity-flow

Conversation

@vortex-hue
Copy link

@vortex-hue vortex-hue commented Aug 14, 2025

Description

This PR implements comprehensive Farcaster miniapp support for the identity verification flow, enabling seamless face verification within Farcaster miniapps with proper navigation and universal link handling. The implementation includes automatic miniapp detection, smart navigation, and robust response handling while fixing critical build system issues that were preventing successful compilation.

The changes address the need for better Farcaster miniapp integration by implementing the official @farcaster/miniapp-sdk, adding universal link support for mobile/native compatibility, and creating utilities for handling post-verification flow continuation. Additionally, this PR resolves significant build configuration issues including Node.js dependency conflicts in browser builds and security vulnerabilities related to environment variable exposure.

Dependencies added:

  • @farcaster/miniapp-sdk (peer dependency)
  • Browser polyfills for Node.js modules (crypto-browserify, stream-browserify, buffer)

About #10

Demo Video: Loom GoodSDK Forecaster Support

I would like to know how I can demo on a mobile please, but I was able to test the demo on a browser.

Checklist:

Key Implementation Details:

Farcaster Integration:

  • isInFarcasterMiniApp() - Async detection using official SDK
  • navigateToFaceVerification() - Smart navigation with automatic environment detection
  • openUrlInFarcaster() - Official SDK integration with openUrl method
  • handleVerificationResponse() - Generic utility for post-verification flow
  • createUniversalLinkCallback() - Universal link support for mobile/native apps

Build System Fixes:

  • Externalized Node.js modules (crypto, http, https, stream, zlib, etc.) for browser compatibility
  • Fixed security vulnerabilities by replacing full process.env exposure with specific variables
  • Optimized tsup configuration for ESM-only builds to avoid IIFE conflicts
  • Restructured package dependencies to use peer dependencies for heavy libraries

Sensitive Files Modified:

These are some of the files I modified after my implementation due to build error I was getting, because it was building for browser, meanwhile some Node.js functions are also present, I'd appreciate if there's another way I could resolve the error without modifying these files to avoid a merge conflict.

  • Core SDK: packages/citizen-sdk/src/index.ts, packages/citizen-sdk/src/utils/auth.ts
  • Build configs: packages/ui-components/tsup.config.claim.ts, packages/ui-components/package.json
  • Demo apps: apps/demo-identity-app/vite.config.mts, apps/demo-webcomponents/vite.config.mts
  • Components: apps/demo-identity-app/src/components/VerifyButton.tsx
  • Types: apps/demo-identity-app/src/globals.d.ts
  • yarn.lock - Updated dependencies

@korbit-ai
Copy link

korbit-ai bot commented Aug 14, 2025

You've used up your 5 PR reviews for this month under the Korbit Starter Plan. You'll get 5 more reviews on August 19th, 2025 or you can upgrade to Pro for unlimited PR reviews and enhanced features in your Korbit Console.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • Extract the repeated list of externalized Node.js modules into a shared build config or utility to avoid duplication across your Vite and tsup configurations.
  • Cache or hoist the dynamic import of '@farcaster/miniapp-sdk' so you’re not re-importing it multiple times during fallback detection and navigation.
  • Refactor the universal link generation and Farcaster navigation logic into a single shared helper or custom hook to eliminate duplication across IdentitySDKs and the demo app.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Extract the repeated list of externalized Node.js modules into a shared build config or utility to avoid duplication across your Vite and tsup configurations.
- Cache or hoist the dynamic import of '@farcaster/miniapp-sdk' so you’re not re-importing it multiple times during fallback detection and navigation.
- Refactor the universal link generation and Farcaster navigation logic into a single shared helper or custom hook to eliminate duplication across IdentitySDKs and the demo app.

## Individual Comments

### Comment 1
<location> `packages/citizen-sdk/src/utils/auth.ts:94` </location>
<code_context>
+ * @param url - The current URL or callback URL to parse
+ * @returns Object containing verification status and any additional parameters
+ */
+export function handleVerificationResponse(url?: string): {
+  isVerified: boolean;
+  params: URLSearchParams;
</code_context>

<issue_to_address>
Parsing the URL without error handling may throw if the input is malformed.

If parsing fails due to an invalid URL, the function will throw. Please add a try/catch block and return a default value when parsing fails.
</issue_to_address>

### Comment 2
<location> `packages/citizen-sdk/src/utils/auth.ts:7` </location>
<code_context>
+/**
+ * Detects if the SDK is running inside a Farcaster miniapp using the official SDK
+ */
+export async function isInFarcasterMiniApp(timeoutMs: number = 100): Promise<boolean> {
+  if (typeof window === "undefined") return false;
+  
</code_context>

<issue_to_address>
Consider refactoring repeated dynamic imports and fallback logic into helper functions to simplify and flatten the code structure.

Here’s one way to collapse all of the repeated dynamic‐imports, pull the fallback logic out into a small helper, and flatten the nesting in isInFarcasterMiniApp / openUrlInFarcaster:

```ts
// new helper to lazy-load & cache the SDK
let _cachedSdk: typeof import('@farcaster/miniapp-sdk').sdk | null = null;
async function loadFarcasterSdk() {
  if (!_cachedSdk) {
    const { sdk } = await import('@farcaster/miniapp-sdk');
    _cachedSdk = sdk;
  }
  return _cachedSdk;
}

// new helper for the fallback context check
async function fallbackDetect() {
  try {
    const sdk = await loadFarcasterSdk();
    const ctx = await sdk.context;
    return !!(ctx.location && ctx.location.type != null);
  } catch {
    return false;
  }
}

export async function isInFarcasterMiniApp(timeoutMs = 100): Promise<boolean> {
  if (typeof window === 'undefined') return false;
  try {
    const sdk = await loadFarcasterSdk();
    return await sdk.isInMiniApp();
  } catch {
    console.warn('SDK failed, trying context fallback…');
    return fallbackDetect();
  }
}

export async function openUrlInFarcaster(
  url: string,
  fallbackToNewTab = true
): Promise<void> {
  if (typeof window === 'undefined') {
    throw new Error('Browser only');
  }

  if (await isInFarcasterMiniApp()) {
    try {
      const sdk = await loadFarcasterSdk();
      await sdk.actions.ready();
      await sdk.actions.openUrl(url);
      return;
    } catch {
      console.warn('SDK.openUrl failed, falling back…');
    }
  }

  fallbackToNewTab ? window.open(url, '_blank') : (window.location.href = url);
}
```

Benefits:

• All `import('@farcaster/miniapp-sdk')` calls are in one place and cached  
• Fallback context logic lives in its own small function  
• Try/catch blocks are only one level deep in each API function  
• Behaviour is unchanged.
</issue_to_address>

### Comment 3
<location> `apps/demo-identity-app/vite.config.mts:37` </location>
<code_context>
   },
+  build: {
+    rollupOptions: {
+      external: [
+        // External Node.js modules that should not be bundled for browser
+        "crypto",
</code_context>

<issue_to_address>
Consider generating the list of Node.js built-ins and their globals dynamically using module.builtinModules instead of maintaining a manual array.

You can eliminate the manual list entirely by pulling Node’s built-ins from `module.builtinModules` (and generate the `node:`-prefixed variants on the fly). For example:

```js
// vite.config.js
import { builtinModules } from 'module'

const builtins = [
  ...builtinModules,
  ...builtinModules.map((m) => `node:${m}`)
]

const globals = builtins.reduce((acc, name) => {
  // strip the `node:` prefix for the global key if present
  const key = name.startsWith('node:') ? name.slice(5) : name
  // map both `foo` and `node:foo` → global `foo`
  acc[name] = key
  return acc
}, {})

export default defineConfig({
  // …other config…
  build: {
    rollupOptions: {
      external: builtins,
      output: { globals }
    }
  }
})
```

This keeps every built-in, automatically picks up new ones, and avoids the long hand-written arrays.
</issue_to_address>

### Comment 4
<location> `packages/ui-components/tsup.config.claim.ts:16` </location>
<code_context>
   },
+  build: {
+    rollupOptions: {
+      external: [
+        // External Node.js modules that should not be bundled for browser
+        "crypto",
</code_context>

<issue_to_address>
Consider dynamically listing Node.js built-ins or using a regex to avoid manually maintaining a long list of external dependencies.

You can drop the hundreds-of-lines hand–listing by pulling Node’s built-ins dynamically from the `module` package (and catching both plain and `node:` names). For example:

```ts
// tsup.config.ts
import { defineConfig } from "tsup"
import { builtinModules } from "module"

const nodeBuiltins = [
  ...builtinModules,              // ['fs','path',…]
  ...builtinModules.map((m) => `node:${m}`), // ['node:fs','node:path',…]
]

export default defineConfig({
  entry: ["src/index.ts"],
  format: ["esm"],
  platform: "browser",
  globalName: "ClaimButton",
  splitting: false,
  sourcemap: false,
  clean: true,
  dts: false,
  minify: true,
  target: "ESNext",
  outDir: "dist",
  external: [
    "@goodsdks/citizen-sdk",
    "viem",
    ...nodeBuiltins,
  ],
  noExternal: [
    "lit",
    "@reown/appkit",
    "@reown/appkit-adapter-ethers",
    "ethers",
  ],
})
```

If you’d rather use a regex to catch all `node:` imports and leave plain built-ins implicit, you can shrink it even more:

```ts
export default defineConfig({
  //
  external: [
    "@goodsdks/citizen-sdk",
    "viem",
    /^node:/,          // all `node:xxx`
    // (esbuild will already treat bare built-ins as external in browser builds)
  ],
  //
})
```

Either approach removes the manual maintenance burden while keeping the same behavior.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@vortex-hue
Copy link
Author

vortex-hue commented Aug 14, 2025

review changes by sorcery-ai implemented

Copy link
Collaborator

@L03TJ3 L03TJ3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think I miss seeing handling of the 'verified' param that you get when returning from the face-verification?

@vortex-hue
Copy link
Author

I also think I miss seeing handling of the 'verified' param that you get when returning from the face-verification?

The verified param from face verification is already properly handled in the demo-identity-app (App.tsx lines 59-71) using the handleVerificationResponse() utility function.

@vortex-hue
Copy link
Author

also, I'd ⁠work on building a test mini-app on farcaster, and demonstrate how it works

Copy link
Contributor

@sirpy sirpy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. The code needs to be organized better. Many duplicates, single line functions etc.
  2. since farcaster always opens a new tab/window it is possibly best to force cbu, ie popup mode, where the tab/window will be closed after FV is done without redirect back.
  3. for cbu need to update the check is verified, to not only check the url for isVerified param (since there is no redirect with that param in cbu mode) but also to accept as input from developer the wallet address and check on-chain if wallet is now whitelisted

"format": "prettier --write ."
},
"dependencies": {
"@farcaster/miniapp-sdk": "^0.1.8",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be devdep/peerdep

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vortex-hue this is not done

Comment on lines 31 to 35
build: {
rollupOptions: {
external: ["@goodsdks/citizen-sdk"]
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesnt make sense this has to be included

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This external configuration is required to prevent the citizen-sdk (which contains Node.js dependencies) from being bundled in the browser environment. Without this, the build fails with 'Agent is not exported by vite-browser-external' errors. The citizen-sdk needs to remain external for this demo app.

/**
* Create a universal link compatible callback URL
*/
private createUniversalLinkCallback(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicate code

url: string,
fallbackToNewTab: boolean = true
): Promise<void> {
if (typeof window === "undefined") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicate code. if in farcaster then it is obvious theres a window this check is redundant

@sirpy
Copy link
Contributor

sirpy commented Aug 17, 2025

@L03TJ3 the non custodial is just a duplicate code requiring now multiple maintenance. it should be integrated into the regular sdk

@vortex-hue
Copy link
Author

vortex-hue commented Aug 23, 2025

Hi @L03TJ3 @sirpy, I've implemented all the corrections, but am yet to still understand how to test it in mini app, should I turn the example test app into a forecaster mini app?

@sirpy
Copy link
Contributor

sirpy commented Aug 24, 2025

Hi @L03TJ3 @sirpy, I've implemented all the corrections, but am yet to still understand how to test it in mini app, should I turn the example test app into a forecaster mini app?

either it should work in both (web/farcaster) or you can create a similar farcaster miniapp

@vortex-hue
Copy link
Author

Hi @sirpy @L03TJ3 , I've been able to test it in web/farcaster environments, here's the loom: Demo in Forecaster & Web, also thanks Lewis for other time, and Emiri helped me alot too

@vortex-hue
Copy link
Author

Do let me know if there's anything else pending, thanks alot.

@vortex-hue
Copy link
Author

Hi @L03TJ3 have you gotten time to review this pr and also probably watch the loom, so I can complete this task.

@sirpy
Copy link
Contributor

sirpy commented Sep 2, 2025

@vortex-hue Please make sure you went over all the open items and made the relevant changes.
The loom looks nice! can you also test it on the mobile farcaster app?

@L03TJ3 should review this by end of week

@vortex-hue
Copy link
Author

@vortex-hue Please make sure you went over all the open items and made the relevant changes. The loom looks nice! can you also test it on the mobile farcaster app?

@L03TJ3 should review this by end of week

Hi @sirpy, I went through all open items and also made the changes requested, is there any specific one I missed? and sure, let me try testing it on mobile right away.

Comment on lines 56 to 59
window.location.href.includes("farcaster") ||
window.location.href.includes("miniapp") ||
// Check if we're in an iframe which is common for miniapps
window.self !== window.top
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 72 to 74
if (typeof window === "undefined") {
throw new Error("URL opening is only supported in browser environments.");
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* @param additionalParams - Additional parameters to include
* @returns A universal link compatible URL
*/
export function createUniversalLinkCallback(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I requested this flow so that the verified param can be appened and a app can handle the redirectBack from face-verification.
You think this should be handled different?

* @param additionalParams - Additional parameters to include
* @returns A universal link compatible URL
*/
export function createUniversalLinkCallback(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

vercel bot commented Sep 2, 2025

Deployment failed with the following error:

The provided GitHub repository does not contain the requested branch or commit reference. Please ensure the repository is not empty.

… also created packages/build-config/index.ts with dynamic Node.js built-ins generation
@vortex-hue vortex-hue force-pushed the add-farcaster-support-for-the-identity-flow branch from 2654e1c to 4060809 Compare October 9, 2025 11:22

interface VerifyButtonProps {
onVerificationSuccess: () => void
// No props needed - verification success is handled by URL callback detection
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if not used, remove @vortex-hue

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Collaborator

@L03TJ3 L03TJ3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the project does not build:

│ src/sdks/viem-claim-sdk.ts:20:2: ERROR: No matching export in "src/constants.ts" for import "contractAddresses"
│ src/sdks/viem-claim-sdk.ts:22:26: ERROR: No matching export in "src/constants.ts" for import "getGasPrice"
│ src/utils/auth.ts:2:50: ERROR: No matching export in "src/constants.ts" for import "contractAddresses"

import { waitForTransactionReceipt } from "viem/actions"

import { IdentitySDK } from "./viem-identity-sdk"
import { createUniversalLinkCallback } from "../utils/auth"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not seem to be used, why is it imported?

Resolved conflicts in:
- packages/citizen-sdk/src/sdks/viem-claim-sdk.ts
- packages/citizen-sdk/src/sdks/viem-identity-sdk.ts

Merged changes:
- Kept Farcaster miniapp support (Universal Links, callback URLs)
- Integrated new chain management system with fallbacks
- Added RPC fallback mechanism
- Refactored faucet trigger utility
- Updated to use identitySDK.navigateToFaceVerification() instead of fvRedirect()
After merging upstream/main, the contract address structure changed from
contractAddresses to chainConfigs. Updated isAddressWhitelisted function
to use the new chainConfigs structure.
@vortex-hue vortex-hue requested a review from L03TJ3 November 10, 2025 22:12
@L03TJ3 L03TJ3 linked an issue Nov 21, 2025 that may be closed by this pull request
6 tasks
- Remove excessive console.log statements from auth utilities and SDKs
- Simplify error handling by removing redundant try-catch blocks
- Remove verbose JSDoc comments that state the obvious
- Consolidate duplicate navigation functions
- Improve code readability and maintainability
- Follow KISS, DRY, and clarity over cleverness principles
- Remove unnecessary console.log/console.error statements
- Remove unused onVerificationSuccess prop from VerifyButton
- Simplify error handling in demo components
- Rename callBackExample to txConfirm for clarity
- Follow DRY and KISS principles
Copy link
Collaborator

@L03TJ3 L03TJ3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of the comments:
A proposed solution in a PR to resolve an issue should tackle only what is relevant to your solution and should not introduced extra refactors or changes without an explicit reason

} catch (error) {
console.error("Verification failed:", error)
// Handle error (e.g., show toast)
} catch {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This refactor is both not needed or in any way related to your fixes for farcaster + its not true.
generateFVLink throws an error and is not handled there

},
}

/**
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these are just placeholders and not working this should be clarified in a readme and should be configurable options for developers to pass down
(we are not hosting a farcaster app, developers integrating the identitysdk/claim-sdk might)

}
}

export async function handleVerificationResponse(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not being used anywhere, whats the purpose?
if not needed, remove

}
}

export function handleVerificationResponseSync(url?: string): {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not being used anywyere, if not needed, remove

return universalLink;
}

export async function createVerificationCallbackUrl(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this supposed to be handling?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It validates the URL and enforces a routing convention (appending /verify if missing) to ensure the callback hits a route handled by the SDK/app

* @param authPeriod - The authentication period.
* @returns The identity expiry data.
*/
calculateIdentityExpiry(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why got the comments removed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was cleaning up the codebase

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We write claritive comments to explain to anyone reading it what it does.
removing comments is not part of your task or is contributing to anything.
revert changes towards it.

your pull-request should only tackle what is described in the issue

);
params[popupMode ? "cbu" : "rdu"] = universalLinkCallback;
} else {
const callbackUrlWithParams = await createVerificationCallbackUrl(callbackUrl, {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are these params used for, and why hardcode 'verified': true?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vortex-hue comment not addressed

if (!isWhitelisted) {
await this.fvRedirect()
// Use IdentitySDK's navigation method to eliminate code duplication
await this.identitySDK.navigateToFaceVerification(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. what duplication is being handled here exactly?
  2. if you choose to do a refactor, always make sure functionality does not change along the way

You introduce: hardcoded chain-id, different behavior of reloading page etc.
Its not necessarily a bad idea to move handling of face-verification to the identity-sdk.

but it should always be trippled checked against exisiting flows.
how would this affect dapps that already have this flow?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. chainid should not be hardcoded to celo
  2. Flow should stay the same as it was, changes needed for your flow should only affect your intended flow

And include a demo that this change works as expected, as I am not sure this is the way to handle pop-up window (fallbackToNewTab? should it not just close the pop-up window?)

return new IdentitySDK({ account, ...props })
}

/**
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why removing comment?

console.error("submitAndWait Error:", error)
throw new Error(`Failed to submit transaction: ${error.message}`)
}
return waitForTransactionReceipt(this.publicClient, { hash })
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are removing a valid catch error flow that could be used to give feedback to a user

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vortex-hue comment not addresses

- Restore original fvRedirect behavior in ClaimSDK (window.location.href)
- Use fvDefaultChain instead of hardcoded chainId
- Restore error handling in submitAndWait
- Restore removed JSDoc comments
- Remove unused handleVerificationResponse functions
- Fix VerifyButton error handling
- Add FarcasterAppConfigs documentation to README
- Add JSDoc to createVerificationCallbackUrl
console.error("submitAndWait Error:", error)
throw new Error(`Failed to submit transaction: ${error.message}`)
}
return waitForTransactionReceipt(this.publicClient, { hash })
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vortex-hue comment not addresses

);
params[popupMode ? "cbu" : "rdu"] = universalLinkCallback;
} else {
const callbackUrlWithParams = await createVerificationCallbackUrl(callbackUrl, {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vortex-hue comment not addressed

* @param authPeriod - The authentication period.
* @returns The identity expiry data.
*/
calculateIdentityExpiry(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We write claritive comments to explain to anyone reading it what it does.
removing comments is not part of your task or is contributing to anything.
revert changes towards it.

your pull-request should only tackle what is described in the issue

await this.identitySDK.getWhitelistedRoot(userAddress)
if (!isWhitelisted) {
await this.fvRedirect()
// Use IdentitySDK's navigation method to eliminate code duplication
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comments should not describe changes done for a commit.
it should explain current behavior, possibly edge-cases or why something has been done in a particular way.

Thats why my suggestion

setIsVerified(isWhitelisted ?? false)
} catch (error) {
console.error("Error checking whitelist:", error)
} catch {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe the setIsWhitelisted should have been added in the catch but there is no reason to remove the error log

throw new Error(`Failed to submit transaction: ${error.message}`)
}
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneccessary remove of comment @vortex-hue

functionName: "getWhitelistedRoot",
args: [account],
})
const root = await this.publicClient.readContract({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your commit statement that changed this reads: - Simplify error handling by removing redundant try-catch blocks

  1. would like to understand your train of thought why this seems redundant?
  2. The explanation why its not redundant is because we are providing both developer and user a meaningful error to handle or see without the front-end of the app breaking.
    If you dont catch here, and there is an rpc error, it will break frontend with a runtime error.

Developer: might want to direct users to a particular screen/flow, might show a particular error, depending on what error is returned by the rpc
User: wants to know whats happening, and wants to know why something is not working

revert changes that were applied based on 'redundant try-catch blocks' they are there for a reason

}
}

async navigateToFaceVerification(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if not used, remove

Comment on lines 149 to 158
if (!url.pathname.includes('/verify') && !url.pathname.includes('/callback')) {
url.pathname = url.pathname.endsWith('/')
? `${url.pathname}verify`
: `${url.pathname}/verify`;
}

if (additionalParams) {
Object.entries(additionalParams).forEach(([key, value]) => {
url.searchParams.set(key, value);
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the original request has not been understood.
we are not going to enforce users to create a particular endpoint.
What we provide is helpers to the verified param, for them to handle UX/UI.

you get a queryParam 'verified' (true/false).
this shows if someone face-verification has been succesfull.
all we need is a helper that parses it.
in the original issue a reference has been shared where we do this in our old sdk: https://github.com/GoodDollar/GoodWeb3-Mono/blob/9846f16b16f2d8406407dd48231ea7c9e33e751e/packages/good-design/src/core/buttons/ClaimButton.tsx#L31

- Restore try-catch in getWhitelistedRoot with meaningful error message
- Re-throw original error in submitAndWait to preserve error type
- Simplify createVerificationCallbackUrl (remove /verify path enforcement)
- Add parseVerificationResult helper for verified query param parsing
- Restore console.error in App.tsx catch block
- Remove unused navigateToFaceVerification method
- Remove commit-style comment in fvRedirect
"zod": "^3.24.2"
},
"devDependencies": {
"@farcaster/miniapp-sdk": "^0.1.8",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also peer dep

output: {
globals: nodeBuiltinGlobals
}
external: ["@goodsdks/citizen-sdk", "viem"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"wagmi": "*"
},
"dependencies": {
"@farcaster/miniapp-sdk": "^0.1.8",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dev?peer? @L03TJ3

const fvLink = await identitySDK.generateFVLink(
false,
window.location.href,
42220,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this removed

}
}

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this removed

Comment on lines 29 to 33
createVerificationCallbackUrl,
createFarcasterCallbackUniversalLink,
isInFarcasterMiniApp,
navigateToUrl
} from "../utils/auth"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this here? this is the claim sdk.
the claim sdk can use the identity sdk

* Initialize the callback URL with proper Farcaster Universal Link support
* @param rdu - The redirect URL after claim
*/
private async initializeCallbackUrl(rdu?: string): Promise<void> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is duplicate. it should be in the identity sdk

return await waitForTransactionReceipt(this.publicClient, { hash })
} catch (error: any) {
console.error("submitAndWait Error:", error)
throw new Error(`Failed to submit transaction: ${error.message}`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why

}
}

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

restore all docs

*
* The SDK will default to these values if no config is provided, which will likely fail in production.
*/
export const FarcasterAppConfigs: Record<string, { appId: string; appSlug: string }> = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apps dont have envs in farcaster

@sirpy
Copy link
Contributor

sirpy commented Feb 11, 2026

@vortex-hue This has been open for a very long time. I want to close it this month.
I suggest you take some time to address all comments, clean your code and provide the missing mobile demo

@vortex-hue
Copy link
Author

vortex-hue commented Feb 12, 2026

Hi @sirpy, thanks for the review, I'd take some time during this week &/ weekend to resolve all issues then, and would also provide the demos.

Thanks for the patience.

- Restore all original JSDoc, try-catch, and error handling patterns
- Add resolveCallbackUrl to IdentitySDK for Farcaster/URL consolidation
- Remove duplicate Farcaster logic from ClaimSDK (delegate to IdentitySDK)
- Remove FarcasterAppConfigs (apps don't have envs in Farcaster)
- Remove redundant isAddressWhitelisted from auth utils
- Move @farcaster/miniapp-sdk to peerDependencies
- Restore VerifyButton original props and chain ID
- Add navigateToFaceVerification() to IdentitySDK for Farcaster-aware FV navigation
- ClaimSDK.fvRedirect() delegates to IdentitySDK instead of using window.location.href
- Refactor FarcasterAppConfig: domain is now required, appId/appSlug optional
- Support domain-based Farcaster deep links for redirect-back flow
- Pass farcasterConfig through useIdentitySDK and useClaimSDK hooks
- VerifyButton uses navigateToFaceVerification() for correct navigation
@vortex-hue
Copy link
Author

vortex-hue commented Feb 16, 2026

Hi @sirpy @L03TJ3 , here's the summary of everything done and abit of request from me.

So on Mobile (Farcaster Mini App): When a user taps "Verify Me" inside the miniapp, the SDK detects the Farcaster context and uses sdk.actions.openUrl to open the GoodDollar face verification page in the phone's browser. After verification, GoodDollar redirects to a Farcaster deep link (farcaster.xyz/~/mini-apps/launch?domain=...&verified=true), which the OS intercepts and reopens the Farcaster app — bringing the user back to the miniapp with the verified status.

On Web Browser: When a user clicks "Verify Me" on a regular web app, the SDK navigates directly to the GoodDollar face verification page via window.location.href. After verification, GoodDollar redirects back to the app's URL with ?verified=true, and the app reads the param to update the UI.

NB:

  • I've tested the full redirect flow and confirmed it works — the miniapp correctly opens the browser for FV and the deep link brings the user back to Farcaster. I usually click "I give up" and it redirects me back to farcaster.

  • The actual facial verification itself keeps failing during my demos, making it difficult to show the complete end-to-end flow.

  • If a staging/mock FV endpoint that bypasses the real liveness check could be provided for demo purposes, that would be very helpful.

Copy link
Contributor

@sirpy sirpy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. you should be able to use identity sdk with dev env, it checks liveness but shouldnt check for duplicates.
  2. in order to show it is working you need to create a demo mini app and then access this app via farcaster web and also via farcaster mobile. you can follow their developer guides how to test apps.

params[popupMode ? "cbu" : "rdu"] = callbackUrl
const isInFarcaster = await isInFarcasterMiniApp();

if (isInFarcaster && this.farcasterConfig) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

identity sdk should not hold farcasterConfig
this whole logic should be extracted to a helper method

See this psuedo code
farcasterCallback = await identitysdk.generateFarcasterCallbac(farcasterconfig) identitysdk.navigateTofaceVerification(farcastercallback)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So requested changes @peterje-tr:

  1. farcaster handling should be done in their own class helpers.
  2. farcasterconfig is managed on the client-side, and passed down to relevant methods on a per-needed basis.
  3. navigateToFaceverification should be an optional helper, not enforced in flows

* @returns The resolved callback URL.
*/
async resolveCallbackUrl(
baseUrl: string,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see my previous comment


export const useClaimSDK = (
env: contractEnv = "production",
farcasterConfig?: FarcasterAppConfig,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that shouldnt be here


export const useIdentitySDK = (
env: contractEnv = "production",
farcasterConfig?: FarcasterAppConfig,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be removed

@sirpy sirpy requested a review from L03TJ3 February 16, 2026 12:03
- Removed `farcasterConfig` from `IdentitySDK` options and class state
- Removed `farcasterConfig` param from `useIdentitySDK` and `useClaimSDK` hooks
- Reverted `generateFVLink` back to using `callbackUrl` directly without internal interception
- Removed `resolveCallbackUrl` helper method
- Reverted `ClaimSDK.fvRedirect()` to use `window.location.href`
- Added explicit `generateFarcasterCallback` method to `IdentitySDK`
- Updated `VerifyButton` to handle Farcaster context explicitly and generate callback URL locally
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Farcaster support for the identity flow

3 participants