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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user