mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
Merge branch 'bunker-pubkey' into 'main'
Let bunker_pubkey be different from user pubkey See merge request soapbox-pub/ditto!568
This commit is contained in:
commit
9b473406cf
5 changed files with 61 additions and 21 deletions
|
|
@ -111,7 +111,7 @@ const revokeTokenController: AppController = async (c) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
async function getToken(
|
async function getToken(
|
||||||
{ pubkey, secret, relays = [] }: { pubkey: string; secret?: string; relays?: string[] },
|
{ pubkey: bunkerPubkey, secret, relays = [] }: { pubkey: string; secret?: string; relays?: string[] },
|
||||||
): Promise<`token1${string}`> {
|
): Promise<`token1${string}`> {
|
||||||
const kysely = await Storages.kysely();
|
const kysely = await Storages.kysely();
|
||||||
const { token, hash } = await generateToken();
|
const { token, hash } = await generateToken();
|
||||||
|
|
@ -119,17 +119,19 @@ async function getToken(
|
||||||
const nip46Seckey = generateSecretKey();
|
const nip46Seckey = generateSecretKey();
|
||||||
|
|
||||||
const signer = new NConnectSigner({
|
const signer = new NConnectSigner({
|
||||||
pubkey,
|
pubkey: bunkerPubkey,
|
||||||
signer: new NSecSigner(nip46Seckey),
|
signer: new NSecSigner(nip46Seckey),
|
||||||
relay: await Storages.pubsub(), // TODO: Use the relays from the request.
|
relay: await Storages.pubsub(), // TODO: Use the relays from the request.
|
||||||
timeout: 60_000,
|
timeout: 60_000,
|
||||||
});
|
});
|
||||||
|
|
||||||
await signer.connect(secret);
|
await signer.connect(secret);
|
||||||
|
const userPubkey = await signer.getPublicKey();
|
||||||
|
|
||||||
await kysely.insertInto('auth_tokens').values({
|
await kysely.insertInto('auth_tokens').values({
|
||||||
token_hash: hash,
|
token_hash: hash,
|
||||||
pubkey,
|
pubkey: userPubkey,
|
||||||
|
bunker_pubkey: bunkerPubkey,
|
||||||
nip46_sk_enc: await aesEncrypt(Conf.seckey, nip46Seckey),
|
nip46_sk_enc: await aesEncrypt(Conf.seckey, nip46Seckey),
|
||||||
nip46_relays: relays,
|
nip46_relays: relays,
|
||||||
created_at: new Date(),
|
created_at: new Date(),
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ interface EventStatsRow {
|
||||||
interface AuthTokenRow {
|
interface AuthTokenRow {
|
||||||
token_hash: Uint8Array;
|
token_hash: Uint8Array;
|
||||||
pubkey: string;
|
pubkey: string;
|
||||||
|
bunker_pubkey: string;
|
||||||
nip46_sk_enc: Uint8Array;
|
nip46_sk_enc: Uint8Array;
|
||||||
nip46_relays: string[];
|
nip46_relays: string[];
|
||||||
created_at: Date;
|
created_at: Date;
|
||||||
|
|
|
||||||
22
src/db/migrations/040_add_bunker_pubkey.ts
Normal file
22
src/db/migrations/040_add_bunker_pubkey.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { Kysely } from 'kysely';
|
||||||
|
|
||||||
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
await db.schema
|
||||||
|
.alterTable('auth_tokens')
|
||||||
|
.addColumn('bunker_pubkey', 'char(64)')
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await db.updateTable('auth_tokens').set((eb) => ({ bunker_pubkey: eb.ref('pubkey') })).execute();
|
||||||
|
|
||||||
|
await db.schema
|
||||||
|
.alterTable('auth_tokens')
|
||||||
|
.alterColumn('bunker_pubkey', (col) => col.setNotNull())
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(db: Kysely<any>): Promise<void> {
|
||||||
|
await db.schema
|
||||||
|
.alterTable('auth_tokens')
|
||||||
|
.dropColumn('bunker_pubkey')
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
@ -26,15 +26,23 @@ export const signerMiddleware: AppMiddleware = async (c, next) => {
|
||||||
const kysely = await Storages.kysely();
|
const kysely = await Storages.kysely();
|
||||||
const tokenHash = await getTokenHash(bech32 as `token1${string}`);
|
const tokenHash = await getTokenHash(bech32 as `token1${string}`);
|
||||||
|
|
||||||
const { pubkey, nip46_sk_enc, nip46_relays } = await kysely
|
const { pubkey: userPubkey, bunker_pubkey: bunkerPubkey, nip46_sk_enc, nip46_relays } = await kysely
|
||||||
.selectFrom('auth_tokens')
|
.selectFrom('auth_tokens')
|
||||||
.select(['pubkey', 'nip46_sk_enc', 'nip46_relays'])
|
.select(['pubkey', 'bunker_pubkey', 'nip46_sk_enc', 'nip46_relays'])
|
||||||
.where('token_hash', '=', tokenHash)
|
.where('token_hash', '=', tokenHash)
|
||||||
.executeTakeFirstOrThrow();
|
.executeTakeFirstOrThrow();
|
||||||
|
|
||||||
const nep46Seckey = await aesDecrypt(Conf.seckey, nip46_sk_enc);
|
const nep46Seckey = await aesDecrypt(Conf.seckey, nip46_sk_enc);
|
||||||
|
|
||||||
c.set('signer', new ConnectSigner(pubkey, new NSecSigner(nep46Seckey), nip46_relays));
|
c.set(
|
||||||
|
'signer',
|
||||||
|
new ConnectSigner({
|
||||||
|
bunkerPubkey,
|
||||||
|
userPubkey,
|
||||||
|
signer: new NSecSigner(nep46Seckey),
|
||||||
|
relays: nip46_relays,
|
||||||
|
}),
|
||||||
|
);
|
||||||
} catch {
|
} catch {
|
||||||
throw new HTTPException(401);
|
throw new HTTPException(401);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,13 @@ import { NConnectSigner, NostrEvent, NostrSigner } from '@nostrify/nostrify';
|
||||||
|
|
||||||
import { Storages } from '@/storages.ts';
|
import { Storages } from '@/storages.ts';
|
||||||
|
|
||||||
|
interface ConnectSignerOpts {
|
||||||
|
bunkerPubkey: string;
|
||||||
|
userPubkey: string;
|
||||||
|
signer: NostrSigner;
|
||||||
|
relays?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NIP-46 signer.
|
* NIP-46 signer.
|
||||||
*
|
*
|
||||||
|
|
@ -12,13 +19,13 @@ import { Storages } from '@/storages.ts';
|
||||||
export class ConnectSigner implements NostrSigner {
|
export class ConnectSigner implements NostrSigner {
|
||||||
private signer: Promise<NConnectSigner>;
|
private signer: Promise<NConnectSigner>;
|
||||||
|
|
||||||
constructor(private pubkey: string, signer: NostrSigner, private relays?: string[]) {
|
constructor(private opts: ConnectSignerOpts) {
|
||||||
this.signer = this.init(signer);
|
this.signer = this.init(opts.signer);
|
||||||
}
|
}
|
||||||
|
|
||||||
async init(signer: NostrSigner): Promise<NConnectSigner> {
|
async init(signer: NostrSigner): Promise<NConnectSigner> {
|
||||||
return new NConnectSigner({
|
return new NConnectSigner({
|
||||||
pubkey: this.pubkey,
|
pubkey: this.opts.bunkerPubkey,
|
||||||
// TODO: use a remote relay for `nprofile` signing (if present and `Conf.relay` isn't already in the list)
|
// TODO: use a remote relay for `nprofile` signing (if present and `Conf.relay` isn't already in the list)
|
||||||
relay: await Storages.pubsub(),
|
relay: await Storages.pubsub(),
|
||||||
signer,
|
signer,
|
||||||
|
|
@ -30,8 +37,8 @@ export class ConnectSigner implements NostrSigner {
|
||||||
const signer = await this.signer;
|
const signer = await this.signer;
|
||||||
try {
|
try {
|
||||||
return await signer.signEvent(event);
|
return await signer.signEvent(event);
|
||||||
} catch (e: any) {
|
} catch (e) {
|
||||||
if (e.name === 'AbortError') {
|
if (e instanceof Error && e.name === 'AbortError') {
|
||||||
throw new HTTPException(408, { message: 'The event was not signed quickly enough' });
|
throw new HTTPException(408, { message: 'The event was not signed quickly enough' });
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
|
|
@ -44,8 +51,8 @@ export class ConnectSigner implements NostrSigner {
|
||||||
const signer = await this.signer;
|
const signer = await this.signer;
|
||||||
try {
|
try {
|
||||||
return await signer.nip04.encrypt(pubkey, plaintext);
|
return await signer.nip04.encrypt(pubkey, plaintext);
|
||||||
} catch (e: any) {
|
} catch (e) {
|
||||||
if (e.name === 'AbortError') {
|
if (e instanceof Error && e.name === 'AbortError') {
|
||||||
throw new HTTPException(408, {
|
throw new HTTPException(408, {
|
||||||
message: 'Text was not encrypted quickly enough',
|
message: 'Text was not encrypted quickly enough',
|
||||||
});
|
});
|
||||||
|
|
@ -59,8 +66,8 @@ export class ConnectSigner implements NostrSigner {
|
||||||
const signer = await this.signer;
|
const signer = await this.signer;
|
||||||
try {
|
try {
|
||||||
return await signer.nip04.decrypt(pubkey, ciphertext);
|
return await signer.nip04.decrypt(pubkey, ciphertext);
|
||||||
} catch (e: any) {
|
} catch (e) {
|
||||||
if (e.name === 'AbortError') {
|
if (e instanceof Error && e.name === 'AbortError') {
|
||||||
throw new HTTPException(408, {
|
throw new HTTPException(408, {
|
||||||
message: 'Text was not decrypted quickly enough',
|
message: 'Text was not decrypted quickly enough',
|
||||||
});
|
});
|
||||||
|
|
@ -76,8 +83,8 @@ export class ConnectSigner implements NostrSigner {
|
||||||
const signer = await this.signer;
|
const signer = await this.signer;
|
||||||
try {
|
try {
|
||||||
return await signer.nip44.encrypt(pubkey, plaintext);
|
return await signer.nip44.encrypt(pubkey, plaintext);
|
||||||
} catch (e: any) {
|
} catch (e) {
|
||||||
if (e.name === 'AbortError') {
|
if (e instanceof Error && e.name === 'AbortError') {
|
||||||
throw new HTTPException(408, {
|
throw new HTTPException(408, {
|
||||||
message: 'Text was not encrypted quickly enough',
|
message: 'Text was not encrypted quickly enough',
|
||||||
});
|
});
|
||||||
|
|
@ -91,8 +98,8 @@ export class ConnectSigner implements NostrSigner {
|
||||||
const signer = await this.signer;
|
const signer = await this.signer;
|
||||||
try {
|
try {
|
||||||
return await signer.nip44.decrypt(pubkey, ciphertext);
|
return await signer.nip44.decrypt(pubkey, ciphertext);
|
||||||
} catch (e: any) {
|
} catch (e) {
|
||||||
if (e.name === 'AbortError') {
|
if (e instanceof Error && e.name === 'AbortError') {
|
||||||
throw new HTTPException(408, {
|
throw new HTTPException(408, {
|
||||||
message: 'Text was not decrypted quickly enough',
|
message: 'Text was not decrypted quickly enough',
|
||||||
});
|
});
|
||||||
|
|
@ -105,12 +112,12 @@ export class ConnectSigner implements NostrSigner {
|
||||||
|
|
||||||
// Prevent unnecessary NIP-46 round-trips.
|
// Prevent unnecessary NIP-46 round-trips.
|
||||||
async getPublicKey(): Promise<string> {
|
async getPublicKey(): Promise<string> {
|
||||||
return this.pubkey;
|
return this.opts.userPubkey;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the user's relays if they passed in an `nprofile` auth token. */
|
/** Get the user's relays if they passed in an `nprofile` auth token. */
|
||||||
async getRelays(): Promise<Record<string, { read: boolean; write: boolean }>> {
|
async getRelays(): Promise<Record<string, { read: boolean; write: boolean }>> {
|
||||||
return this.relays?.reduce<Record<string, { read: boolean; write: boolean }>>((acc, relay) => {
|
return this.opts.relays?.reduce<Record<string, { read: boolean; write: boolean }>>((acc, relay) => {
|
||||||
acc[relay] = { read: true, write: true };
|
acc[relay] = { read: true, write: true };
|
||||||
return acc;
|
return acc;
|
||||||
}, {}) ?? {};
|
}, {}) ?? {};
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue