fix(auth): enterprise SSO bcrypt hash + upgrade cost 10→12

- Enterprise SAML/OIDC callbacks used raw 'sso_xxx' string as passwordHash
  which would crash bcrypt.compare(). Now uses userRepo.hashPassword(randomUUID())
- Added updateLastLogin() for existing enterprise SSO users
- Upgraded bcrypt cost factor from 10 to 12 per PRD spec
- All 53 auth tests passing
This commit is contained in:
saravanakumardb1 2026-03-12 15:35:00 -07:00
parent 0c4e53a0ed
commit b0e1a54481
8 changed files with 37 additions and 20 deletions

View File

@ -7,18 +7,23 @@ description: Refresh the Windsurf chat history archive (re-scan all repos, updat
Refreshes the centralized Windsurf chat history archive at `__LOCAL_LLMs/AI_IDE_CHAT_HISTORY/WINDSURF/`.
Auto-discovers new repos, updates symlinks, and re-copies docs + workflows.
## Covered Repos (All 8 workspaces)
## Covered Repos (All 13 workspaces)
| Repo | Product | Workflows | Docs |
| ----------------------------------- | ---------------- | --------- | ---- |
| `learning_voice_ai_agent` | LysnrAI | ✅ | ✅ |
| `learning_multimodal_memory_agents` | MindLyst | ✅ | ✅ |
| `learning_ai_clock` | ChronoMind | ✅ | — |
| `learning_ai_peakpulse` | PeakPulse | ✅ | — |
| `learning_ai_fastgap` | NomGap | ✅ | — |
| `learning_ai_jarvis_jr` | JarvisJr | ✅ | — |
| `learning_ai_common_plat` | Common Platform | ✅ | — |
| `learning_agent_monitoring_fx` | Agent Monitoring | ✅ | — |
| Repo | Product | Workflows | Docs |
| ----------------------------------- | ------------------ | --------- | ---- |
| `learning_ai_common_plat` | Common Platform | ✅ | ✅ |
| `learning_voice_ai_agent` | LysnrAI | ✅ | ✅ |
| `learning_multimodal_memory_agents` | MindLyst | ✅ | ✅ |
| `learning_ai_clock` | ChronoMind | ✅ | — |
| `learning_ai_fastgap` | NomGap | ✅ | — |
| `learning_ai_jarvis_jr` | JarvisJr | ✅ | — |
| `learning_ai_peakpulse` | PeakPulse | ✅ | — |
| `learning_ai_notes` | NoteLett | ✅ | — |
| `learning_ai_flowmonk` | FlowMonk | ✅ | — |
| `learning_ai_trails` | ActionTrail | ✅ | — |
| `learning_ai_smart_auth` | SmartAuth | ✅ | — |
| `learning_ai_auth_app` | ByteLyst Auth | ✅ | — |
| `learning_ai_productivity_web` | Productivity Tools | ✅ | — |
## Steps

View File

@ -30,7 +30,7 @@ echo "✨ All repos pushed!"
## What it does:
1. **Backup** — creates timestamped backup branches, cleans up old ones (7 days), skips duplicates
2. **Push** — pushes `main` to `origin/main` for all 7 repos
2. **Push** — pushes `main` to `origin/main` for all 13 repos
## Repositories:

View File

@ -1,5 +1,5 @@
---
description: Push local main branch to origin for all 7 workspace repos
description: Push local main branch to origin for all 13 workspace repos
---
# Push Repos
@ -18,7 +18,7 @@ done < ~/code/mygh/learning_ai_common_plat/.windsurf/workflows/repos.txt
## What it does:
1. Iterates over all 7 workspace repos
1. Iterates over all 13 workspace repos
2. Runs `git push origin main` in each
3. Fails fast if a repo has diverged from remote (resolve with rebase manually)

View File

@ -1,5 +1,5 @@
---
description: Pull latest from origin main across all 7 workspace repos
description: Pull latest from origin main across all 13 workspace repos
---
# Sync Repos
@ -18,7 +18,7 @@ done < ~/code/mygh/learning_ai_common_plat/.windsurf/workflows/repos.txt
## What it does:
1. Iterates over all 7 workspace repos
1. Iterates over all 13 workspace repos
2. Runs `git pull --ff-only origin main` in each
3. Fails fast if there are local divergent commits (use `git pull --rebase` manually in that case)

View File

@ -11,3 +11,7 @@ learning_ai_jarvis_jr
learning_ai_peakpulse
learning_ai_notes
learning_ai_flowmonk
learning_ai_trails
learning_ai_smart_auth
learning_ai_auth_app
learning_ai_productivity_web

View File

@ -45,6 +45,10 @@ All code across the ByteLyst workspace repos:
- learning_ai_jarvis_jr (JarvisJr)
- learning_ai_peakpulse (PeakPulse)
- learning_ai_notes (NoteLett)
- learning_ai_trails (ActionTrail)
- learning_ai_smart_auth (SmartAuth)
- learning_ai_auth_app (ByteLyst Auth)
- learning_ai_productivity_web (Productivity Tools)
## Domain Context
@ -52,7 +56,7 @@ This is a multi-product ecosystem with shared platform services. Key architectur
- **Platform services** (Fastify 5, TypeScript ESM) provide auth, telemetry, feature flags, etc.
- **Shared packages** (@bytelyst/\*) eliminate duplication across products
- **Product backends** handle domain-specific logic (port 4010-4017)
- **Product backends** handle domain-specific logic (port 4010-4018)
- **Web apps** use Next.js 16 + React 19
- **Mobile apps** use native platforms (SwiftUI, Jetpack Compose, React Native)

View File

@ -199,7 +199,7 @@ export async function enterpriseRoutes(app: FastifyInstance) {
id: `usr_${randomUUID()}`,
productId: idp.productId,
email,
passwordHash: `sso_${randomUUID()}`, // SSO-only — no password login
passwordHash: await userRepo.hashPassword(randomUUID()), // SSO-only — random bcrypt hash
plan: 'free',
role: 'user',
displayName: samlData.attributes?.displayName ?? email.split('@')[0],
@ -211,6 +211,8 @@ export async function enterpriseRoutes(app: FastifyInstance) {
primaryProductId: idp.productId,
memberships: [{ productId: idp.productId, plan: 'free', role: 'user', firstAccessAt: now }],
});
} else {
await userRepo.updateLastLogin(user.id);
}
// Issue tokens
@ -306,7 +308,7 @@ export async function enterpriseRoutes(app: FastifyInstance) {
id: `usr_${randomUUID()}`,
productId: idp.productId,
email,
passwordHash: `sso_${randomUUID()}`,
passwordHash: await userRepo.hashPassword(randomUUID()), // SSO-only — random bcrypt hash
plan: 'free',
role: 'user',
displayName: displayName ?? email.split('@')[0],
@ -318,6 +320,8 @@ export async function enterpriseRoutes(app: FastifyInstance) {
primaryProductId: idp.productId,
memberships: [{ productId: idp.productId, plan: 'free', role: 'user', firstAccessAt: now }],
});
} else {
await userRepo.updateLastLogin(user.id);
}
// Issue tokens

View File

@ -123,7 +123,7 @@ export async function remove(id: string): Promise<boolean> {
}
export async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, 10);
return bcrypt.hash(password, 12);
}
export async function verifyPassword(password: string, hash: string): Promise<boolean> {