Skip to content

Commit f1072bd

Browse files
committed
server handling of account address and signer
1 parent 4901ff4 commit f1072bd

File tree

14 files changed

+112
-18
lines changed

14 files changed

+112
-18
lines changed

apps/connect/src/components/create-space.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export function CreateSpace() {
5454

5555
const message: Messages.RequestConnectCreateSpaceEvent = {
5656
type: 'connect-create-space-event',
57+
accountAddress,
5758
event: spaceEvent,
5859
spaceId: spaceEvent.transaction.id,
5960
keyBox: {

apps/connect/src/hooks/use-spaces.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getAppInfoByIds } from '@/lib/get-app-info-by-ids';
2+
import { Connect } from '@graphprotocol/hypergraph';
23
import { useIdentityToken } from '@privy-io/react-auth';
34
import { useQuery } from '@tanstack/react-query';
45

@@ -22,8 +23,10 @@ export const useSpaces = () => {
2223
queryKey: ['spaces'],
2324
queryFn: async () => {
2425
if (!identityToken) return [];
26+
const accountAddress = Connect.loadAccountAddress(localStorage);
27+
if (!accountAddress) return [];
2528
const response = await fetch(`${import.meta.env.VITE_HYPERGRAPH_SYNC_SERVER_ORIGIN}/connect/spaces`, {
26-
headers: { 'privy-id-token': identityToken },
29+
headers: { 'privy-id-token': identityToken, 'account-address': accountAddress },
2730
});
2831
const data = await response.json();
2932
const appIds = new Set<string>();

apps/connect/src/routes/authenticate.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ function AuthenticateComponent() {
268268
const message: Messages.RequestConnectAddAppIdentityToSpaces = {
269269
type: 'connect-add-app-identity-to-spaces',
270270
appIdentityAddress: appIdentity.address,
271+
accountAddress,
271272
spacesInput,
272273
};
273274

@@ -277,7 +278,6 @@ function AuthenticateComponent() {
277278
{
278279
headers: {
279280
'privy-id-token': identityToken,
280-
'account-address': accountAddress,
281281
'Content-Type': 'application/json',
282282
},
283283
method: 'POST',
@@ -366,6 +366,7 @@ function AuthenticateComponent() {
366366
const message: Messages.RequestConnectCreateAppIdentity = {
367367
appId: state.appInfo.appId,
368368
address: newAppIdentity.address,
369+
accountAddress,
369370
signaturePublicKey: newAppIdentity.signaturePublicKey,
370371
encryptionPublicKey: newAppIdentity.encryptionPublicKey,
371372
ciphertext,
@@ -377,7 +378,6 @@ function AuthenticateComponent() {
377378
const response = await fetch(`${import.meta.env.VITE_HYPERGRAPH_SYNC_SERVER_ORIGIN}/connect/app-identity`, {
378379
headers: {
379380
'privy-id-token': identityToken,
380-
'account-address': accountAddress,
381381
'Content-Type': 'application/json',
382382
},
383383
method: 'POST',

apps/events/src/routes/authenticate-success.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ function RouteComponent() {
4444
setIdentity({
4545
address: parsedAuthParams.appIdentityAddress,
4646
addressPrivateKey: parsedAuthParams.appIdentityAddressPrivateKey,
47+
permissionId: parsedAuthParams.permissionId,
4748
signaturePublicKey: parsedAuthParams.signaturePublicKey,
4849
signaturePrivateKey: parsedAuthParams.signaturePrivateKey,
4950
encryptionPublicKey: parsedAuthParams.encryptionPublicKey,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
Warnings:
3+
4+
- Added the required column `connectSignerAddress` to the `Account` table without a default value. This is not possible if the table is not empty.
5+
6+
*/
7+
-- RedefineTables
8+
PRAGMA defer_foreign_keys=ON;
9+
PRAGMA foreign_keys=OFF;
10+
CREATE TABLE "new_Account" (
11+
"address" TEXT NOT NULL PRIMARY KEY,
12+
"connectAddress" TEXT NOT NULL,
13+
"connectCiphertext" TEXT NOT NULL,
14+
"connectNonce" TEXT NOT NULL,
15+
"connectSignaturePublicKey" TEXT NOT NULL,
16+
"connectEncryptionPublicKey" TEXT NOT NULL,
17+
"connectAccountProof" TEXT NOT NULL,
18+
"connectKeyProof" TEXT NOT NULL,
19+
"connectSignerAddress" TEXT NOT NULL
20+
);
21+
INSERT INTO "new_Account" ("address", "connectAccountProof", "connectAddress", "connectCiphertext", "connectEncryptionPublicKey", "connectKeyProof", "connectNonce", "connectSignaturePublicKey") SELECT "address", "connectAccountProof", "connectAddress", "connectCiphertext", "connectEncryptionPublicKey", "connectKeyProof", "connectNonce", "connectSignaturePublicKey" FROM "Account";
22+
DROP TABLE "Account";
23+
ALTER TABLE "new_Account" RENAME TO "Account";
24+
CREATE UNIQUE INDEX "Account_connectAddress_key" ON "Account"("connectAddress");
25+
PRAGMA foreign_keys=ON;
26+
PRAGMA defer_foreign_keys=OFF;

apps/server/prisma/schema.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ model Account {
109109
connectKeyProof String
110110
infoAuthor Space[]
111111
spaceKeyBoxes SpaceKeyBox[]
112+
connectSignerAddress String
112113
}
113114

114115
model AppIdentity {

apps/server/src/handlers/createIdentity.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { prisma } from '../prisma.js';
22

33
type Params = {
4+
signerAddress: string;
45
accountAddress: string;
56
ciphertext: string;
67
nonce: string;
@@ -11,6 +12,7 @@ type Params = {
1112
};
1213

1314
export const createIdentity = async ({
15+
signerAddress,
1416
accountAddress,
1517
ciphertext,
1618
nonce,
@@ -32,6 +34,7 @@ export const createIdentity = async ({
3234
}
3335
return await prisma.account.create({
3436
data: {
37+
connectSignerAddress: signerAddress,
3538
address: accountAddress,
3639
connectAccountProof: accountProof,
3740
connectKeyProof: keyProof,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { prisma } from '../prisma';
2+
3+
export const isSignerForAccount = async (signerAddress: string, accountAddress: string) => {
4+
const account = await prisma.account.findUnique({
5+
where: {
6+
address: accountAddress,
7+
},
8+
});
9+
if (!account) {
10+
return false;
11+
}
12+
return account.connectSignerAddress === signerAddress;
13+
};

apps/server/src/index.ts

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { getLatestAccountInboxMessages } from './handlers/getLatestAccountInboxM
2323
import { getLatestSpaceInboxMessages } from './handlers/getLatestSpaceInboxMessages.js';
2424
import { getSpace } from './handlers/getSpace.js';
2525
import { getSpaceInbox } from './handlers/getSpaceInbox.js';
26+
import { isSignerForAccount } from './handlers/is-signer-for-account.js';
2627
import { listAccountInboxes } from './handlers/list-account-inboxes.js';
2728
import { listPublicAccountInboxes } from './handlers/list-public-account-inboxes.js';
2829
import { listSpacesByAccount } from './handlers/list-spaces-by-account.js';
@@ -76,7 +77,12 @@ app.get('/connect/spaces', async (req, res) => {
7677
console.log('GET connect/spaces');
7778
try {
7879
const idToken = req.headers['privy-id-token'];
79-
const accountAddress = await getAddressByPrivyToken(Array.isArray(idToken) ? idToken[0] : idToken);
80+
const accountAddress = req.headers['account-address'] as string;
81+
const signerAddress = await getAddressByPrivyToken(idToken);
82+
if (!(await isSignerForAccount(signerAddress, accountAddress))) {
83+
res.status(401).send('Unauthorized');
84+
return;
85+
}
8086
const spaces = await listSpacesByAccount({ accountAddress });
8187
const spaceResults = spaces.map((space) => ({
8288
id: space.id,
@@ -117,8 +123,13 @@ app.post('/connect/spaces', async (req, res) => {
117123
console.log('POST connect/spaces');
118124
try {
119125
const idToken = req.headers['privy-id-token'];
120-
const accountAddress = await getAddressByPrivyToken(Array.isArray(idToken) ? idToken[0] : idToken);
121126
const message = Schema.decodeUnknownSync(Messages.RequestConnectCreateSpaceEvent)(req.body);
127+
const accountAddress = message.accountAddress;
128+
const signerAddress = await getAddressByPrivyToken(idToken);
129+
if (!(await isSignerForAccount(signerAddress, accountAddress))) {
130+
res.status(401).send('Unauthorized');
131+
return;
132+
}
122133
const space = await createSpace({
123134
accountAddress,
124135
event: message.event,
@@ -145,10 +156,15 @@ app.post('/connect/add-app-identity-to-spaces', async (req, res) => {
145156
console.log('POST connect/add-app-identity-to-spaces');
146157
try {
147158
const idToken = req.headers['privy-id-token'];
148-
const accountAddress = await getAddressByPrivyToken(Array.isArray(idToken) ? idToken[0] : idToken);
159+
160+
const signerAddress = await getAddressByPrivyToken(idToken);
149161
const message = Schema.decodeUnknownSync(Messages.RequestConnectAddAppIdentityToSpaces)(req.body);
162+
if (!(await isSignerForAccount(signerAddress, message.accountAddress))) {
163+
res.status(401).send('Unauthorized');
164+
return;
165+
}
150166
const space = await addAppIdentityToSpaces({
151-
accountAddress,
167+
accountAddress: message.accountAddress,
152168
appIdentityAddress: message.appIdentityAddress,
153169
spacesInput: message.spacesInput,
154170
});
@@ -169,10 +185,14 @@ app.post('/connect/identity', async (req, res) => {
169185
console.log('POST connect/identity');
170186
try {
171187
const idToken = req.headers['privy-id-token'];
172-
const signerAddress = await getAddressByPrivyToken(Array.isArray(idToken) ? idToken[0] : idToken);
188+
const signerAddress = await getAddressByPrivyToken(idToken);
173189
const message = Schema.decodeUnknownSync(Messages.RequestConnectCreateIdentity)(req.body);
174190
const accountAddress = message.keyBox.accountAddress;
175191

192+
if (signerAddress !== message.keyBox.signer) {
193+
res.status(401).send('Unauthorized');
194+
return;
195+
}
176196
if (
177197
!Identity.verifyIdentityOwnership(
178198
accountAddress,
@@ -223,17 +243,23 @@ app.post('/connect/identity', async (req, res) => {
223243
}
224244
});
225245

226-
app.post('/connect/identity/encrypted', async (req, res) => {
227-
console.log('POST connect/identity/encrypted');
246+
app.get('/connect/identity/encrypted', async (req, res) => {
247+
console.log('GET connect/identity/encrypted');
228248
try {
229249
const idToken = req.headers['privy-id-token'];
230-
const accountAddress = await getAddressByPrivyToken(Array.isArray(idToken) ? idToken[0] : idToken);
250+
const signerAddress = await getAddressByPrivyToken(idToken);
251+
const accountAddress = req.headers['account-address'] as string;
252+
if (!(await isSignerForAccount(signerAddress, accountAddress))) {
253+
res.status(401).send('Unauthorized');
254+
return;
255+
}
231256
const identity = await getConnectIdentity({ accountAddress });
232257
const outgoingMessage: Messages.ResponseIdentityEncrypted = {
233258
keyBox: {
234259
accountAddress,
235260
ciphertext: identity.ciphertext,
236261
nonce: identity.nonce,
262+
signer: signerAddress,
237263
},
238264
};
239265
res.status(200).send(outgoingMessage);
@@ -253,7 +279,12 @@ app.get('/connect/app-identity/:appId', async (req, res) => {
253279
console.log('GET connect/app-identity/:appId');
254280
try {
255281
const idToken = req.headers['privy-id-token'];
256-
const accountAddress = await getAddressByPrivyToken(Array.isArray(idToken) ? idToken[0] : idToken);
282+
const signerAddress = await getAddressByPrivyToken(idToken);
283+
const accountAddress = req.headers['account-address'] as string;
284+
if (!(await isSignerForAccount(signerAddress, accountAddress))) {
285+
res.status(401).send('Unauthorized');
286+
return;
287+
}
257288
const appId = req.params.appId;
258289
const appIdentity = await findAppIdentity({ accountAddress, appId });
259290
if (!appIdentity) {
@@ -277,8 +308,13 @@ app.post('/connect/app-identity', async (req, res) => {
277308
console.log('POST connect/app-identity');
278309
try {
279310
const idToken = req.headers['privy-id-token'];
280-
const accountAddress = await getAddressByPrivyToken(Array.isArray(idToken) ? idToken[0] : idToken);
311+
const signerAddress = await getAddressByPrivyToken(idToken);
281312
const message = Schema.decodeUnknownSync(Messages.RequestConnectCreateAppIdentity)(req.body);
313+
const accountAddress = message.accountAddress;
314+
if (!(await isSignerForAccount(signerAddress, accountAddress))) {
315+
res.status(401).send('Unauthorized');
316+
return;
317+
}
282318
const sessionToken = bytesToHex(randomBytes(32));
283319
const sessionTokenExpires = new Date(Date.now() + 1000 * 60 * 60 * 24 * 30); // 30 days
284320
const appIdentity = await createAppIdentity({

apps/server/src/utils/get-address-by-privy-token.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import { PrivyClient, type Wallet } from '@privy-io/server-auth';
22

3-
export async function getAddressByPrivyToken(idToken: string | undefined): Promise<string> {
3+
export async function getAddressByPrivyToken(idToken: string[] | string | undefined): Promise<string> {
44
if (!idToken) {
55
throw new Error('No Privy ID token provided');
66
}
77

8+
const idTokenString = Array.isArray(idToken) ? idToken[0] : idToken;
9+
810
if (!process.env.PRIVY_APP_SECRET || !process.env.PRIVY_APP_ID) {
911
throw new Error('Missing Privy configuration');
1012
}
1113

1214
const privy = new PrivyClient(process.env.PRIVY_APP_ID, process.env.PRIVY_APP_SECRET);
13-
const user = await privy.getUser({ idToken });
15+
const user = await privy.getUser({ idToken: idTokenString });
1416

1517
if (!user) {
1618
throw new Error('Invalid Privy user');

0 commit comments

Comments
 (0)