Files
backend/src/routes/auth/manage.ts
2023-11-03 18:07:26 +00:00

80 lines
2.1 KiB
TypeScript

import { ChallengeCode } from '@/db/models/ChallengeCode';
import { formatSession } from '@/db/models/Session';
import { User, formatUser } from '@/db/models/User';
import { getMetrics } from '@/modules/metrics';
import { assertCaptcha } from '@/services/captcha';
import { handle } from '@/services/handler';
import { makeRouter } from '@/services/router';
import { makeSession, makeSessionToken } from '@/services/session';
import { z } from 'zod';
import { assertChallengeCode } from '@/services/challenge';
const startSchema = z.object({
captchaToken: z.string().optional(),
});
const completeSchema = z.object({
publicKey: z.string(),
challenge: z.object({
code: z.string(),
signature: z.string(),
}),
namespace: z.string().min(1),
device: z.string().max(500).min(1),
profile: z.object({
colorA: z.string(),
colorB: z.string(),
icon: z.string(),
}),
});
export const manageAuthRouter = makeRouter((app) => {
app.post(
'/auth/register/start',
{ schema: { body: startSchema } },
handle(async ({ em, body }) => {
await assertCaptcha(body.captchaToken);
const challenge = new ChallengeCode();
challenge.authType = 'mnemonic';
challenge.stage = 'registration';
await em.persistAndFlush(challenge);
return {
challenge: challenge.code,
};
}),
);
app.post(
'/auth/register/complete',
{ schema: { body: completeSchema } },
handle(async ({ em, body, req }) => {
await assertChallengeCode(
em,
body.challenge.code,
body.publicKey,
body.challenge.signature,
);
const user = new User();
user.namespace = body.namespace;
user.publicKey = body.publicKey;
user.profile = body.profile;
const session = makeSession(
user.id,
body.device,
req.headers['user-agent'],
);
await em.persistAndFlush([user, session]);
getMetrics().user.inc({ namespace: body.namespace }, 1);
return {
user: formatUser(user),
session: formatSession(session),
token: makeSessionToken(session),
};
}),
);
});