Skip to content

[TASK-8116] feat: persist webauthn data#623

Merged
jjramirezn merged 2 commits intopeanut-walletfrom
feat/persist-webauthn-data
Jan 14, 2025
Merged

[TASK-8116] feat: persist webauthn data#623
jjramirezn merged 2 commits intopeanut-walletfrom
feat/persist-webauthn-data

Conversation

@jjramirezn
Copy link
Copy Markdown
Contributor

@jjramirezn jjramirezn commented Jan 13, 2025

After register o login, persist the webauthn data in local storage so it can be used to create the passkey validator and kernel client without needing to ask for passkey again (except for operations of course).

Summary by CodeRabbit

  • Refactor

    • Improved local storage handling for complex data types
    • Streamlined account registration and login processes
    • Enhanced WebAuthn key management in wallet context
  • Bug Fixes

    • Added more robust error handling during registration and login
    • Improved serialization and deserialization of local storage data
  • Chores

    • Removed deprecated passkey storage helpers
    • Updated component logic to use a more reactive hooks approach

After register o login, persist the webauthn data in local storage so it
can be used to create the passkey validator and kernel client without
needing to ask for passkey again (except for operations of course).
@vercel
Copy link
Copy Markdown

vercel bot commented Jan 13, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
peanut-ui ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 13, 2025 5:51pm

@notion-workspace
Copy link
Copy Markdown

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 13, 2025

Walkthrough

The pull request introduces significant changes to the passkey and wallet management system. The modifications focus on improving the handling of WebAuthn keys, local storage operations, and the setup process for passkeys. The changes involve refactoring the context management, enhancing local storage utility functions, and streamlining the component logic for passkey registration and account setup.

Changes

File Change Summary
src/components/Setup/Setup.helpers.ts Entire file deleted; removed PasskeyStorage class, useLocalPasskeys function, and related interfaces
src/components/Setup/Views/SetupPasskey.tsx Refactored component logic using hooks, added address and user variables, modified error handling for registration
src/context/walletContext/zeroDevContext.context.tsx Added useEffect hooks for webAuthnKey management, updated handleRegister and handleLogin methods, introduced new WebAuthnKey type
src/utils/general.utils.ts Enhanced local storage functions to handle bigint serialization and deserialization, improved error handling

Sequence Diagram

sequenceDiagram
    participant User
    participant SetupPasskey
    participant ZeroDevContext
    participant LocalStorage

    User->>SetupPasskey: Initiate Registration
    SetupPasskey->>ZeroDevContext: handleRegister()
    ZeroDevContext->>LocalStorage: Save WebAuthnKey
    ZeroDevContext-->>SetupPasskey: Registration Complete
    SetupPasskey->>ZeroDevContext: Add Account
Loading

Poem

🐰 A Passkey's Tale of Digital Grace 🔑

In bytes and bits, a rabbit's embrace,
WebAuthn dances with storage's trace,
Hooks weave magic, context takes flight,
Serializing bigints with delight,
A digital journey of secure space! 🚀


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (4)
src/utils/general.utils.ts (2)

87-98: Enhance bigint serialization with type safety.

The implementation correctly handles bigint serialization, but it could benefit from type safety improvements.

Consider this type-safe implementation:

 if (typeof localStorage === 'undefined') return
 try {
     // Convert the data to a string before storing it in localStorage
-    const serializedData = JSON.stringify(data, (_key, value) => {
-        if ('bigint' === typeof value) {
-            return {
-                '@type': 'BigInt',
-                value: value.toString(),
-            }
-        }
-        return value
-    })
+    const serializedData = JSON.stringify(data, (_key, value) => {
+        if (typeof value === 'bigint') {
+            return {
+                '@type': 'BigInt' as const,
+                value: value.toString(),
+            }
+        }
+        return value
+    })
     localStorage.setItem(key, serializedData)
     console.log(`Saved ${key} to localStorage:`, data)

107-119: Improve bigint deserialization with type guards.

The implementation correctly handles bigint deserialization, but it could benefit from type safety improvements.

Consider this type-safe implementation:

 if (typeof localStorage === 'undefined') return
 try {
     const data = localStorage.getItem(key)
     if (data === null) {
         console.log(`No data found in localStorage for ${key}`)
         return null
     }
-    const parsedData = JSON.parse(data, (_key, value) => {
-        if (value && typeof value === 'object' && value['@type'] === 'BigInt') {
-            return BigInt(value.value)
-        }
-        return value
-    })
+    const parsedData = JSON.parse(data, (_key, value) => {
+        if (value && 
+            typeof value === 'object' && 
+            '@type' in value && 
+            value['@type'] === 'BigInt' &&
+            'value' in value && 
+            typeof value.value === 'string') {
+            return BigInt(value.value)
+        }
+        return value
+    })
     console.log(`Retrieved ${key} from localStorage:`, parsedData)
     return parsedData
src/components/Setup/Views/SetupPasskey.tsx (2)

31-43: Enhance error handling specificity

While error handling is implemented, the error message is generic and doesn't provide specific feedback to users about what went wrong during passkey registration.

Consider this enhancement:

    onClick={async () => {
        try {
            await handleRegister(handle)
        } catch (e) {
            console.error('Error registering passkey', e)
-           setError('Error registering passkey')
+           setError(
+               e instanceof Error
+                   ? e.message
+                   : 'Failed to register passkey. Please try again.'
+           )
        }
    }}

46-46: Enhance error message accessibility

The error message should be more accessible to screen readers.

Consider this enhancement:

-   {error && <p className="text-sm font-bold text-error">{error}</p>}
+   {error && (
+       <p 
+           role="alert"
+           aria-live="polite"
+           className="text-sm font-bold text-error"
+       >
+           {error}
+       </p>
+   )}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a99a1f3 and 21106a0.

📒 Files selected for processing (4)
  • src/components/Setup/Setup.helpers.ts (0 hunks)
  • src/components/Setup/Views/SetupPasskey.tsx (1 hunks)
  • src/context/walletContext/zeroDevContext.context.tsx (7 hunks)
  • src/utils/general.utils.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • src/components/Setup/Setup.helpers.ts
🔇 Additional comments (2)
src/utils/general.utils.ts (1)

Line range hint 87-119: Verify WebAuthn data serialization.

The enhanced local storage utilities correctly handle bigint values, which is crucial for storing WebAuthn credentials. Let's verify that all WebAuthn-related data is properly serialized.

✅ Verification successful

WebAuthn data serialization implementation verified successfully

The enhanced localStorage utilities are properly integrated with the WebAuthn implementation in zeroDevContext.context.tsx, correctly handling WebAuthn credentials during both registration and login flows. The bigint serialization enhancement is actively used and necessary for storing WebAuthn keys.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for WebAuthn-related data structures and their usage of bigint values

# Test: Search for WebAuthn credential structures
rg -A 5 "interface.*Credential|type.*Credential"

# Test: Search for WebAuthn-related local storage operations
rg -A 5 "saveToLocalStorage.*[Cc]redential|getFromLocalStorage.*[Cc]redential"

Length of output: 131


Script:

#!/bin/bash
# Search for WebAuthn-related files and code
rg -i "webauthn" --type ts

# Search for actual usage of the enhanced localStorage utilities
rg -A 5 "saveToLocalStorage\(|getFromLocalStorage\(" --type ts

Length of output: 3490

src/components/Setup/Views/SetupPasskey.tsx (1)

7-7: Verify WebAuthn data persistence implementation

While the component now handles address and user state, I don't see explicit persistence of WebAuthn data to local storage as mentioned in the PR objectives. This might affect the requirement of not asking users to input their passkey again.

Let's verify if the persistence is handled elsewhere:

Also applies to: 12-13

✅ Verification successful

WebAuthn persistence implementation verified

The WebAuthn data persistence is properly implemented through the zeroDevContext, which handles storage via LOCAL_STORAGE_WEB_AUTHN_KEY. The SetupPasskey component correctly integrates with this context through the useZeroDev hook, ensuring proper persistence of passkey data.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for WebAuthn/passkey related storage operations
rg -i "localStorage.*(webauthn|passkey)" 

# Search for potential storage utility functions
rg -i "storage.*webauthn|passkey.*storage" 

# Look for any WebAuthn related classes/interfaces
ast-grep --pattern 'interface $INTERFACE {
  $$$
  webauthn
  $$$
}'

Length of output: 662


Script:

#!/bin/bash
# Check for zeroDevContext usage in SetupPasskey
rg -l "useZeroDev|zeroDevContext" src/components/Setup/Views/SetupPasskey.tsx

# Find saveToLocalStorage implementation
ast-grep --pattern 'function saveToLocalStorage($_,$_) {
  $$$
}'

# Look for LOCAL_STORAGE_WEB_AUTHN_KEY definition
rg "LOCAL_STORAGE_WEB_AUTHN_KEY.*=" -A 1

# Get more context around the storage operations
rg -B 2 -A 2 "saveToLocalStorage.*WEB_AUTHN_KEY"

Length of output: 1358


Script:

#!/bin/bash
# Get context usage in SetupPasskey
rg -A 5 -B 5 "useZeroDev|zeroDevContext" src/components/Setup/Views/SetupPasskey.tsx

# Look for any webauthn/passkey related state or effects
rg -A 3 "useState|useEffect" src/components/Setup/Views/SetupPasskey.tsx

# Check for any registration or setup handlers
ast-grep --pattern 'const $HANDLER = async () => {
  $$$
}'  src/components/Setup/Views/SetupPasskey.tsx

Length of output: 1265

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/components/Setup/Views/SetupPasskey.tsx (2)

16-31: Consider simplifying the effect implementation.

The current implementation could be improved for better maintainability and type safety:

    useEffect(() => {
        if (address && user) {
-           addAccount({
-               accountIdentifier: address,
-               accountType: WalletProviderType.PEANUT,
-               userId: user?.user.userId as string,
-           })
-               .then(() => {
-                   handleNext()
-               })
-               .catch((e) => {
-                   console.error('Error adding account', e)
-                   setError('Error adding account')
-               })
+           const setupAccount = async () => {
+               try {
+                   if (!user.user.userId) throw new Error('User ID is required')
+                   await addAccount({
+                       accountIdentifier: address,
+                       accountType: WalletProviderType.PEANUT,
+                       userId: user.user.userId,
+                   })
+                   handleNext()
+               } catch (e) {
+                   console.error('Error adding account', e)
+                   setError('Error adding account')
+               }
+           }
+           setupAccount()
        }
    }, [address, addAccount, user, handleNext])

35-47: Consider using more semantic class names.

The button implementation looks good with proper error handling and loading state. However, consider using a more semantic class name instead of text-nowrap.

-                className="text-nowrap"
+                className="whitespace-nowrap"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21106a0 and 7245fd9.

📒 Files selected for processing (1)
  • src/components/Setup/Views/SetupPasskey.tsx (1 hunks)
🔇 Additional comments (3)
src/components/Setup/Views/SetupPasskey.tsx (3)

7-7: LGTM! Clean hook implementation.

The added imports and hook usage follow React best practices, with clear destructuring of required dependencies.

Also applies to: 12-13


16-31: Error handling implementation looks good!

The error handling follows the previously suggested pattern, properly catching and displaying errors to users.


37-43: Verify webauthn data persistence.

The registration handler is implemented, but let's verify that the webauthn data is properly persisted in local storage after registration.

✅ Verification successful

WebAuthn data persistence is properly implemented.

The implementation correctly persists WebAuthn data in localStorage after successful passkey registration, using ZeroDev's passkey validator. The data is stored with a dedicated key and is properly reused during subsequent authentications.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for local storage persistence of webauthn data
rg -A 5 'localStorage.setItem.*webauthn|localStorage.getItem.*webauthn'

Length of output: 73


Script:

#!/bin/bash
# Search for handleRegister implementation and storage patterns
ast-grep --pattern 'const handleRegister = async $_'

# Search for alternative storage patterns
rg -A 5 "setItem|getItem"

# Search for credential storage patterns
rg -A 5 "credential.*store|store.*credential"

Length of output: 14381


Script:

#!/bin/bash
# Search for auth-related files
fd "auth|webauthn|passkey" --type f

# Search for authentication-related patterns
rg -A 5 "webauthn|passkey|authenticator|credential"

# Look for specific setup component implementation
ast-grep --pattern 'function SetupPasskey'

Length of output: 15135

Copy link
Copy Markdown
Contributor

@kushagrasarathe kushagrasarathe left a comment

Choose a reason for hiding this comment

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

@jjramirezn added one nit, rest lgtm 🫡

////// Lifecycle hooks
//
useEffect(() => {
const storedWebAuthnKey = getFromLocalStorage(LOCAL_STORAGE_WEB_AUTHN_KEY)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@jjramirezn we have UserPreferences in utils, which maintains other keys in local-storage and is being commonly used, lets use that here 🫡

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

webauthn key is not an user preference and very particular for the web authentication flow so I'm ok having it as another key


return client
setWebAuthnKey(webAuthnKey)
saveToLocalStorage(LOCAL_STORAGE_WEB_AUTHN_KEY, webAuthnKey)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same updateUserPreferences in utils

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.

2 participants