feat(chat): Validate and cache remote user public keys for encryption

- Add validation to check if cached users have valid public keys (PEM format)
- Fetch remote public keys when local cache has placeholder/invalid keys
- Update existing cached users with real public keys instead of creating duplicates
- Include public key in user profile API response for E2E encrypted chat support
- Improve error handling to reject recipients without valid encryption keys
- Ensure remote user data is properly extracted from API responses
This prevents sending encrypted messages to users with placeholder keys and ensures the chat system always has access to valid public keys for encryption operations.
This commit is contained in:
Christomatt
2026-01-27 02:12:43 +01:00
parent 998a1bcf4a
commit fb2c80b0e3
2 changed files with 32 additions and 14 deletions
+31 -14
View File
@@ -82,8 +82,11 @@ export async function POST(request: NextRequest) {
where: eq(users.handle, recipientHandle), where: eq(users.handle, recipientHandle),
}); });
if (!recipientUser) { // Check if we have a valid public key (not a placeholder)
// Fetch from remote node const hasValidPublicKey = recipientUser?.publicKey?.startsWith('-----BEGIN');
if (!recipientUser || !hasValidPublicKey) {
// Fetch from remote node to get the real public key
try { try {
console.log('[Chat Send] Fetching remote user from node:', domain); console.log('[Chat Send] Fetching remote user from node:', domain);
const protocol = domain.includes('localhost') ? 'http' : 'https'; const protocol = domain.includes('localhost') ? 'http' : 'https';
@@ -95,25 +98,39 @@ export async function POST(request: NextRequest) {
} }
const remoteUserData = await response.json(); const remoteUserData = await response.json();
recipientPublicKey = remoteUserData.publicKey; const userData = remoteUserData.user || remoteUserData;
recipientPublicKey = userData.publicKey;
if (!recipientPublicKey || !recipientPublicKey.startsWith('-----BEGIN')) {
console.error('[Chat Send] Remote user has no valid public key');
return NextResponse.json({ error: 'Recipient does not support encrypted chat' }, { status: 400 });
}
// Cache the remote user if (recipientUser) {
const [newUser] = await db.insert(users).values({ // Update existing cached user with real public key
did: remoteUserData.did || `did:swarm:${domain}:${handle}`, await db.update(users)
handle: recipientHandle, .set({ publicKey: recipientPublicKey })
displayName: remoteUserData.displayName, .where(eq(users.id, recipientUser.id));
avatarUrl: remoteUserData.avatarUrl, console.log('[Chat Send] Updated cached user with real public key');
publicKey: recipientPublicKey, } else {
}).returning(); // Cache the remote user
recipientUser = newUser; const [newUser] = await db.insert(users).values({
console.log('[Chat Send] Remote user cached'); did: userData.did || `did:swarm:${domain}:${handle}`,
handle: recipientHandle,
displayName: userData.displayName,
avatarUrl: userData.avatarUrl,
publicKey: recipientPublicKey,
}).returning();
recipientUser = newUser;
console.log('[Chat Send] Remote user cached');
}
} catch (error) { } catch (error) {
console.error('[Chat Send] Failed to fetch remote user:', error); console.error('[Chat Send] Failed to fetch remote user:', error);
return NextResponse.json({ error: 'Failed to reach recipient node' }, { status: 503 }); return NextResponse.json({ error: 'Failed to reach recipient node' }, { status: 503 });
} }
} else { } else {
recipientPublicKey = recipientUser.publicKey; recipientPublicKey = recipientUser.publicKey;
console.log('[Chat Send] Remote user found in cache'); console.log('[Chat Send] Remote user found in cache with valid key');
} }
} else { } else {
// Local user // Local user
+1
View File
@@ -94,6 +94,7 @@ export async function GET(request: Request, context: RouteContext) {
website: user.website, website: user.website,
movedTo: user.movedTo, movedTo: user.movedTo,
isBot: user.isBot, isBot: user.isBot,
publicKey: user.publicKey, // Needed for E2E encrypted chat
}; };
// If this is a bot, include owner info // If this is a bot, include owner info