Skip to content

feat: Y2K theme system, auth security hardening, city passport, and oracle improvements - #6

Merged
melaniesigrid merged 11 commits into
mainfrom
feat/y2k-system-and-bug-fixes
May 30, 2026
Merged

feat: Y2K theme system, auth security hardening, city passport, and oracle improvements#6
melaniesigrid merged 11 commits into
mainfrom
feat/y2k-system-and-bug-fixes

Conversation

@melaniesigrid

Copy link
Copy Markdown
Owner

Summary

  • Security hardening: Migrate auth credentials from AsyncStorage to expo-secure-store (encrypted at rest), add SIWA nonce binding to prevent replay attacks, add timing-safe password comparison via XOR loop, and harden makeSalt() to throw on unavailable CSPRNG
  • Worker refactoring: Restructure Cloudflare Worker into modular routes/ + lib/ with input validation (size caps on city/payload inputs, 256KB data limit)
  • Y2K theme system: Complete Y2K design system with Y2KDecreeCard, Y2KWeatherCard, Y2KOutfitCard, Y2KAvoidSection, font subthemes (Decree/Club), and Y2KOracleScreen thin-router integration
  • City passport: Track cities visited, display arrival/return banners, passport page card with climate personality
  • Oracle accuracy: Rate oracle suggestions, track accuracy over time with useOracleAccuracy
  • Shopping links: Affiliate shopping link service integrated with outfit cards
  • Cloud data sync: Cross-device history/saved/archive sync via cloudData service
  • Test coverage: 187 tests across 21 suites — added SecureStore mock, auth tests, wordUsage, recentCities regression, image generation, outfit selection, shopping links, location time
  • iOS config: Migrated to Swift-only AppDelegate, updated splash screen assets, updated Podfile

Security changes

Change Threat mitigated
SecureStore for auth credentials Unencrypted credential exposure via AsyncStorage
SIWA nonce binding Identity token replay attacks
Apple iat age check (max 10 min) Stale token reuse
timingSafeEqual() for password hashes Timing side-channel attacks
makeSalt() throws on weak PRNG Silent security downgrade
Worker payload size caps DoS via oversized inputs

Test plan

  • Run npm test — 187 tests should pass
  • Run npx tsc --noEmit — no type errors
  • Sign in with Apple on device — nonce flow works, no ERR_REQUEST_CANCELED false errors
  • Create local account — credentials stored in SecureStore, not AsyncStorage
  • Sign out and sign back in — session restored correctly
  • Test wrong password — error shown, no timing leak
  • City passport — visit a city, see arrival card, return to see return banner

🤖 Generated with Claude Code

melaniesigrid and others added 11 commits May 17, 2026 02:20
In React Native/Hermes, 'crypto' is not accessible as a bare identifier
in module scope even on SDK 54. globalThis.crypto is the correct accessor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
crypto.subtle is not available in all Hermes builds even on Expo SDK 54.
Replaced with a self-contained SHA-256 + HMAC-SHA256 + PBKDF2 implementation
that works in any JS environment with no native dependencies.

Changes:
- sha256(), hmacSha256(), hashPassword() — pure JS, synchronous, spec-correct
- 10k iterations (down from 100k) to keep it fast in pure JS (~200-400ms on Hermes)
- makeSalt() tries globalThis.crypto.getRandomValues first, falls back to
  xorshift + Date entropy if not available
- hashPassword() wrapped in Promise so callers stay async-compatible

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ing-safe password comparison

- Migrate all credential storage from AsyncStorage to expo-secure-store (encrypted at rest)
- Add rawNonce generation in AuthScreen.tsx + SHA-256 hash passed to Apple sign-in
- Verify nonce binding server-side in cloudflare-worker/lib/apple.js (prevents SIWA replay attacks)
- Add iat age check (max 10 min) to Apple token verification
- Add timingSafeEqual() XOR loop to prevent timing side-channels on password comparison
- makeSalt() now throws instead of silently falling back to Math.random if CSPRNG unavailable
- Pass nonce through AuthContext.tsx → authApi.ts → cloudflare worker
- Refactor worker to modular routes/ + lib/ structure with route-level input validation
- Cap city/country to 100 chars, negative_prompt to 1000 chars, data payloads to 256KB/64KB
- Clarify EXPO_PUBLIC_FAL_KEY is development-only in .env.example

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ntCities, image gen, outfit selection

- Add expo-secure-store mock with _clear() for test isolation
- Update auth.test.ts to use SecureStore mock (clears state between tests)
- Add wordUsage.test.ts: 8 tests for fashionUsageFor() — accents, hyphens, multi-word French
- Add recentCities.test.ts: 7 regression tests for case-insensitive city dedup fix
- Add fashionCapitals.test.ts, imageGeneration.test.ts, locationTime.test.ts,
  outfitSelection.test.ts, shoppingLinks.test.ts (tests for services added this branch)
- Update oracle.test.ts, oracleProxy.test.ts, theme.test.ts, weatherAlerts.test.ts,
  weatherBadges.test.ts to cover new code paths
- Add SecureStore mock mapping to jest.config.js moduleNameMapper

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… notifications

- Add city passport system (useCityPassport, PassportPageCard, CityArrivalModal, CityReturnBanner)
- Add oracle accuracy tracking (useOracleAccuracy) for rating outfit suggestions
- Add outfit rating prompt component (OutfitRatingPrompt, UnlockToast)
- Add shopping links service (shoppingLinks.ts) for affiliate item search
- Add cloud data sync service (cloudData.ts) for cross-device history/saved/archive
- Add city descriptor service (cityDescriptor.ts) for location-aware editorial copy
- Add notifications hook (useNotifications) for push notification management
- Add outfit selection utility (outfitSelection.ts) and location time helper (locationTime.ts)
- Add SunnyWeatherAnimation component for weather animations
- Add local outfit data (src/data/)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…, and Oracle improvements

- Update OracleScreen, TodayScreen, YouScreen, SettingsScreen with new feature integrations
- Update MapScreen with city passport and city return banner
- Update Y2KOracleScreen, Y2KTodayScreen and Mondrian screens with theme refinements
- Update ProfileEditScreen with cloud data sync
- Update HourlyGraph with improved rendering
- Update WeatherGlanceCard, WeatherAlertBanner with new weather features
- Update OutfitCard, ShareCard, AvoidSection with shopping links and rating support
- Update OracleImage with improved generation flow
- Update useOracle, useOracleImage, useArchive, useConsultStreak, useSavedOutfits,
  useOutfitHistory, useStyleProfile, useWeatherBadges hooks
- Update oracle.ts, weather.ts, imageGeneration.ts services
- Update theme/index.ts with Y2K refinements

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…cies

- Update iOS project config (AppDelegate.swift, Bridging-Header, entitlements, Info.plist)
- Update Podfile and Podfile.lock with latest native dependencies
- Remove legacy AppDelegate.h/mm and noop-file.swift (Swift-only project)
- Add SplashScreenLegacy and SplashScreenBackground.colorset assets
- Update app.json with new build config
- Bump android/gradle.properties
- Update storm/sunny weather glance card animations
- Update package.json and package-lock.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Update TODOS.md with new feature backlog and completed items
- Update ARCHITECTURE.md with cloudflare worker refactoring notes and new services
- Update DESIGN.md with Y2K theme refinements
- Update README.md with setup and feature documentation
- Update BEST_PRACTICES.md with auth security guidelines
- Update AFFILIATE_STRATEGY.md and IMAGE_GENERATION.md
- Add MARKETING.md for launch strategy
- Add .reap/ genome and environment configuration
- Add .github/ CI/CD workflows
- Update CLAUDE.md with skill routing and learned rules
- Update Roadmap.md with completed Y2K milestone
- Update orchestrator.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Client:
- Add expo-auth-session + expo-web-browser + expo-crypto (SDK 54 compatible)
- AuthScreen: Google and Facebook OAuth buttons using useAuthRequest hooks
  (buttons are conditionally rendered — only shown when client IDs are configured)
- auth.ts: add signInWithGoogle / signInWithFacebook — same pattern as SIWA,
  stores googleUserId / facebookUserId in SecureStore
- authApi.ts: add cloudSignInWithGoogle (idToken) / cloudSignInWithFacebook (accessToken)
- AuthContext: expose signInWithGoogle / signInWithFacebook, mirrors SIWA cloud+local flow

Server (Cloudflare Worker):
- lib/google.js: verify Google ID token via tokeninfo endpoint, validate aud + exp
- lib/facebook.js: verify Facebook access token via debug_token endpoint, fetch profile
- routes/auth.js: POST /auth/google and POST /auth/facebook — upsert user, issue session
- schema.sql: add google_sub + facebook_sub columns (apple_sub now nullable)
- migrations/001_social_auth.sql: ALTER TABLE migration for existing databases

Config:
- .env.example: EXPO_PUBLIC_GOOGLE_CLIENT_ID, EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID,
  EXPO_PUBLIC_FACEBOOK_APP_ID
- wrangler.toml: document GOOGLE_CLIENT_ID, FACEBOOK_APP_ID, FACEBOOK_APP_SECRET secrets

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@melaniesigrid
melaniesigrid merged commit e129841 into main May 30, 2026
3 checks passed
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.

1 participant