Compare commits

...

5 commits

Author SHA1 Message Date
unlinkability 40e7350390
Merge 32a297d2ef into b59d1b24ff 2024-06-25 15:11:41 -05:00
May 32a297d2ef
why
html forms only support get and post im gonna have to make this a separate route
2024-06-25 13:11:14 -07:00
May ca99f85f62
fix invite signup teehee 2024-06-25 13:01:54 -07:00
May 0f7ae63c97
forcing accounts for identity proofs for now (doesn't matter) 2024-06-25 12:56:09 -07:00
May c53a31c405
allow PATCH /account/:id with application/x-www-form-urlencoded, limit on codes 2024-06-25 12:48:35 -07:00
3 changed files with 32 additions and 12 deletions

View file

@ -4,12 +4,14 @@ import crypto from "node:crypto"
export type Intent = "verifyEmail" | "recoverAccount" | "identityProof" export type Intent = "verifyEmail" | "recoverAccount" | "identityProof"
export const Intents = { export const Intents = {
verifyEmail: {}, verifyEmail: {
limit: 2
},
recoverAccount: {}, recoverAccount: {},
identityProof: { identityProof: {
codeGenerator: crypto.randomUUID codeGenerator: crypto.randomUUID
} }
} as Record<Intent, {codeGenerator?: () => string}> } as Record<Intent, {codeGenerator?: () => string, limit?: number}>
export function isIntent(intent: string): intent is Intent { export function isIntent(intent: string): intent is Intent {
return intent in Intents return intent in Intents
@ -72,4 +74,15 @@ export class Code {
check(forUser: string) { check(forUser: string) {
return forUser === this.for return forUser === this.for
} }
}
export function code(...params: ConstructorParameters<typeof Code>): { success: true, code: Code } | { success: false, error: string } {
const [intent, forUser] = params
const {limit = 100} = Intents[intent]
const {length: codeCount} = codes[intent].byUser.get(forUser) || [];
if (codeCount >= limit)
return { success: false, error: `Too many active codes for intent ${intent} (${limit})` }
else
return { success: true, code: new Code(...params) }
} }

View file

@ -111,12 +111,12 @@ const validators: {
// send verification email // send verification email
if ( const tryCode = CodeMgr.code("verifyEmail", target.id, params.email)
(CodeMgr.codes.verifyEmail.byUser.get(target.id)?.length || 0) >= 2
)
return [429, "you have too many active codes"]
let code = new CodeMgr.Code("verifyEmail", target.id, params.email) if (!tryCode.success)
return [429, tryCode.error]
const { code } = tryCode
sendMail( sendMail(
params.email, params.email,
@ -264,7 +264,7 @@ export default function (files: Files) {
password: AccountSchemas.StringPassword, password: AccountSchemas.StringPassword,
invite: z.string().max(6) invite: z.string().max(6)
}).omit( }).omit(
Configuration.accounts.requiredForUpload !Configuration.accounts.requiredForUpload
? { invite: true } ? { invite: true }
: {} : {}
)), async (ctx) => { )), async (ctx) => {
@ -302,7 +302,9 @@ export default function (files: Files) {
router.patch( router.patch(
"/:user", "/:user",
scheme(UserUpdateScheme), scheme(
UserUpdateScheme
),
assertAPI( assertAPI(
ctx => ctx =>
Object.keys(ctx.get("parsedScheme")) Object.keys(ctx.get("parsedScheme"))

View file

@ -49,7 +49,7 @@ export default function () {
return ctx.json(["none"]) // if we add 2fa in the future, return available 2fa methods return ctx.json(["none"]) // if we add 2fa in the future, return available 2fa methods
}) })
router.post("/", scheme( router.post("/", requiresAccount, scheme(
ProofCreationSchema ProofCreationSchema
), async (ctx) => { ), async (ctx) => {
@ -68,12 +68,17 @@ export default function () {
// if actor does have 2fa in an else block here // if actor does have 2fa in an else block here
return ctx.text(new CodeMgr.Code( const tryCode = CodeMgr.code(
"identityProof", "identityProof",
target.id, target.id,
Boolean(actor), // so that you can only log in with proofs created when logged out Boolean(actor), // so that you can only log in with proofs created when logged out
5 * 60 * 1000 5 * 60 * 1000
).id) )
if (!tryCode.success)
return ServeError(ctx, 429, tryCode.error)
return ctx.text(tryCode.code.id)
}) })
return router return router