session upgrades work

This commit is contained in:
counterweight 2025-02-13 02:20:07 +01:00
parent bee8218e40
commit 74019e97a6
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C
7 changed files with 146 additions and 40 deletions

View file

@ -1,10 +1,3 @@
class ChallengedUsedError extends Error {
constructor(message) {
super(message);
this.name = "ChallengeUsedError";
}
}
class AlreadyUsedError extends Error { class AlreadyUsedError extends Error {
constructor(message) { constructor(message) {
super(message); super(message);
@ -19,13 +12,6 @@ class InvalidSignatureError extends Error {
} }
} }
class AppInvitedUsedError extends Error {
constructor(message) {
super(message);
this.name = "AppInvitedUsedError";
}
}
class NotFoundError extends Error { class NotFoundError extends Error {
constructor(message) { constructor(message) {
super(message); super(message);
@ -33,8 +19,16 @@ class NotFoundError extends Error {
} }
} }
class ExpiredError extends Error {
constructor(message) {
super(message);
this.name = 'ExpiredError';
}
}
module.exports = { module.exports = {
AlreadyUsedError, AlreadyUsedError,
InvalidSignatureError, InvalidSignatureError,
NotFoundError NotFoundError,
ExpiredError
}; };

View file

@ -11,16 +11,24 @@ async function setAndPersistNewSession(res) {
async function createSessionMiddleware(req, res, next) { async function createSessionMiddleware(req, res, next) {
if (!req.cookies.sessionUuid) { const sessionUuid = req.cookies.sessionUuid;
console.log("Running cookie middleware")
if (!sessionUuid) {
console.log("Found no cookie")
await setAndPersistNewSession(res); await setAndPersistNewSession(res);
} }
if (req.cookies.sessionUuid) { if (sessionUuid) {
if (await sessionService.isSessionExpired(req.cookies.sessionUuid)) { console.log(`Found a cookie ${sessionUuid}`)
if (!(await sessionService.isSessionValid(sessionUuid))) {
console.log("But it's not valid")
await setAndPersistNewSession(res); await setAndPersistNewSession(res);
} }
} }
console.log("Moving on...")
next(); next();
} }

View file

@ -0,0 +1,27 @@
const { DataTypes } = require('sequelize');
const sequelize = require('../database');
const SessionRelatedToPublickey = sequelize.define('SessionRelatedToPublickey', {
uuid: {
type: DataTypes.UUID,
allowNull: false,
unique: true,
primaryKey: true
},
session_uuid: {
type: DataTypes.UUID,
allowNull: false,
},
public_key: {
type: DataTypes.STRING,
allowNull: false
},
created_at: {
type: DataTypes.DATE,
allowNull: false
}
}, {
tableName: 'session_related_to_public_key'
});
module.exports = SessionRelatedToPublickey;

View file

@ -2,6 +2,7 @@ const express = require('express');
const invitesService = require('../services/invitesService'); const invitesService = require('../services/invitesService');
const nostrService = require('../services/nostrService'); const nostrService = require('../services/nostrService');
const sessionService = require('../services/sessionService');
const { TimeoutError } = require('sequelize'); const { TimeoutError } = require('sequelize');
const errors = require('../errors'); const errors = require('../errors');
@ -55,22 +56,37 @@ router.get('/signup/nostr-challenge', async (req, res) => {
router.post("/signup/nostr-verify", async (req, res) => { router.post("/signup/nostr-verify", async (req, res) => {
const signedEvent = req.body; const signedEvent = req.body;
const sessionUuid = req.cookies.sessionUuid;
let completedSignUpChallenge;
try { try {
console.log(`Starting nostr-verify with event: ${signedEvent}`); completedSignUpChallenge = await invitesService.verifySignUpChallenge(signedEvent);
const completedSignUpChallenge = await invitesService.verifySignUpChallenge(signedEvent);
console.log(`Finished nostr-verify`);
} catch (error) { } catch (error) {
if (error instanceof TimeoutError) { if (error instanceof errors.ExpiredError) {
console.error('The challenge is outdated.'); return res.status(410).json({
success: false,
message: 'The challenge has expired, request a new one.'
})
} }
if (error instanceof errors.AlreadyUsedError) { if (error instanceof errors.AlreadyUsedError) {
console.error('The challenge was already used, request a new one.'); return res.status(410).json({
success: false,
message: 'The challenge has been used, request a new one.'
})
} }
if (error instanceof errors.InvalidSignatureError) { if (error instanceof errors.InvalidSignatureError) {
console.error('Signature is not valid.') return res.status(400).json({
success: false,
message: 'The challenge signature is not valid.'
})
} }
} }
await sessionService.relateSessionToPublicKey(
sessionUuid,
completedSignUpChallenge.public_key
)
return res.status(200).json({ success: true }); return res.status(200).json({ success: true });
}); });

View file

@ -32,6 +32,30 @@ router.get('/invite/:inviteUuid', async (req, res) => {
return res.render('invite', { invite }); return res.render('invite', { invite });
}); });
router.get('/invite/:inviteUuid', async (req, res) => {
const { inviteUuid } = req.params;
res.cookie('inviteUuid', inviteUuid, { httpOnly: true, maxAge: 86400000 });
let invite;
try {
invite = await invitesService.getAppInvite(inviteUuid);
if (!invite) {
return res.status(404).render('error', { message: 'Invite not found.' });
}
if (await invitesService.isAppInviteSpent(inviteUuid)) {
return res.status(410).render('invite_spent', { invite })
}
} catch (error) {
console.error('Error fetching invite:', error);
return res.status(500).render('error', { message: 'An error occurred' });
}
return res.render('invite', { invite });
});
router.get('/private', authMiddleware, (req, res) => { router.get('/private', authMiddleware, (req, res) => {
res.render('private', {}); res.render('private', {});
}); });

View file

@ -73,22 +73,17 @@ async function verifySignUpChallenge(signedEvent) {
null, challenge null, challenge
); );
console.log(`Found this nostr challenge: ${nostrChallenge}`);
const signUpChallenge = await SignUpChallengeCreated.findOne({ const signUpChallenge = await SignUpChallengeCreated.findOne({
where: { where: {
nostr_challenge_uuid: nostrChallenge.uuid nostr_challenge_uuid: nostrChallenge.uuid
} }
}) })
console.log(`Found this signup challenge: ${signUpChallenge}`);
if (await nostrService.hasNostrChallengeBeenCompleted(challenge)) { if (await nostrService.hasNostrChallengeBeenCompleted(challenge)) {
throw new errors.AlreadyUsedError("This challenge has already been used."); throw new errors.AlreadyUsedError("This challenge has already been used.");
} }
console.log(`I'm gonna verify the nostr challenge`);
const completedNostrChallenge = await nostrService.verifyNostrChallenge(signedEvent); const completedNostrChallenge = await nostrService.verifyNostrChallenge(signedEvent);
console.log(`Verified the NostrChallenge: ${completedNostrChallenge}`);
const completedSignUpChallenge = await SignUpChallengeCompleted.create( const completedSignUpChallenge = await SignUpChallengeCompleted.create(
{ {
@ -99,17 +94,33 @@ async function verifySignUpChallenge(signedEvent) {
created_at: new Date().toISOString() created_at: new Date().toISOString()
} }
); );
console.log(`Verified the SignUpChallenge: ${completedSignUpChallenge}`);
return completedSignUpChallenge; return completedSignUpChallenge;
} }
async function isPublicKeySignedUp(publicKey) {
const signUpChallengeCompleted = await SignUpChallengeCompleted.findOne(
{
where: {
public_key: publicKey
}
}
)
if (signUpChallengeCompleted) {
return true;
}
return false;
}
module.exports = { module.exports = {
appInviteExists, appInviteExists,
getAppInvite, getAppInvite,
isAppInviteSpent, isAppInviteSpent,
createAppInvite, createAppInvite,
createSignUpChallenge, createSignUpChallenge,
verifySignUpChallenge verifySignUpChallenge,
isPublicKeySignedUp
}; };

View file

@ -1,4 +1,9 @@
const uuid = require('uuid');
const SessionCreated = require('../models/SessionCreated'); const SessionCreated = require('../models/SessionCreated');
const SessionRelatedToPublickey = require('../models/SessionRelatedToPublickey');
const invitesService = require('./invitesService');
const constants = require('../constants'); const constants = require('../constants');
@ -14,7 +19,7 @@ async function createSession(sessionUuid) {
}); });
} }
async function isSessionExpired(sessionUuid) { async function isSessionValid(sessionUuid) {
const currentSession = await SessionCreated.findOne({ const currentSession = await SessionCreated.findOne({
where: { where: {
'uuid': sessionUuid 'uuid': sessionUuid
@ -22,15 +27,36 @@ async function isSessionExpired(sessionUuid) {
}); });
if (!currentSession) { if (!currentSession) {
return true;
}
if (currentSession.expires_at <= new Date()) {
return true;
}
return false; return false;
} }
exports.createSession = createSession; if (currentSession.expires_at <= new Date()) {
exports.isSessionExpired = isSessionExpired; return false;
}
return true;
}
async function relateSessionToPublicKey(sessionUuid, publicKey) {
if (!(await isSessionValid(sessionUuid))) {
throw Error("Session is not valid anymore.");
}
if (!(await invitesService.isPublicKeySignedUp(publicKey))) {
throw Error("Public key is not signed up.");
}
return SessionRelatedToPublickey.create({
'uuid': uuid.v7(),
session_uuid: sessionUuid,
public_key: publicKey,
created_at: new Date().toISOString()
});
}
module.exports = {
createSession,
isSessionValid,
relateSessionToPublicKey
}