feat: create an api for signing events

This commit is contained in:
MTG2000
2022-07-19 18:17:25 +03:00
parent accbf67b1f
commit 9d8f591596
8 changed files with 131 additions and 52 deletions

View File

@@ -1,29 +1,13 @@
const { ApolloServer } = require("apollo-server-lambda");
const schema = require('./schema')
const cookie = require('cookie')
const jose = require('jose');
const { JWT_SECRET } = require("../../utils/consts");
const extractKeyFromCookie = require("../../utils/extractKeyFromCookie");
const extractKey = async (cookieHeader) => {
const cookies = cookie.parse(cookieHeader ?? '');
const token = cookies.Authorization;
if (token) {
try {
const { payload } = await jose.jwtVerify(token, Buffer.from(JWT_SECRET), {
algorithms: ['HS256'],
})
return payload.pubKey
} catch (error) {
return null
}
}
return null;
}
const server = new ApolloServer({
schema,
context: async ({ event }) => {
const userPubKey = await extractKey(event.headers.cookie ?? event.headers.Cookie)
const userPubKey = await extractKeyFromCookie(event.headers.cookie ?? event.headers.Cookie)
return { userPubKey }
},
});

View File

@@ -0,0 +1,74 @@
const serverless = require('serverless-http');
const { createExpressApp } = require('../../modules');
const express = require('express');
const extractKeyFromCookie = require('../../utils/extractKeyFromCookie');
const { getUserByPubKey } = require('../../auth/utils/helperFuncs');
const { generatePrivateKey, getPublicKey, signEvent: signNostrEvent } = require('nostr-tools');
const { prisma } = require('../../prisma');
const signEvent = async (req, res) => {
try {
const userPubKey = await extractKeyFromCookie(req.headers.cookie ?? req.headers.Cookie)
const user = await getUserByPubKey(userPubKey);
if (!user)
return res.status(401).json({ status: 'ERROR', reason: 'Not Authenticated' });
let prvkey = user.nostr_prv_key, pubkey = user.nostr_pub_key;
if (!prvkey) {
prvkey = generatePrivateKey();
pubkey = getPublicKey(prvkey);
await prisma.user.update({
where: {
id: user.id,
},
data: {
nostr_prv_key: prvkey,
nostr_pub_key: pubkey
}
})
}
const { content, tags } = req.body
const event = {
kind: 1,
pubkey,
content,
tags,
created_at: Date.now(),
}
event.sig = await signNostrEvent(event, prvkey)
return res
.status(200)
.json({ event });
} catch (error) {
console.log(error);
res.status(500).send("Unexpected error happened, please try again")
}
}
let app;
if (process.env.LOCAL) {
app = createExpressApp()
app.get('/sign-event', signEvent);
}
else {
const router = express.Router();
router.get('/sign-event', signEvent)
app = createExpressApp(router)
}
const handler = serverless(app);
exports.handler = async (event, context) => {
return await handler(event, context);
};

View File

@@ -0,0 +1,22 @@
const cookie = require('cookie')
const jose = require('jose');
const { JWT_SECRET } = require("./consts");
const extractKeyFromCookie = async (cookieHeader) => {
const cookies = cookie.parse(cookieHeader ?? '');
const token = cookies.Authorization;
if (token) {
try {
const { payload } = await jose.jwtVerify(token, Buffer.from(JWT_SECRET), {
algorithms: ['HS256'],
})
return payload.pubKey
} catch (error) {
return null
}
}
return null;
}
module.exports = extractKeyFromCookie;

View File

@@ -0,0 +1,6 @@
-- AlterTable
ALTER TABLE "Question" ADD COLUMN "is_published" BOOLEAN NOT NULL DEFAULT true;
-- AlterTable
ALTER TABLE "User" ADD COLUMN "nostr_prv_key" TEXT,
ADD COLUMN "nostr_pub_key" TEXT;

View File

@@ -56,7 +56,8 @@ model User {
linkedin String?
bio String?
location String?
nostr_prv_key String?
nostr_pub_key String?
join_date DateTime @default(now())
@@ -134,13 +135,13 @@ model Story {
}
model Question {
id Int @id @default(autoincrement())
title String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
body String
excerpt String
votes_count Int @default(0)
id Int @id @default(autoincrement())
title String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
body String
excerpt String
votes_count Int @default(0)
is_published Boolean @default(true)

View File

@@ -48,3 +48,9 @@ functions:
- http:
path: is-logged-in
method: get
sign-event:
handler: api/functions/sign-event/sign-event.handler
events:
- http:
path: sign-event
method: post

View File

@@ -1,26 +1,3 @@
import { signEvent } from "nostr-tools";
const prvkey = '3e345e14bbf758be3ccdca67cab6c808d0a7f001433cf2492b3c2c93327ebde8';
const pubkey = 'f183af77e76bd62f0af8820ffa6d708d6826cf4a2adaa6be2d49061c94e22c0b';
export async function signEventByServer(_event) {
// Extract user Id from cookie
// Get his prvkey & pubkey from database
// Generate a new prvkey if not exist & store it in the DB
const event = {
..._event,
pubkey,
tags: _event.tags ?? []
};
if (!event.sig)
event.sig = await signEvent(event, prvkey)
return event;
}
export async function mapPubkeysToUsers(pubkeys) {
const users = await Promise.all(pubkeys.map(pubkey =>

View File

@@ -2,7 +2,8 @@ import dayjs from 'dayjs'
import { relayPool } from 'nostr-tools'
import { Nullable } from 'remirror';
import { mapPubkeysToUsers, signEventByServer } from './comment.server';
import { CONSTS } from 'src/utils';
import { mapPubkeysToUsers, } from './comment.server';
const pool = relayPool()
@@ -50,6 +51,14 @@ export function sub(filter: any) {
};
}
const getSignedEvents = async (event: any) => {
const res = await fetch(CONSTS.apiEndpoint + '/sign-event', {
credentials: 'include'
})
const data = await res.json()
return data.event;
}
export async function post(data: string, filter: any) {
@@ -60,7 +69,7 @@ export async function post(data: string, filter: any) {
content: data
};
event = await signEventByServer(event);
event = await getSignedEvents(event);
const publishTimeout = setTimeout(() => {
alert(
`failed to publish event ${event.id?.slice(0, 5)}… to any relay.`