1
0
mirror of https://github.com/spacebarchat/server.git synced 2024-11-22 02:12:40 +01:00

update mfa and login to reflect latest discord

This commit is contained in:
Puyodead1 2023-12-16 18:17:36 -05:00
parent 5e91a24e53
commit 40db978c7d
No known key found for this signature in database
GPG Key ID: A4FA4FEC0DD353FC
9 changed files with 2133 additions and 12 deletions

File diff suppressed because it is too large Load Diff

View File

@ -139,7 +139,9 @@ router.post(
ticket: ticket, ticket: ticket,
mfa: true, mfa: true,
sms: false, // TODO sms: false, // TODO
token: null, totp: true,
backup: true,
user_id: user.id,
}); });
} }
@ -172,7 +174,9 @@ router.post(
ticket: ticket, ticket: ticket,
mfa: true, mfa: true,
sms: false, // TODO sms: false, // TODO
token: null, totp: true,
backup: true,
user_id: user.id,
webauthn: challenge, webauthn: challenge,
}); });
} }
@ -202,7 +206,13 @@ router.post(
// Discord header is just the user id as string, which is not possible with npm-jsonwebtoken package // Discord header is just the user id as string, which is not possible with npm-jsonwebtoken package
// https://user-images.githubusercontent.com/6506416/81051916-dd8c9900-8ec2-11ea-8794-daf12d6f31f0.png // https://user-images.githubusercontent.com/6506416/81051916-dd8c9900-8ec2-11ea-8794-daf12d6f31f0.png
res.json({ token, settings: { ...user.settings, index: undefined } }); res.json({
token,
settings: {
locale: user.settings.locale,
theme: user.settings.theme,
},
});
}, },
); );
@ -211,15 +221,12 @@ router.post(
* @argument { login: "email@gmail.com", password: "cleartextpassword", undelete: false, captcha_key: null, login_source: null, gift_code_sku_id: null, } * @argument { login: "email@gmail.com", password: "cleartextpassword", undelete: false, captcha_key: null, login_source: null, gift_code_sku_id: null, }
* MFA required: * MFA required:
* @returns {"token": null, "mfa": true, "sms": true, "ticket": "SOME TICKET JWT TOKEN"} * @returns {"mfa": true, "sms": boolean, "totp": boolean, "backup": true, "ticket": "SOME TICKET JWT TOKEN", "user_id": "USER ID", "webauthn": "WEBAUTHN DATA or null"}
* WebAuthn MFA required:
* @returns {"token": null, "mfa": true, "webauthn": true, "sms": true, "ticket": "SOME TICKET JWT TOKEN"}
* Captcha required: * Captcha required:
* @returns {"captcha_key": ["captcha-required"], "captcha_sitekey": null, "captcha_service": "recaptcha"} * @returns {"captcha_key": ["captcha-required"], "captcha_sitekey": null, "captcha_service": "recaptcha"}
* Sucess: * Sucess:
* @returns {"token": "USERTOKEN", "settings": {"locale": "en", "theme": "dark"}} * @returns {"token": "USERTOKEN", "user_id": "USER ID", "settings": {"locale": "en-US", "theme": "dark"}}
*/ */

View File

@ -73,7 +73,10 @@ router.post(
return res.json({ return res.json({
token: await generateToken(user.id), token: await generateToken(user.id),
settings: { ...user.settings, index: undefined }, settings: {
locale: user.settings.locale,
theme: user.settings.theme,
},
}); });
}, },
); );

View File

@ -114,7 +114,10 @@ router.post(
return res.json({ return res.json({
token: await generateToken(user.id), token: await generateToken(user.id),
user_settings: user.settings, user_settings: {
locale: user.settings.locale,
theme: user.settings.theme,
},
}); });
}, },
); );

View File

@ -18,6 +18,7 @@
import { route } from "@spacebar/api"; import { route } from "@spacebar/api";
import { import {
AuthenticatorType,
TotpEnableSchema, TotpEnableSchema,
User, User,
generateMfaBackupCodes, generateMfaBackupCodes,
@ -74,7 +75,14 @@ router.post(
await Promise.all(backup_codes.map((x) => x.save())); await Promise.all(backup_codes.map((x) => x.save()));
await User.update( await User.update(
{ id: req.user_id }, { 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({ res.send({

View File

@ -18,9 +18,12 @@
import { route } from "@spacebar/api"; import { route } from "@spacebar/api";
import { import {
AuthenticatorType,
BackupCode,
CreateWebAuthnCredentialSchema, CreateWebAuthnCredentialSchema,
DiscordApiErrors, DiscordApiErrors,
FieldErrors, FieldErrors,
generateMfaBackupCodes,
GenerateWebAuthnCredentialsSchema, GenerateWebAuthnCredentialsSchema,
generateWebAuthnTicket, generateWebAuthnTicket,
SecurityKey, SecurityKey,
@ -193,12 +196,41 @@ router.post(
await Promise.all([ await Promise.all([
securityKey.save(), 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({ return res.json({
name, name,
id: securityKey.id, id: securityKey.id,
type: AuthenticatorType.WEBAUTHN, // I think thats what this is?
backup_codes: backup_codes.map((x) => ({
...x,
expired: undefined,
})),
}); });
} else { } else {
throw DiscordApiErrors.INVALID_AUTHENTICATION_TOKEN; throw DiscordApiErrors.INVALID_AUTHENTICATION_TOKEN;

View File

@ -18,4 +18,6 @@
export class TwoFactorConfiguration { export class TwoFactorConfiguration {
generateBackupCodes: boolean = true; generateBackupCodes: boolean = true;
webauthnAttestation: "none" | "indirect" | "direct" = "none";
webauthnTimeout: number = 60000;
} }

View File

@ -85,6 +85,12 @@ export interface UserPrivate extends Pick<User, PrivateUserKeys> {
locale: string; locale: string;
} }
export enum AuthenticatorType {
WEBAUTHN = 1,
TOTP = 2,
SMS = 3,
}
@Entity("users") @Entity("users")
export class User extends BaseClass { export class User extends BaseClass {
@Column() @Column()
@ -231,6 +237,9 @@ export class User extends BaseClass {
@OneToMany(() => SecurityKey, (key: SecurityKey) => key.user) @OneToMany(() => SecurityKey, (key: SecurityKey) => key.user)
security_keys: SecurityKey[]; security_keys: SecurityKey[];
@Column({ type: "simple-array", select: false })
authenticator_types: AuthenticatorType[] = [];
// TODO: I don't like this method? // TODO: I don't like this method?
validate() { validate() {
if (this.discriminator) { if (this.discriminator) {

View File

@ -33,6 +33,15 @@ export const WebAuthn: {
init: function () { init: function () {
this.fido2 = new Fido2Lib({ this.fido2 = new Fido2Lib({
challengeSize: 128, 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",
}); });
}, },
}; };