Skip to content

feat(card): integrate card holder and secrets details#3729

Open
esaugomez31 wants to merge 10 commits intofeat--home-add-card-accountfrom
feat--card-details-integration
Open

feat(card): integrate card holder and secrets details#3729
esaugomez31 wants to merge 10 commits intofeat--home-add-card-accountfrom
feat--card-details-integration

Conversation

@esaugomez31
Copy link
Copy Markdown
Collaborator

@esaugomez31 esaugomez31 commented Mar 21, 2026

Summary

Integrates cardHolder and cardSecretsEncrypted GraphQL queries into the card flow, replacing hardcoded/empty values with real data from the API.

Changes

  • Card holder name: BlinkCard component now resolves the holder name internally via useCardHolder hook when receiving a cardId prop — eliminates duplicate hook calls across screens
  • Card secrets (PAN/CVC): Card details screen fetches and decrypts PAN and CVC using the encrypted secrets endpoint, displaying the full card number and CVV with copy-to-clipboard support
  • Holder name display: Card visual shows the name in uppercase (matching physical card convention), while the info field shows it in original format
  • Route params cleanup: Replaced holderName string param with cardId across all navigation routes — screens no longer pass display data through navigation, they resolve it from the API
  • AES-GCM decryption: Added decryptAesGcm utility to complement the existing encryptAesGcm

Screens updated

Screen Change
Card details Shows full PAN, CVC, and cardholder name from API
Card dashboard Shows holder name on card visual
Card status (approved/ordered) Shows holder name on card visual
Add to mobile wallet Shows holder name on card visual
Order card / Replace card Passes cardId instead of holderName to status screen

Pending

  • Expiry date: Not yet available in the backend API — field remains empty until the endpoint is implemented

Tests

All new hooks, utilities, and screen modifications are covered by tests.

Copy link
Copy Markdown
Collaborator Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@esaugomez31 esaugomez31 changed the title feat(crypto): add decryptAesGcm utility and tests feat(card): integrate card holder and secrets details Mar 21, 2026
@esaugomez31 esaugomez31 marked this pull request as ready for review March 21, 2026 16:45
@esaugomez31 esaugomez31 self-assigned this Mar 21, 2026
@esaugomez31 esaugomez31 requested a review from grimen March 21, 2026 16:46
@esaugomez31
Copy link
Copy Markdown
Collaborator Author

@grimen @blink-claw-bot please Review

Copy link
Copy Markdown
Contributor

@blink-claw-bot blink-claw-bot left a comment

Choose a reason for hiding this comment

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

Code Review: feat(card): integrate card holder and secrets details

Overview

This PR integrates real card holder and encrypted secrets data into the card flow, replacing hardcoded/empty values. The changes are well-structured with comprehensive test coverage and follow good security practices for handling sensitive financial data.

✅ Security Assessment

AES-GCM Decryption Implementation

The new decryptAesGcm utility is correctly implemented:

  • ✅ Proper key derivation from hex
  • ✅ Correct IV handling from base64
  • ✅ Authentication tag extraction (last 16 bytes)
  • ✅ Secure algorithm choice (aes-128-gcm)
  • ✅ Proper buffer handling and UTF-8 conversion

Card Secrets Security Flow

The useCardSecrets hook follows excellent security practices:

  • ✅ RSA-OAEP session key encryption before transmission
  • ✅ Client-side AES-GCM decryption (secrets never transmitted in plaintext)
  • ✅ Biometric authentication gate on card details screen
  • ✅ Error handling doesn't leak sensitive information
  • ✅ No secrets stored in AsyncStorage or persistent state
  • ✅ Secrets are ephemeral (stored only in React state)

Authentication & Access Control

  • ✅ Biometric gate properly implemented for sensitive data access
  • ✅ GraphQL queries properly parameterized (no injection risks)
  • ✅ Session-based encryption prevents replay attacks

📝 Code Quality

Architecture & Design

  • Clean separation: UI components → hooks → GraphQL → crypto utilities
  • Proper abstraction: useCardHolder centralizes holder name resolution
  • Navigation cleanup: Replaced display data passing with API resolution
  • Consistent patterns: Follows existing Apollo/GraphQL patterns

Error Handling

  • ✅ Comprehensive error handling in useCardSecrets
  • ✅ Toast notifications for user feedback
  • ✅ Graceful fallbacks (empty strings when data unavailable)
  • ✅ Loading states properly managed

Test Coverage

Outstanding test coverage across all new functionality:

  • useCardHolder hook: 140 test lines covering all scenarios
  • useCardSecrets hook: 174 test lines with comprehensive mocking
  • ✅ Crypto utilities: 69 additional test lines for decryptAesGcm
  • ✅ Component tests updated for new props/behavior
  • ✅ Screen integration tests updated

🔍 Detailed Review

New Hooks

useCardHolder (app/hooks/use-card-holder.ts):

  • ✅ Proper GraphQL query implementation
  • ✅ Cache-first fetch policy (good for performance)
  • ✅ Conditional execution (skip when no cardId)
  • ✅ Safe string concatenation for fullName

useCardSecrets (app/screens/card-screen/hooks/use-card-secrets.ts):

  • ✅ Excellent async error handling
  • ✅ Proper state management (loading/error/data)
  • ✅ Secure key generation and encryption flow
  • ✅ Clean separation of concerns

UI Components

BlinkCard updates:

  • ✅ Smooth integration of useCardHolder hook
  • ✅ Proper prop handling (cardId vs holderName precedence)
  • ✅ Uppercase formatting for visual consistency

Card Details Screen:

  • ✅ Proper integration of sensitive data display
  • ✅ Copy-to-clipboard functionality with proper error handling
  • ✅ Conditional rendering based on data availability

Navigation Changes

  • Breaking change handled well: Replaced holderName with cardId in navigation params
  • ✅ All affected screens and tests updated consistently
  • ✅ Better architecture: screens resolve data from API instead of passing through navigation

🚨 Minor Issues

Nit: Potential Memory Optimization

In useCardSecrets.ts, the secrets are stored in component state indefinitely. Consider:

// Could add a cleanup mechanism
useEffect(() => {
  return () => {
    // Clear secrets from memory when component unmounts
    setSecrets(undefined)
  }
}, [])

Nit: Error Message Consistency

In useCardSecrets.ts line 73, consider using a consistent error message format:

const message = err instanceof Error ? err.message : "Unknown error occurred"

Nit: GraphQL Generated Files

The app/graphql/generated.ts changes look correct (auto-generated), but ensure the schema changes are intentional and match backend implementation.

🎯 Recommendations

  1. Monitor Performance: With the new useCardHolder hook being called in multiple places, monitor for N+1 query scenarios
  2. Consider Caching: The holder name data is relatively static - consider longer cache TTL if appropriate
  3. Expiry Date: Good that the TODO for expiry date is documented - ensure backend implementation aligns with the UI expectations

🏁 Verdict

This is excellent work with:

  • ✅ Proper security implementation for sensitive financial data
  • ✅ Comprehensive test coverage
  • ✅ Clean architecture and code organization
  • ✅ Consistent error handling
  • ✅ Proper navigation parameter cleanup

The security implementation is particularly noteworthy - the client-side decryption, session key encryption, and biometric authentication create a robust security model for handling card secrets.

Recommendation: APPROVE

The minor nits above are suggestions for future consideration and don't block this PR.

🤖 Review by Claude Sonnet 4 via Blink-Claw-Bot

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.

Utility libraries unit tests should test actual values rather than mocked, looks like too much magic here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done. Rewrote tests using real node:crypto as backend, no more mocked cipher/decipher. Tests now do actual encrypt then decrypt roundtrips verifying real values (simple strings, unicode, card numbers, CVCs, wrong key rejection, tampered data detection).

}
}, [authenticated, card?.id, secrets, fetchSecrets])

const copyAction = useCallback(
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.

Seems very universal, so would make sense to have a dedicated hook useCopyToClipboard.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

useClipboard already exists as the universal clipboard hook. Instead of creating a duplicate hook, simplified copyAction to a plain handleCopy function that calls copyToClipboard directly. ActionField already handles the undefined case internally (disables action when value is null), so no wrapper needed.

Comment thread app/screens/card-screen/card-details-screen.tsx
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.

fix(card): Integrate all card secrets/details

3 participants