From 40db978c7d83c4db38effedb52522a5414400528 Mon Sep 17 00:00:00 2001 From: Puyodead1 Date: Sat, 16 Dec 2023 18:17:36 -0500 Subject: [PATCH] update mfa and login to reflect latest discord --- assets/schemas.json | Bin 18300690 -> 18366994 bytes src/api/routes/auth/login.ts | 23 +++++++----- src/api/routes/auth/mfa/totp.ts | 5 ++- src/api/routes/auth/mfa/webauthn.ts | 5 ++- src/api/routes/users/@me/mfa/totp/enable.ts | 10 +++++- .../@me/mfa/webauthn/credentials/index.ts | 34 +++++++++++++++++- .../subconfigurations/security/TwoFactor.ts | 2 ++ src/util/entities/User.ts | 9 +++++ src/util/util/WebAuthn.ts | 9 +++++ 9 files changed, 85 insertions(+), 12 deletions(-) diff --git a/assets/schemas.json b/assets/schemas.json index a0ab1697c99e4bad5533af3b5d5feaa87e003ecc..91e23f1aaad92775c9ca0efa8fe5bd03d280e366 100644 GIT binary patch delta 17384 zcmd6v|4-C)9LM)L?(hzd@0>6|_CB-8V~}nkkMUaA-WW#7B!TMsgTBCjG(Nb4-i}Z1<(8WKA-Ke=kxh`y}#b4 zd|^afUih=P^`q}tZBT?znv|NAk{F+rmT@R6hn-S6x>OX1BrI-0cF0tjmAv{Zt208!&OT?7%L=?$CIbw zBYTeXv7P92u&8neZz>2o-iIX#m22|jrJRUnPNaDl2ukn8^lU6jq@14I$;`(>yeKEL zKb0j|h!8o`7rrBAsrQ637T2iW!3wPQyH&UdWUO>tWXW2NMXw?K#_R@kr=v}8 z?vm51mlbd&w&YxW9^JM&8=C9ULZ};>MHD{NI)UE9G3 zSWe>`=tRIwK6+1q_+a3t7jIV1<)F778Gk2COnJS(kHMGGMpMruU!dDoi|5~oa8Gy$ zGwO|6+=8u>)mt~(nnI4t!sg@t6gU~$=@Ai^j&9*vp;*l2Vy)mW+jLer+p~fg@@Jzy z5(oZ60zT{k+1RTia;(_2fU{=nD=eIu($-DmmY*d> zC9guvLK!9@0s?0da4{od8jUdq7(xNd9i|-EDH%12y1T1C@7bR_&vTyhoco@8?%C>3 zXNA?vQ^K}$qokqeP%klLR0qS|0~y(ylq&UBP^q^?$;kFnzBTE;Wp58DNxZl=HWMhb z9K%c61-CA<<4Jgg#2Wsa7OJk`BoRbDFHXWTQ6&wM2~K3&4rBP^dOL#Yzk1XD1QwsU zhZg|Zzf*T{q$yPVrB$-$bFE>7K+l=OY8>pIvZ@R@h}_&WrT z3-oA2>_YV(uMtdNto8=vV2NR_SwK{Ot`|Vwx9dJYGMSh_nTd!Tl&|rNMGg**#s}o~ zBjwhX2l-;Sd}t%~ouOheI2rSI$}?1n`RhsCbQHtyC|-}nE{k+FLIRT=n=3I9N;-n! zLAKG4up(}U$1Gy;@`G^=J*-3CDkG)w0SRO}+ct&-?C%R#OYPn<{;~o?)0<(fthmkv z77{Ikc`n|K>4@T5|F^p^+$l9lgH%imh3S0AsNCVs6rpp|NFt&q4iebhyECVgW%nr) z(&l%yBR9vKv)?s^UDz|FvhAt;$jt$2OyA441I60C8<4^vS--CUX*$STkvWUy=iWH5 z$n1wG8E8tfLXrGrOqj7=J!2Q54$pN}E3ABpTj-pXdcfI{G^9ve)KZb1Cf!%e3CKth z-+>ZV8(WH!#YDlkevTNvuXog>QG)fE_r*4_`=Tn4*=12OP3pT6P6(cNv%L^cPg#G9 zKg!2gg^hep0xyLfE=E|8us`=ShMzo>AB(gT1z=9%I`kS3mQ#?}=RDNEVZf{l^^_*#j4d!Xxt9E-PEdC8A?99v{W=)U7c zB9~W%kderWS4hLl9#Z0ZoVU_Ac0d<0xi3LDDJ)S>Jl@aJ{B_34I%M6>nMSly?yTuvKt@tP1inTI;9hP>F0sZ0g0!X z&SUYFA%iU#esAB@$?uuh>##G;r1mA zR~3$aiQ!YtV_#vo&%^|Z=Ue^Kfy5g>JRin`?kKw0->f@x8qM)m&1@r*1I}Lm6|M2N z`TI`D5@BjiXmOi+FvRu<5t+Yh5EG<%+oA~6H4o|#5{zG;zkuLoC;khDW`qQ#E`Jqc zgN8c6TB5t)(Glh-dA8TBz4%0r-DlHQ+B3dnb&Y*w=BtwBu$*Qo9ib$fUPTn=kJM$} zdvxhJJnk@JO=nzuYB9pVPXg``a=^ODe}r5Crtq(inA0Lf>5PktE h%c x.save())); await User.update( { id: req.user_id }, - { mfa_enabled: true, totp_secret: body.secret }, + { + mfa_enabled: true, + totp_secret: body.secret, + authenticator_types: [ + ...user.authenticator_types, + AuthenticatorType.TOTP, + ], + }, ); res.send({ diff --git a/src/api/routes/users/@me/mfa/webauthn/credentials/index.ts b/src/api/routes/users/@me/mfa/webauthn/credentials/index.ts index f383ffb78..c8e5b67a1 100644 --- a/src/api/routes/users/@me/mfa/webauthn/credentials/index.ts +++ b/src/api/routes/users/@me/mfa/webauthn/credentials/index.ts @@ -18,9 +18,12 @@ import { route } from "@spacebar/api"; import { + AuthenticatorType, + BackupCode, CreateWebAuthnCredentialSchema, DiscordApiErrors, FieldErrors, + generateMfaBackupCodes, GenerateWebAuthnCredentialsSchema, generateWebAuthnTicket, SecurityKey, @@ -193,12 +196,41 @@ router.post( await Promise.all([ securityKey.save(), - User.update({ id: req.user_id }, { webauthn_enabled: true }), + User.update( + { id: req.user_id }, + { + webauthn_enabled: true, + authenticator_types: [ + ...user.authenticator_types, + AuthenticatorType.WEBAUTHN, + ], + }, + ), ]); + // try and get the users existing backup codes + let backup_codes = await BackupCode.find({ + where: { + user: { + id: req.user_id, + }, + }, + }); + + // if there arent any, create them + if (!backup_codes.length) { + backup_codes = generateMfaBackupCodes(req.user_id); + await Promise.all(backup_codes.map((x) => x.save())); + } + return res.json({ name, id: securityKey.id, + type: AuthenticatorType.WEBAUTHN, // I think thats what this is? + backup_codes: backup_codes.map((x) => ({ + ...x, + expired: undefined, + })), }); } else { throw DiscordApiErrors.INVALID_AUTHENTICATION_TOKEN; diff --git a/src/util/config/types/subconfigurations/security/TwoFactor.ts b/src/util/config/types/subconfigurations/security/TwoFactor.ts index 757571244..dfa493a75 100644 --- a/src/util/config/types/subconfigurations/security/TwoFactor.ts +++ b/src/util/config/types/subconfigurations/security/TwoFactor.ts @@ -18,4 +18,6 @@ export class TwoFactorConfiguration { generateBackupCodes: boolean = true; + webauthnAttestation: "none" | "indirect" | "direct" = "none"; + webauthnTimeout: number = 60000; } diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts index c6582b00a..255867930 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts @@ -85,6 +85,12 @@ export interface UserPrivate extends Pick { locale: string; } +export enum AuthenticatorType { + WEBAUTHN = 1, + TOTP = 2, + SMS = 3, +} + @Entity("users") export class User extends BaseClass { @Column() @@ -231,6 +237,9 @@ export class User extends BaseClass { @OneToMany(() => SecurityKey, (key: SecurityKey) => key.user) security_keys: SecurityKey[]; + @Column({ type: "simple-array", select: false }) + authenticator_types: AuthenticatorType[] = []; + // TODO: I don't like this method? validate() { if (this.discriminator) { diff --git a/src/util/util/WebAuthn.ts b/src/util/util/WebAuthn.ts index b0027b13c..599efe332 100644 --- a/src/util/util/WebAuthn.ts +++ b/src/util/util/WebAuthn.ts @@ -33,6 +33,15 @@ export const WebAuthn: { init: function () { this.fido2 = new Fido2Lib({ challengeSize: 128, + rpName: Config.get().general.instanceName, + rpId: + Config.get().general.frontPage ?? + Config.get().general.instanceName.toLowerCase(), + attestation: Config.get().security.twoFactor.webauthnAttestation, + // rpIcon: + timeout: Config.get().security.twoFactor.webauthnTimeout, + authenticatorRequireResidentKey: false, + authenticatorUserVerification: "preferred", }); }, };