OAuth: add a "nostr" grant_type
This commit is contained in:
parent
c55cd2a977
commit
1aa2bafc44
|
@ -24,6 +24,7 @@
|
|||
"@isaacs/ttlcache": "npm:@isaacs/ttlcache@^1.4.1",
|
||||
"@noble/secp256k1": "npm:@noble/secp256k1@^2.0.0",
|
||||
"@nostrify/nostrify": "jsr:@nostrify/nostrify@^0.22.4",
|
||||
"@scure/base": "npm:@scure/base@^1.1.6",
|
||||
"@sentry/deno": "https://deno.land/x/sentry@7.112.2/index.mjs",
|
||||
"@soapbox/kysely-deno-sqlite": "jsr:@soapbox/kysely-deno-sqlite@^2.1.0",
|
||||
"@soapbox/stickynotes": "jsr:@soapbox/stickynotes@^0.4.0",
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
import { NConnectSigner, NSchema as n, NSecSigner } from '@nostrify/nostrify';
|
||||
import { bech32 } from '@scure/base';
|
||||
import { encodeBase64 } from '@std/encoding/base64';
|
||||
import { escape } from 'entities';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { generateSecretKey, getPublicKey, nip19 } from 'nostr-tools';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { AppController } from '@/app.ts';
|
||||
import { DittoDB } from '@/db/DittoDB.ts';
|
||||
import { nostrNow } from '@/utils.ts';
|
||||
import { parseBody } from '@/utils/api.ts';
|
||||
import { getClientConnectUri } from '@/utils/connect.ts';
|
||||
import { Storages } from '@/storages.ts';
|
||||
|
||||
const passwordGrantSchema = z.object({
|
||||
grant_type: z.literal('password'),
|
||||
|
@ -22,10 +26,18 @@ const credentialsGrantSchema = z.object({
|
|||
grant_type: z.literal('client_credentials'),
|
||||
});
|
||||
|
||||
const nostrGrantSchema = z.object({
|
||||
grant_type: z.literal('nostr'),
|
||||
pubkey: n.id(),
|
||||
relays: z.string().url().array().optional(),
|
||||
secret: z.string().optional(),
|
||||
});
|
||||
|
||||
const createTokenSchema = z.discriminatedUnion('grant_type', [
|
||||
passwordGrantSchema,
|
||||
codeGrantSchema,
|
||||
credentialsGrantSchema,
|
||||
nostrGrantSchema,
|
||||
]);
|
||||
|
||||
const createTokenController: AppController = async (c) => {
|
||||
|
@ -37,6 +49,13 @@ const createTokenController: AppController = async (c) => {
|
|||
}
|
||||
|
||||
switch (result.data.grant_type) {
|
||||
case 'nostr':
|
||||
return c.json({
|
||||
access_token: await getToken(result.data),
|
||||
token_type: 'Bearer',
|
||||
scope: 'read write follow push',
|
||||
created_at: nostrNow(),
|
||||
});
|
||||
case 'password':
|
||||
return c.json({
|
||||
access_token: result.data.password,
|
||||
|
@ -61,6 +80,40 @@ const createTokenController: AppController = async (c) => {
|
|||
}
|
||||
};
|
||||
|
||||
async function getToken({ pubkey, secret, relays = [] }: z.infer<typeof nostrGrantSchema>): Promise<`token1${string}`> {
|
||||
const kysely = await DittoDB.getInstance();
|
||||
const token = generateToken();
|
||||
|
||||
const serverSeckey = generateSecretKey();
|
||||
const serverPubkey = getPublicKey(serverSeckey);
|
||||
|
||||
const signer = new NConnectSigner({
|
||||
pubkey,
|
||||
signer: new NSecSigner(serverSeckey),
|
||||
relay: await Storages.pubsub(),
|
||||
timeout: 60_000,
|
||||
});
|
||||
|
||||
await signer.connect(secret);
|
||||
|
||||
await kysely.insertInto('connections').values({
|
||||
api_token: token,
|
||||
user_pubkey: pubkey,
|
||||
server_seckey: serverSeckey,
|
||||
server_pubkey: serverPubkey,
|
||||
relays: JSON.stringify(relays),
|
||||
connected_at: new Date(),
|
||||
}).execute();
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/** Generate a bech32 token for the API. */
|
||||
function generateToken(): `token1${string}` {
|
||||
const words = bech32.toWords(generateSecretKey());
|
||||
return bech32.encode('token', words);
|
||||
}
|
||||
|
||||
/** Display the OAuth form. */
|
||||
const oauthController: AppController = async (c) => {
|
||||
const encodedUri = c.req.query('redirect_uri');
|
||||
|
|
|
@ -21,7 +21,7 @@ export class ConnectSigner implements NostrSigner {
|
|||
// TODO: use a remote relay for `nprofile` signing (if present and `Conf.relay` isn't already in the list)
|
||||
relay: await Storages.pubsub(),
|
||||
signer,
|
||||
timeout: 60000,
|
||||
timeout: 60_000,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue