Add a UUID to auth tokens for private websocket event signing

This commit is contained in:
Alex Gleason
2023-05-20 14:34:13 -05:00
parent 9500ceee7c
commit f3e42cc6a7
6 changed files with 42 additions and 49 deletions

View File

@@ -1,4 +1,4 @@
import { lodash, nip19, z } from '@/deps.ts';
import { lodash, nip19, uuid62, z } from '@/deps.ts';
import { AppController } from '@/app.ts';
import { parseBody } from '@/utils.ts';
@@ -94,6 +94,7 @@ function maybeDecodeUri(uri: string): string {
}
}
/** Schema for FormData POSTed to the OAuthController. */
const oauthAuthorizeSchema = z.object({
pubkey: z.string().regex(/^[0-9a-f]{64}$/).optional().catch(undefined),
nip19: z.string().regex(new RegExp(`^${nip19.BECH32_REGEX.source}$`)).optional().catch(undefined),
@@ -107,21 +108,31 @@ const oauthAuthorizeSchema = z.object({
}
});
/** Controller the OAuth form is POSTed to. */
const oauthAuthorizeController: AppController = async (c) => {
/** FormData results in JSON. */
const result = oauthAuthorizeSchema.safeParse(await parseBody(c.req.raw));
if (!result.success) {
return c.json(result.error, 422);
}
// Parsed FormData values.
const { pubkey, nip19: nip19id, redirect_uri: redirectUri } = result.data;
/**
* Normally the auth token is just an npub, which is public information.
* The sessionId helps us know that Request "B" and Request "A" came from the same person.
* Useful for sending websocket events to the correct client.
*/
const sessionId: string = uuid62.v4();
if (pubkey) {
const encoded = nip19.npubEncode(pubkey!);
const url = addCodeToRedirectUri(redirectUri, encoded);
const url = addCodeToRedirectUri(redirectUri, `${encoded}_${sessionId}`);
return c.redirect(url);
} else if (nip19id) {
const url = addCodeToRedirectUri(redirectUri, nip19id);
const url = addCodeToRedirectUri(redirectUri, `${nip19id}_${sessionId}`);
return c.redirect(url);
}

View File

@@ -1,5 +1,6 @@
import { AppController } from '@/app.ts';
import { nip19 } from '@/deps.ts';
import { TOKEN_REGEX } from '@/middleware/auth.ts';
import { signStreams } from '@/sign.ts';
const streamingController: AppController = (c) => {
@@ -17,19 +18,26 @@ const streamingController: AppController = (c) => {
return c.json({ error: 'Missing access token' }, 401);
}
if (!nip19.BECH32_REGEX.test(token)) {
if (!(new RegExp(`^${TOKEN_REGEX.source}$`)).test(token)) {
return c.json({ error: 'Invalid access token' }, 401);
}
const { socket, response } = Deno.upgradeWebSocket(c.req.raw, { protocol: token });
socket.addEventListener('open', () => console.log('websocket: connection opened'));
socket.addEventListener('close', () => console.log('websocket: connection closed'));
socket.addEventListener('open', () => {
console.log('websocket: connection opened');
// Only send signing events if the user has a session ID.
if (stream === 'user' && nostr === 'true' && new RegExp(`^${nip19.BECH32_REGEX.source}_\\w+$`).test(token)) {
signStreams.set(token, socket);
}
});
socket.addEventListener('message', (e) => console.log('websocket message: ', e.data));
if (stream === 'user' && nostr === 'true') {
signStreams.set(token, socket);
}
socket.addEventListener('close', () => {
signStreams.delete(token);
console.log('websocket: connection closed');
});
return response;
};