Skip to content

Conversation

@lmcmz
Copy link
Collaborator

@lmcmz lmcmz commented Nov 5, 2025

🔗 Related Issues

Closes #667

Linked automatically from the branch name. If incorrect, edit:

📝 Description

📸 Screenshots/Videos

@lmcmz lmcmz linked an issue Nov 5, 2025 that may be closed by this pull request
@github-actions
Copy link

github-actions bot commented Nov 5, 2025

PR Summary

Created a comprehensive Flow passkey wallet example application demonstrating WebAuthn integration with Flow blockchain. The app includes a complete Next.js setup with authentication, transaction signing, and wallet management using passkeys instead of traditional seed phrases. Features include passkey creation/login, Flow account management, transaction signing with WebAuthn, and a modern UI built with Tamagui components.

Changes

File Summary
.claude/settings.local.json Added new development tools and permissions for Chrome DevTools, Figma integration, and passkey development workflow commands.
.gitignore Excluded /apps/passkeys/.next directory from version control to prevent tracking of Next.js build artifacts.
apps/passkeys/.env.example Created environment variables template for Flow network configuration including testnet endpoints and development settings.
apps/passkeys/.eslintrc.cjs Configured ESLint with TypeScript, React, and Next.js rules with relaxed settings for development ease.
apps/passkeys/.prettierrc Set up consistent code formatting rules with semicolons, single quotes, and 100-character line width.
apps/passkeys/README.md Detailed README covering features, installation, architecture, browser support, security model, and development workflow for the Flow passkey wallet.
apps/passkeys/next-env.d.ts Auto-generated TypeScript definitions for Next.js environment and routing.
apps/passkeys/next.config.js Configured Next.js with React Native Web compatibility, Tamagui transpilation, and webpack aliases for cross-platform component usage.
apps/passkeys/package.json Defined project dependencies including Flow SDK, Tamagui UI, cryptographic libraries, and development scripts for the passkey wallet application.
apps/passkeys/public/favicon.ico Placeholder file for application favicon.
apps/passkeys/public/img/landing.jpg New file for wallet interface preview image.
apps/passkeys/src/components/Passkey/CTASection.tsx Marketing section component with decorative elements and join button for the landing page.
apps/passkeys/src/components/Passkey/FeatureCards.tsx Grid of feature cards highlighting wallet capabilities like crypto trading, token swapping, staking, and payments with custom icons.
apps/passkeys/src/components/Passkey/StickyHeader.tsx Fixed header with Flow logo, branding, and connect wallet button with backdrop blur styling.
apps/passkeys/src/components/Passkey/WalletFooter.tsx Configurable footer component with simple and detailed variants, including links, branding, and legal information.
apps/passkeys/src/components/Passkey/WalletHero.tsx Main landing page hero with Flow branding, compelling headline, and call-to-action buttons for different page variants.
apps/passkeys/src/components/Passkey/WalletPreview.tsx Card component displaying a screenshot of the wallet interface with styling and overlay effects.
apps/passkeys/src/components/Passkey/auth-container.tsx Main authentication UI handling passkey selection, approval/decline actions, and loading states for FCL authentication flow.
apps/passkeys/src/components/Passkey/passkey-select.tsx Custom select component for choosing passkey credentials with visual indicators, address display, and credential summaries.
apps/passkeys/src/components/passkey-login.tsx Login interface for authenticating with existing passkeys, including WebAuthn support detection and error handling.
apps/passkeys/src/components/passkey-setup.tsx Setup interface for creating new passkey credentials with username input and WebAuthn registration flow.
apps/passkeys/src/components/passkey-sign-container.tsx UI for reviewing and signing Flow transactions with passkey credentials, including transaction preview and credential selection.
apps/passkeys/src/components/wallet-dashboard.tsx Complete wallet interface showing account info, balance, transaction sending, and passkey details with Flow integration.
apps/passkeys/src/pages/_app.tsx Application root with Tamagui provider setup, portal provider, and global CSS imports with logger initialization.
apps/passkeys/src/pages/_document.tsx HTML document structure with meta tags, favicon, and basic page setup for the passkey wallet application.
apps/passkeys/src/pages/api/create-address.ts API route for creating new Flow addresses using Lilico's OpenAPI service with public key registration.
apps/passkeys/src/pages/api/find-address.ts API route for finding existing Flow addresses associated with public keys using Flow's key indexer service.
apps/passkeys/src/pages/authn.tsx FCL authentication handler page managing passkey-based wallet connection with credential selection and Flow service integration.
apps/passkeys/src/pages/authz.tsx FCL authorization handler for signing Flow transactions using passkeys with WebAuthn challenge generation and signature creation.
apps/passkeys/src/pages/index.tsx Complete landing page with hero section, feature showcase, authentication modals, and wallet dashboard integration with modern gradient design.

autogenerated by presubmit.ai

@github-actions
Copy link

github-actions bot commented Nov 5, 2025

Dependency Review

The following issues were found:

  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 11 package(s) with unknown licenses.
  • ⚠️ 4 packages with OpenSSF Scorecard issues.

View full job summary

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

🚨 Pull request needs attention.

Review Summary

Commits Considered (5)
  • 4abcf41: feat: fix authn and authz styling

Closes #667

Closes #667

  • 2044b99: feat: update passkey ui style

Closes #667

  • 31229d5: feat: update passkey styles

Closes #667

  • 539c14a: feat: add passkey login page

Closes #667

Files Processed (30)
  • .claude/settings.local.json (1 hunk)
  • .gitignore (1 hunk)
  • apps/passkeys/.env.example (1 hunk)
  • apps/passkeys/.eslintrc.cjs (1 hunk)
  • apps/passkeys/.prettierrc (1 hunk)
  • apps/passkeys/README.md (1 hunk)
  • apps/passkeys/next-env.d.ts (1 hunk)
  • apps/passkeys/next.config.js (1 hunk)
  • apps/passkeys/package.json (1 hunk)
  • apps/passkeys/public/favicon.ico (1 hunk)
  • apps/passkeys/public/img/landing.jpg (0 hunks)
  • apps/passkeys/src/components/Passkey/CTASection.tsx (1 hunk)
  • apps/passkeys/src/components/Passkey/FeatureCards.tsx (1 hunk)
  • apps/passkeys/src/components/Passkey/StickyHeader.tsx (1 hunk)
  • apps/passkeys/src/components/Passkey/WalletFooter.tsx (1 hunk)
  • apps/passkeys/src/components/Passkey/WalletHero.tsx (1 hunk)
  • apps/passkeys/src/components/Passkey/WalletPreview.tsx (1 hunk)
  • apps/passkeys/src/components/Passkey/auth-container.tsx (1 hunk)
  • apps/passkeys/src/components/Passkey/passkey-select.tsx (1 hunk)
  • apps/passkeys/src/components/passkey-login.tsx (1 hunk)
  • apps/passkeys/src/components/passkey-setup.tsx (1 hunk)
  • apps/passkeys/src/components/passkey-sign-container.tsx (1 hunk)
  • apps/passkeys/src/components/wallet-dashboard.tsx (1 hunk)
  • apps/passkeys/src/pages/_app.tsx (1 hunk)
  • apps/passkeys/src/pages/_document.tsx (1 hunk)
  • apps/passkeys/src/pages/api/create-address.ts (1 hunk)
  • apps/passkeys/src/pages/api/find-address.ts (1 hunk)
  • apps/passkeys/src/pages/authn.tsx (1 hunk)
  • apps/passkeys/src/pages/authz.tsx (1 hunk)
  • apps/passkeys/src/pages/index.tsx (1 hunk)
Actionable Comments (3)
  • apps/passkeys/src/pages/api/create-address.ts [32-42]

    security: "Missing environment variable validation"

  • apps/passkeys/src/pages/api/create-address.ts [16-20]

    security: "Missing input validation for public key"

  • apps/passkeys/src/pages/api/find-address.ts [10-14]

    security: "Missing input validation for public key parameter"

Skipped Comments (7)
  • apps/passkeys/.env.example [8-8]

    best practice: "Missing newline at end of file"

  • apps/passkeys/.eslintrc.cjs [35-35]

    best practice: "Missing newline at end of file"

  • apps/passkeys/.prettierrc [14-14]

    best practice: "Missing newline at end of file"

  • apps/passkeys/public/favicon.ico [2-2]

    best practice: "Missing newline at end of file"

  • apps/passkeys/src/components/passkey-login.tsx [41-41]

    security: "Potential exposure of sensitive key information"

  • apps/passkeys/src/components/passkey-setup.tsx [47-47]

    security: "Potential exposure of sensitive key information"

  • apps/passkeys/src/pages/index.tsx [347-349]

    possible issue: "Hardcoded newline character in text"

Comment on lines +32 to +42
headers: {
Authorization: process.env.LILICO_API_KEY || '',
'Content-Type': 'application/json',
},
body: JSON.stringify({
publicKey,
weight,
hashAlgorithm,
signatureAlgorithm,
}),
});
Copy link

Choose a reason for hiding this comment

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

The API endpoint uses process.env.LILICO_API_KEY without proper validation. If the environment variable is undefined or empty, it will send an empty Authorization header, which could cause authentication failures or expose the endpoint to unauthorized access.

Comment on lines +16 to +20
} = req.body;

if (!publicKey) {
return res.status(400).json({ error: 'Public key is required' });
}
Copy link

Choose a reason for hiding this comment

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

The API endpoint accepts user input for publicKey without proper validation. This could potentially lead to injection attacks or malformed requests to the external API.

Comment on lines +10 to +14
const { publicKey } = req.query;

if (!publicKey || typeof publicKey !== 'string') {
return res.status(400).json({ error: 'Public key is required' });
}
Copy link

Choose a reason for hiding this comment

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

The API endpoint accepts user input for publicKey from query parameters without proper validation. This could potentially lead to injection attacks or malformed requests to the external API.

);
}

if (!signerAddress) {

Check failure

Code scanning / CodeQL

User-controlled bypass of security check High

This condition guards a sensitive
action
, but a
user-provided value
controls it.
useEffect(() => {
if (typeof window === 'undefined') return;

const handleMessage = (event: MessageEvent) => {

Check warning

Code scanning / CodeQL

Missing origin verification in `postMessage` handler Medium

Postmessage handler has no origin check.

Copilot Autofix

AI about 1 month ago

To fix the problem, we need to ensure that the message handler validates the origin of the incoming message before processing it. Specifically:

  • Only messages sent from trusted origins (e.g., the expected wallet or dApp origin) should be processed; messages from any other origin must be ignored.
  • The safest option is to define a constant or configuration (e.g., ALLOWED_ORIGIN) representing the allowed origin(s), and check event.origin against this before handling any message.
  • Modify the handler function in apps/passkeys/src/pages/authn.tsx (lines 111-143) to insert this origin check immediately at the top of the function.
  • You may add a definition for the allowed origin(s), either as a constant above the useEffect, or inline (if a single origin is expected).

No new imports are required; event.origin is a platform property.


Suggested changeset 1
apps/passkeys/src/pages/authn.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/apps/passkeys/src/pages/authn.tsx b/apps/passkeys/src/pages/authn.tsx
--- a/apps/passkeys/src/pages/authn.tsx
+++ b/apps/passkeys/src/pages/authn.tsx
@@ -105,10 +105,19 @@
   const [appName, setAppName] = useState<string>('Flow dApp');
   const [appUrl, setAppUrl] = useState<string | undefined>();
 
+  // Only accept messages from the allowed origin.
+  // Replace this with the trusted dApp/wallet origin as appropriate.
+  const ALLOWED_ORIGIN = 'https://www.example.com';
+
   useEffect(() => {
     if (typeof window === 'undefined') return;
 
     const handleMessage = (event: MessageEvent) => {
+      if (event.origin !== ALLOWED_ORIGIN) {
+        // Optionally log rejected messages for debugging:
+        logger.warn(`Rejected message from untrusted origin: ${event.origin}`);
+        return;
+      }
       const { data } = event;
       if (!data || typeof data !== 'object') {
         return;
EOF
@@ -105,10 +105,19 @@
const [appName, setAppName] = useState<string>('Flow dApp');
const [appUrl, setAppUrl] = useState<string | undefined>();

// Only accept messages from the allowed origin.
// Replace this with the trusted dApp/wallet origin as appropriate.
const ALLOWED_ORIGIN = 'https://www.example.com';

useEffect(() => {
if (typeof window === 'undefined') return;

const handleMessage = (event: MessageEvent) => {
if (event.origin !== ALLOWED_ORIGIN) {
// Optionally log rejected messages for debugging:
logger.warn(`Rejected message from untrusted origin: ${event.origin}`);
return;
}
const { data } = event;
if (!data || typeof data !== 'object') {
return;
Copilot is powered by AI and may make mistakes. Always verify output.
useEffect(() => {
if (typeof window === 'undefined') return;

const handleMessage = (event: MessageEvent) => {

Check warning

Code scanning / CodeQL

Missing origin verification in `postMessage` handler Medium

Postmessage handler has no origin check.

Copilot Autofix

AI about 1 month ago

To fix this vulnerability, you should verify the origin of incoming messages to ensure they originate from a trusted source before acting on their data payload. This check should be performed in the handleMessage function, where you should compare event.origin to the expected trusted origin (such as the main app domain, e.g., 'https://app.example.com', or using an environment variable for flexibility). Place this check near the top of the function, immediately after extracting event. Only process further if the check passes; otherwise, ignore the message. Since the code does not define a list of trusted origins, and only edits within this file are allowed, define a constant within this file (e.g., const TRUSTED_ORIGIN = ...) above the hook/component, and use that for comparison.

Insert the origin check just after line 89 (const { data } = event;), with comments explaining its purpose. If the origin does not match, immediately return. Place the trusted origin string as an explicit constant, e.g., 'https://app.example.com', or as close as possible to the real domain. If environment variables are used to set the origin, you can use them, but only if their access is shown in the current code snippet.


Suggested changeset 1
apps/passkeys/src/pages/authz.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/apps/passkeys/src/pages/authz.tsx b/apps/passkeys/src/pages/authz.tsx
--- a/apps/passkeys/src/pages/authz.tsx
+++ b/apps/passkeys/src/pages/authz.tsx
@@ -72,6 +72,9 @@
     : 'testnet';
 
 export default function AuthzPage() {
+  // Define the trusted origin for postMessage verification
+  const TRUSTED_ORIGIN = 'https://app.example.com'; // <-- CHANGE to your actual origin
+
   const [signable, setSignable] = useState<FlowSignable | null>(null);
   const [credentials, setCredentials] = useState<CredentialItem[]>([]);
   const [selectedId, setSelectedId] = useState<string | null>(null);
@@ -87,6 +90,11 @@
 
     const handleMessage = (event: MessageEvent) => {
       const { data } = event;
+      // Security: verify event.origin is trusted before handling message
+      if (event.origin !== TRUSTED_ORIGIN) {
+        logger.warn(`Untrusted message origin: ${event.origin}`);
+        return;
+      }
       if (!data || typeof data !== 'object') {
         return;
       }
EOF
@@ -72,6 +72,9 @@
: 'testnet';

export default function AuthzPage() {
// Define the trusted origin for postMessage verification
const TRUSTED_ORIGIN = 'https://app.example.com'; // <-- CHANGE to your actual origin

const [signable, setSignable] = useState<FlowSignable | null>(null);
const [credentials, setCredentials] = useState<CredentialItem[]>([]);
const [selectedId, setSelectedId] = useState<string | null>(null);
@@ -87,6 +90,11 @@

const handleMessage = (event: MessageEvent) => {
const { data } = event;
// Security: verify event.origin is trusted before handling message
if (event.origin !== TRUSTED_ORIGIN) {
logger.warn(`Untrusted message origin: ${event.origin}`);
return;
}
if (!data || typeof data !== 'object') {
return;
}
Copilot is powered by AI and may make mistakes. Always verify output.
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 Passkey examaple

1 participant