Skip to content

Commit 22dba44

Browse files
committed
fix: use the app identity keys everywhere
1 parent fc20c53 commit 22dba44

File tree

22 files changed

+430
-117
lines changed

22 files changed

+430
-117
lines changed

apps/connect/src/routes/authenticate.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ function AuthenticateComponent() {
278278
id: keyData.id,
279279
ciphertext: Utils.bytesToHex(keyBox.keyBoxCiphertext),
280280
nonce: Utils.bytesToHex(keyBox.keyBoxNonce),
281-
authorPublicKey: appIdentity.encryptionPublicKey,
281+
authorPublicKey: keys.encryptionPublicKey,
282282
accountAddress: accountAddress,
283283
};
284284
});
@@ -405,27 +405,33 @@ function AuthenticateComponent() {
405405
rpcUrl: import.meta.env.VITE_HYPERGRAPH_RPC_URL,
406406
});
407407

408+
const appIdentityKeys = {
409+
encryptionPrivateKey: newAppIdentity.encryptionPrivateKey,
410+
encryptionPublicKey: newAppIdentity.encryptionPublicKey,
411+
signaturePrivateKey: newAppIdentity.signaturePrivateKey,
412+
signaturePublicKey: newAppIdentity.signaturePublicKey,
413+
};
408414
console.log('encrypting app identity');
409415
const { ciphertext, nonce } = await Connect.encryptAppIdentity(
410416
signer,
411417
newAppIdentity.address,
412418
newAppIdentity.addressPrivateKey,
413419
permissionId,
414-
keys,
420+
appIdentityKeys,
415421
);
416422
console.log('proving ownership');
417423
const { accountProof, keyProof } = await Identity.proveIdentityOwnership(
418424
smartAccountClient,
419425
accountAddress,
420-
keys,
426+
appIdentityKeys,
421427
);
422428

423429
const message: Messages.RequestConnectCreateAppIdentity = {
424430
appId: state.appInfo.appId,
425431
address: newAppIdentity.address,
426432
accountAddress,
427-
signaturePublicKey: keys.signaturePublicKey,
428-
encryptionPublicKey: keys.encryptionPublicKey,
433+
signaturePublicKey: newAppIdentity.signaturePublicKey,
434+
encryptionPublicKey: newAppIdentity.encryptionPublicKey,
429435
ciphertext,
430436
nonce,
431437
accountProof,
@@ -449,10 +455,10 @@ function AuthenticateComponent() {
449455
address: newAppIdentity.address,
450456
addressPrivateKey: newAppIdentity.addressPrivateKey,
451457
accountAddress,
452-
encryptionPrivateKey: keys.encryptionPrivateKey,
453-
signaturePrivateKey: keys.signaturePrivateKey,
454-
encryptionPublicKey: keys.encryptionPublicKey,
455-
signaturePublicKey: keys.signaturePublicKey,
458+
encryptionPrivateKey: newAppIdentity.encryptionPrivateKey,
459+
signaturePrivateKey: newAppIdentity.signaturePrivateKey,
460+
encryptionPublicKey: newAppIdentity.encryptionPublicKey,
461+
signaturePublicKey: newAppIdentity.signaturePublicKey,
456462
sessionToken: appIdentityResponse.appIdentity.sessionToken,
457463
sessionTokenExpires: new Date(appIdentityResponse.appIdentity.sessionTokenExpires),
458464
permissionId,

apps/events/src/Boot.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ declare module '@tanstack/react-router' {
1515

1616
export function Boot() {
1717
return (
18-
<HypergraphAppProvider syncServerUri="http://localhost:3030" mapping={mapping}>
18+
<HypergraphAppProvider
19+
syncServerUri="http://localhost:3030"
20+
mapping={mapping}
21+
appId="93bb8907-085a-4a0e-83dd-62b0dc98e793"
22+
>
1923
<RouterProvider router={router} />
2024
</HypergraphAppProvider>
2125
);

apps/events/src/routes/login.lazy.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ function Login() {
1616
storage: localStorage,
1717
connectUrl: 'http://localhost:5180',
1818
successUrl: 'http://localhost:5173/authenticate-success',
19-
appId: '93bb8907-085a-4a0e-83dd-62b0dc98e793',
2019
redirectFn: (url: URL) => {
2120
window.location.href = url.toString();
2221
},

apps/next-example/src/components/providers.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ export default function Providers({ children }: { children: React.ReactNode }) {
77
const storage = typeof window !== 'undefined' ? window.localStorage : (undefined as unknown as Storage);
88

99
return (
10-
<HypergraphAppProvider syncServerUri="http://localhost:3030" mapping={{}}>
10+
<HypergraphAppProvider
11+
syncServerUri="http://localhost:3030"
12+
mapping={{}}
13+
appId="83aa8907-085b-430f-1296-ab87dc98e793"
14+
>
1115
{children}
1216
</HypergraphAppProvider>
1317
);

apps/server/src/handlers/applySpaceEvent.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Messages } from '@graphprotocol/hypergraph';
44
import { Identity, SpaceEvents } from '@graphprotocol/hypergraph';
55

66
import { prisma } from '../prisma.js';
7+
import { getAppOrConnectIdentity } from './getAppOrConnectIdentity.js';
78
import { getConnectIdentity } from './getConnectIdentity.js';
89

910
type Params = {
@@ -40,7 +41,7 @@ export async function applySpaceEvent({ accountAddress, spaceId, event, keyBoxes
4041
orderBy: { counter: 'desc' },
4142
});
4243

43-
const getVerifiedIdentity = (accountAddressToFetch: string) => {
44+
const getVerifiedIdentity = (accountAddressToFetch: string, publicKey: string) => {
4445
console.log('getVerifiedIdentity', accountAddressToFetch, accountAddress);
4546
// applySpaceEvent is only allowed to be called by the account that is applying the event
4647
if (accountAddressToFetch !== accountAddress) {
@@ -49,7 +50,8 @@ export async function applySpaceEvent({ accountAddress, spaceId, event, keyBoxes
4950

5051
return Effect.gen(function* () {
5152
const identity = yield* Effect.tryPromise({
52-
try: () => getConnectIdentity({ accountAddress: accountAddressToFetch }),
53+
try: () =>
54+
getAppOrConnectIdentity({ accountAddress: accountAddressToFetch, signaturePublicKey: publicKey, spaceId }),
5355
catch: () => new Identity.InvalidIdentityError(),
5456
});
5557
return identity;

apps/server/src/handlers/create-space.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { Messages } from '@graphprotocol/hypergraph';
55
import { Identity, SpaceEvents } from '@graphprotocol/hypergraph';
66

77
import { prisma } from '../prisma.js';
8+
import { getAppOrConnectIdentity } from './getAppOrConnectIdentity.js';
89
import { getConnectIdentity } from './getConnectIdentity.js';
910

1011
type Params = {
@@ -26,15 +27,15 @@ export const createSpace = async ({
2627
infoSignatureRecovery,
2728
name,
2829
}: Params) => {
29-
const getVerifiedIdentity = (accountAddressToFetch: string) => {
30+
const getVerifiedIdentity = (accountAddressToFetch: string, publicKey: string) => {
3031
// applySpaceEvent is only allowed to be called by the account that is applying the event
3132
if (accountAddressToFetch !== accountAddress) {
3233
return Effect.fail(new Identity.InvalidIdentityError());
3334
}
3435

3536
return Effect.gen(function* () {
3637
const identity = yield* Effect.tryPromise({
37-
try: () => getConnectIdentity({ accountAddress: accountAddressToFetch }),
38+
try: () => getAppOrConnectIdentity({ accountAddress: accountAddressToFetch, signaturePublicKey: publicKey }),
3839
catch: () => new Identity.InvalidIdentityError(),
3940
});
4041
return identity;

apps/server/src/handlers/getAccountInbox.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export async function getAccountInbox({ accountAddress, inboxId }: { accountAddr
88
id: true,
99
account: {
1010
select: {
11-
id: true,
11+
address: true,
1212
},
1313
},
1414
isPublic: true,
@@ -24,7 +24,7 @@ export async function getAccountInbox({ accountAddress, inboxId }: { accountAddr
2424

2525
return {
2626
inboxId: inbox.id,
27-
accountAddress: inbox.account.id,
27+
accountAddress: inbox.account.address,
2828
isPublic: inbox.isPublic,
2929
authPolicy: inbox.authPolicy as Inboxes.InboxSenderAuthPolicy,
3030
encryptionPublicKey: inbox.encryptionPublicKey,
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { prisma } from '../prisma.js';
2+
3+
type Params =
4+
| {
5+
accountAddress: string;
6+
signaturePublicKey: string;
7+
spaceId?: string;
8+
}
9+
| {
10+
accountAddress: string;
11+
appId: string;
12+
spaceId?: string;
13+
};
14+
15+
export type GetIdentityResult = {
16+
accountAddress: string;
17+
ciphertext: string;
18+
nonce: string;
19+
signaturePublicKey: string;
20+
encryptionPublicKey: string;
21+
accountProof: string;
22+
keyProof: string;
23+
appId: string | null;
24+
};
25+
26+
export const getAppOrConnectIdentity = async (params: Params): Promise<GetIdentityResult> => {
27+
if (!('appId' in params)) {
28+
const where: { address: string; connectSignaturePublicKey?: string } = { address: params.accountAddress };
29+
if ('signaturePublicKey' in params) {
30+
where.connectSignaturePublicKey = params.signaturePublicKey;
31+
}
32+
const account = await prisma.account.findFirst({
33+
where,
34+
});
35+
if (account) {
36+
return {
37+
accountAddress: account.address,
38+
ciphertext: account.connectCiphertext,
39+
nonce: account.connectNonce,
40+
signaturePublicKey: account.connectSignaturePublicKey,
41+
encryptionPublicKey: account.connectEncryptionPublicKey,
42+
accountProof: account.connectAccountProof,
43+
keyProof: account.connectKeyProof,
44+
appId: null,
45+
};
46+
}
47+
}
48+
const appWhere: {
49+
accountAddress: string;
50+
appId?: string;
51+
signaturePublicKey?: string;
52+
spaces?: { some: { id: string } };
53+
} = {
54+
accountAddress: params.accountAddress,
55+
};
56+
if ('signaturePublicKey' in params) {
57+
appWhere.signaturePublicKey = params.signaturePublicKey;
58+
}
59+
if ('appId' in params) {
60+
appWhere.appId = params.appId;
61+
}
62+
if (params.spaceId) {
63+
appWhere.spaces = { some: { id: params.spaceId } };
64+
}
65+
console.log('appWhere', appWhere);
66+
const appIdentity = await prisma.appIdentity.findFirst({
67+
where: appWhere,
68+
});
69+
if (appIdentity) {
70+
return {
71+
accountAddress: appIdentity.accountAddress,
72+
ciphertext: appIdentity.ciphertext,
73+
nonce: appIdentity.nonce,
74+
signaturePublicKey: appIdentity.signaturePublicKey,
75+
encryptionPublicKey: appIdentity.encryptionPublicKey,
76+
accountProof: appIdentity.accountProof,
77+
keyProof: appIdentity.keyProof,
78+
appId: appIdentity.appId,
79+
};
80+
}
81+
throw new Error('Identity not found');
82+
};

apps/server/src/index.ts

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { createUpdate } from './handlers/createUpdate.js';
1717
import { findAppIdentity } from './handlers/find-app-identity.js';
1818
import { getAppIdentityBySessionToken } from './handlers/get-app-identity-by-session-token.js';
1919
import { getAccountInbox } from './handlers/getAccountInbox.js';
20+
import { getAppOrConnectIdentity } from './handlers/getAppOrConnectIdentity.js';
2021
import { type GetIdentityResult, getConnectIdentity } from './handlers/getConnectIdentity.js';
2122
import { getLatestAccountInboxMessages } from './handlers/getLatestAccountInboxMessages.js';
2223
import { getLatestSpaceInboxMessages } from './handlers/getLatestSpaceInboxMessages.js';
@@ -318,6 +319,20 @@ app.post('/connect/app-identity', async (req, res) => {
318319
res.status(401).send('Unauthorized');
319320
return;
320321
}
322+
if (
323+
!Identity.verifyIdentityOwnership(
324+
accountAddress,
325+
message.signaturePublicKey,
326+
message.accountProof,
327+
message.keyProof,
328+
CHAIN,
329+
RPC_URL,
330+
)
331+
) {
332+
console.log('Ownership proof is invalid');
333+
res.status(401).send('Unauthorized');
334+
return;
335+
}
321336
const sessionToken = bytesToHex(randomBytes(32));
322337
const sessionTokenExpires = new Date(Date.now() + 1000 * 60 * 60 * 24 * 30); // 30 days
323338
const appIdentity = await createAppIdentity({
@@ -361,21 +376,47 @@ app.get('/whoami', async (req, res) => {
361376
}
362377
});
363378

379+
app.get('/connect/identity', async (req, res) => {
380+
console.log('GET connect/identity');
381+
const accountAddress = req.query.accountAddress as string;
382+
const identity = await getConnectIdentity({ accountAddress });
383+
if (!identity) {
384+
res.status(404).send('Identity not found');
385+
return;
386+
}
387+
const outgoingMessage: Messages.ResponseIdentity = {
388+
accountAddress,
389+
signaturePublicKey: identity.signaturePublicKey,
390+
encryptionPublicKey: identity.encryptionPublicKey,
391+
accountProof: identity.accountProof,
392+
keyProof: identity.keyProof,
393+
};
394+
res.status(200).send(outgoingMessage);
395+
});
396+
364397
app.get('/identity', async (req, res) => {
365398
console.log('GET identity');
366399
const accountAddress = req.query.accountAddress as string;
400+
const signaturePublicKey = req.query.signaturePublicKey as string;
401+
const appId = req.query.appId as string;
367402
if (!accountAddress) {
368403
res.status(400).send('No accountAddress');
369404
return;
370405
}
406+
if (!signaturePublicKey && !appId) {
407+
res.status(400).send('No signaturePublicKey or appId');
408+
return;
409+
}
371410
try {
372-
const identity = await getConnectIdentity({ accountAddress });
411+
const params = signaturePublicKey ? { accountAddress, signaturePublicKey } : { accountAddress, appId };
412+
const identity = await getAppOrConnectIdentity(params);
373413
const outgoingMessage: Messages.ResponseIdentity = {
374414
accountAddress,
375415
signaturePublicKey: identity.signaturePublicKey,
376416
encryptionPublicKey: identity.encryptionPublicKey,
377417
accountProof: identity.accountProof,
378418
keyProof: identity.keyProof,
419+
appId: identity.appId ?? undefined,
379420
};
380421
res.status(200).send(outgoingMessage);
381422
} catch (error) {
@@ -455,7 +496,10 @@ app.post('/spaces/:spaceId/inboxes/:inboxId/messages', async (req, res) => {
455496
// Check if this public key corresponds to a user's identity
456497
let authorIdentity: GetIdentityResult;
457498
try {
458-
authorIdentity = await getConnectIdentity({ connectSignaturePublicKey: authorPublicKey });
499+
authorIdentity = await getAppOrConnectIdentity({
500+
accountAddress: message.authorAccountAddress,
501+
signaturePublicKey: authorPublicKey,
502+
});
459503
} catch (error) {
460504
res.status(403).send({ error: 'Not authorized to post to this inbox' });
461505
return;
@@ -538,7 +582,10 @@ app.post('/accounts/:accountAddress/inboxes/:inboxId/messages', async (req, res)
538582
// Check if this public key corresponds to a user's identity
539583
let authorIdentity: GetIdentityResult;
540584
try {
541-
authorIdentity = await getConnectIdentity({ connectSignaturePublicKey: authorPublicKey });
585+
authorIdentity = await getAppOrConnectIdentity({
586+
accountAddress: message.authorAccountAddress,
587+
signaturePublicKey: authorPublicKey,
588+
});
542589
} catch (error) {
543590
res.status(403).send({ error: 'Not authorized to post to this inbox' });
544591
return;
@@ -700,19 +747,15 @@ webSocketServer.on('connection', async (webSocket: CustomWebSocket, request: Req
700747
break;
701748
}
702749
case 'create-space-event': {
703-
const getVerifiedIdentity = (accountAddressToFetch: string) => {
704-
console.log(
705-
'TODO getVerifiedIdentity should work for app identities',
706-
accountAddressToFetch,
707-
accountAddress,
708-
);
750+
const getVerifiedIdentity = (accountAddressToFetch: string, publicKey: string) => {
709751
if (accountAddressToFetch !== accountAddress) {
710752
return Effect.fail(new Identity.InvalidIdentityError());
711753
}
712754

713755
return Effect.gen(function* () {
714756
const identity = yield* Effect.tryPromise({
715-
try: () => getConnectIdentity({ accountAddress: accountAddressToFetch }),
757+
try: () =>
758+
getAppOrConnectIdentity({ accountAddress: accountAddressToFetch, signaturePublicKey: publicKey }),
716759
catch: () => new Identity.InvalidIdentityError(),
717760
});
718761
return identity;
@@ -811,7 +854,10 @@ webSocketServer.on('connection', async (webSocket: CustomWebSocket, request: Req
811854
throw new Error('Invalid accountAddress');
812855
}
813856
const signer = Inboxes.recoverAccountInboxCreatorKey(data);
814-
const signerAccount = await getConnectIdentity({ connectSignaturePublicKey: signer });
857+
const signerAccount = await getAppOrConnectIdentity({
858+
accountAddress: data.accountAddress,
859+
signaturePublicKey: signer,
860+
});
815861
if (signerAccount.accountAddress !== accountAddress) {
816862
throw new Error('Invalid signature');
817863
}
@@ -881,7 +927,10 @@ webSocketServer.on('connection', async (webSocket: CustomWebSocket, request: Req
881927
// Check that the update was signed by a valid identity
882928
// belonging to this accountAddress
883929
const signer = Messages.recoverUpdateMessageSigner(data);
884-
const identity = await getConnectIdentity({ connectSignaturePublicKey: signer });
930+
const identity = await getAppOrConnectIdentity({
931+
accountAddress: data.accountAddress,
932+
signaturePublicKey: signer,
933+
});
885934
if (identity.accountAddress !== accountAddress) {
886935
throw new Error('Invalid signature');
887936
}

0 commit comments

Comments
 (0)