diff --git a/src/api/routes/users/@me/mfa/codes-verification.ts b/src/api/routes/users/@me/mfa/codes-verification.ts
index c8995d83..4cc4c072 100644
--- a/src/api/routes/users/@me/mfa/codes-verification.ts
+++ b/src/api/routes/users/@me/mfa/codes-verification.ts
@@ -24,52 +24,52 @@ import { CodesVerificationSchema } from "@spacebar/schemas";
const router = Router({ mergeParams: true });
router.post(
- "/",
- route({
- requestBody: "CodesVerificationSchema",
- responses: {
- 200: {
- body: "APIBackupCodeArray",
- },
- 400: {
- body: "APIErrorResponse",
- },
- 404: {
- body: "APIErrorResponse",
- },
- },
- }),
- async (req: Request, res: Response) => {
- // const { key, nonce, regenerate } = req.body as CodesVerificationSchema;
- const { regenerate } = req.body as CodesVerificationSchema;
+ "/",
+ route({
+ requestBody: "CodesVerificationSchema",
+ responses: {
+ 200: {
+ body: "APIBackupCodeArray",
+ },
+ 400: {
+ body: "APIErrorResponse",
+ },
+ 404: {
+ body: "APIErrorResponse",
+ },
+ },
+ }),
+ async (req: Request, res: Response) => {
+ // const { key, nonce, regenerate } = req.body as CodesVerificationSchema;
+ const { regenerate } = req.body as CodesVerificationSchema;
- // TODO: We don't have email/etc etc, so can't send a verification code.
- // Once that's done, this route can verify `key`
+ // TODO: We don't have email/etc etc, so can't send a verification code.
+ // Once that's done, this route can verify `key`
- // const user = await User.findOneOrFail({ where: { id: req.user_id } });
- if ((await User.count({ where: { id: req.user_id } })) === 0) throw DiscordApiErrors.UNKNOWN_USER;
+ // const user = await User.findOneOrFail({ where: { id: req.user_id } });
+ if ((await User.count({ where: { id: req.user_id } })) === 0) throw DiscordApiErrors.UNKNOWN_USER;
- let codes: BackupCode[];
- if (regenerate) {
- await BackupCode.update({ user: { id: req.user_id } }, { expired: true });
+ let codes: BackupCode[];
+ if (regenerate) {
+ await BackupCode.update({ user: { id: req.user_id } }, { expired: true });
- codes = generateMfaBackupCodes(req.user_id);
- await Promise.all(codes.map((x) => x.save()));
- } else {
- codes = await BackupCode.find({
- where: {
- user: {
- id: req.user_id,
- },
- expired: false,
- },
- });
- }
+ codes = generateMfaBackupCodes(req.user_id);
+ await Promise.all(codes.map((x) => x.save()));
+ } else {
+ codes = await BackupCode.find({
+ where: {
+ user: {
+ id: req.user_id,
+ },
+ expired: false,
+ },
+ });
+ }
- return res.json({
- backup_codes: codes.map((x) => ({ ...x, expired: undefined })),
- });
- },
+ return res.json({
+ backup_codes: codes.map((x) => ({ ...x, expired: undefined })),
+ });
+ },
);
export default router;
diff --git a/src/api/routes/users/@me/mfa/codes.ts b/src/api/routes/users/@me/mfa/codes.ts
index 579a106e..2eb1a3e3 100644
--- a/src/api/routes/users/@me/mfa/codes.ts
+++ b/src/api/routes/users/@me/mfa/codes.ts
@@ -27,61 +27,61 @@ const router = Router({ mergeParams: true });
// TODO: This route is replaced with users/@me/mfa/codes-verification in newer clients
router.post(
- "/",
- route({
- requestBody: "MfaCodesSchema",
- deprecated: true,
- description: "This route is replaced with users/@me/mfa/codes-verification in newer clients",
- responses: {
- 200: {
- body: "APIBackupCodeArray",
- },
- 400: {
- body: "APIErrorResponse",
- },
- 404: {
- body: "APIErrorResponse",
- },
- },
- }),
- async (req: Request, res: Response) => {
- const { password, regenerate } = req.body as MfaCodesSchema;
+ "/",
+ route({
+ requestBody: "MfaCodesSchema",
+ deprecated: true,
+ description: "This route is replaced with users/@me/mfa/codes-verification in newer clients",
+ responses: {
+ 200: {
+ body: "APIBackupCodeArray",
+ },
+ 400: {
+ body: "APIErrorResponse",
+ },
+ 404: {
+ body: "APIErrorResponse",
+ },
+ },
+ }),
+ async (req: Request, res: Response) => {
+ const { password, regenerate } = req.body as MfaCodesSchema;
- const user = await User.findOneOrFail({
- where: { id: req.user_id },
- select: ["data"],
- });
+ const user = await User.findOneOrFail({
+ where: { id: req.user_id },
+ select: ["data"],
+ });
- if (!(await bcrypt.compare(password, user.data.hash || ""))) {
- throw FieldErrors({
- password: {
- message: req.t("auth:login.INVALID_PASSWORD"),
- code: "INVALID_PASSWORD",
- },
- });
- }
+ if (!(await bcrypt.compare(password, user.data.hash || ""))) {
+ throw FieldErrors({
+ password: {
+ message: req.t("auth:login.INVALID_PASSWORD"),
+ code: "INVALID_PASSWORD",
+ },
+ });
+ }
- let codes: BackupCode[];
- if (regenerate) {
- await BackupCode.update({ user: { id: req.user_id } }, { expired: true });
+ let codes: BackupCode[];
+ if (regenerate) {
+ await BackupCode.update({ user: { id: req.user_id } }, { expired: true });
- codes = generateMfaBackupCodes(req.user_id);
- await Promise.all(codes.map((x) => x.save()));
- } else {
- codes = await BackupCode.find({
- where: {
- user: {
- id: req.user_id,
- },
- expired: false,
- },
- });
- }
+ codes = generateMfaBackupCodes(req.user_id);
+ await Promise.all(codes.map((x) => x.save()));
+ } else {
+ codes = await BackupCode.find({
+ where: {
+ user: {
+ id: req.user_id,
+ },
+ expired: false,
+ },
+ });
+ }
- return res.json({
- backup_codes: codes.map((x) => ({ ...x, expired: undefined })),
- });
- },
+ return res.json({
+ backup_codes: codes.map((x) => ({ ...x, expired: undefined })),
+ });
+ },
);
export default router;
diff --git a/src/api/routes/users/@me/mfa/totp/disable.ts b/src/api/routes/users/@me/mfa/totp/disable.ts
index 003dcb74..11c927e4 100644
--- a/src/api/routes/users/@me/mfa/totp/disable.ts
+++ b/src/api/routes/users/@me/mfa/totp/disable.ts
@@ -26,51 +26,51 @@ import { TotpDisableSchema } from "@spacebar/schemas";
const router = Router({ mergeParams: true });
router.post(
- "/",
- route({
- requestBody: "TotpDisableSchema",
- responses: {
- 200: {
- body: "TokenOnlyResponse",
- },
- 400: {
- body: "APIErrorResponse",
- },
- },
- }),
- async (req: Request, res: Response) => {
- const body = req.body as TotpDisableSchema;
+ "/",
+ route({
+ requestBody: "TotpDisableSchema",
+ responses: {
+ 200: {
+ body: "TokenOnlyResponse",
+ },
+ 400: {
+ body: "APIErrorResponse",
+ },
+ },
+ }),
+ async (req: Request, res: Response) => {
+ const body = req.body as TotpDisableSchema;
- const user = await User.findOneOrFail({
- where: { id: req.user_id },
- select: ["totp_secret"],
- });
+ const user = await User.findOneOrFail({
+ where: { id: req.user_id },
+ select: ["totp_secret"],
+ });
- const backup = await BackupCode.findOne({ where: { code: body.code } });
- if (!backup) {
- const ret = verifyToken(user.totp_secret || "", body.code);
- if (!ret || ret.delta != 0) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
- }
+ const backup = await BackupCode.findOne({ where: { code: body.code } });
+ if (!backup) {
+ const ret = verifyToken(user.totp_secret || "", body.code);
+ if (!ret || ret.delta != 0) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
+ }
- await User.update(
- { id: req.user_id },
- {
- mfa_enabled: false,
- totp_secret: "",
- },
- );
+ await User.update(
+ { id: req.user_id },
+ {
+ mfa_enabled: false,
+ totp_secret: "",
+ },
+ );
- await BackupCode.update(
- { user: { id: req.user_id } },
- {
- expired: true,
- },
- );
+ await BackupCode.update(
+ { user: { id: req.user_id } },
+ {
+ expired: true,
+ },
+ );
- return res.json({
- token: await generateToken(user.id),
- });
- },
+ return res.json({
+ token: await generateToken(user.id),
+ });
+ },
);
export default router;
diff --git a/src/api/routes/users/@me/mfa/totp/enable.ts b/src/api/routes/users/@me/mfa/totp/enable.ts
index f0563e30..a400c628 100644
--- a/src/api/routes/users/@me/mfa/totp/enable.ts
+++ b/src/api/routes/users/@me/mfa/totp/enable.ts
@@ -27,54 +27,54 @@ import { TotpEnableSchema } from "@spacebar/schemas";
const router = Router({ mergeParams: true });
router.post(
- "/",
- route({
- requestBody: "TotpEnableSchema",
- responses: {
- 200: {
- body: "TokenWithBackupCodesResponse",
- },
- 400: {
- body: "APIErrorResponse",
- },
- 404: {
- body: "APIErrorResponse",
- },
- },
- }),
- async (req: Request, res: Response) => {
- const body = req.body as TotpEnableSchema;
+ "/",
+ route({
+ requestBody: "TotpEnableSchema",
+ responses: {
+ 200: {
+ body: "TokenWithBackupCodesResponse",
+ },
+ 400: {
+ body: "APIErrorResponse",
+ },
+ 404: {
+ body: "APIErrorResponse",
+ },
+ },
+ }),
+ async (req: Request, res: Response) => {
+ const body = req.body as TotpEnableSchema;
- const user = await User.findOneOrFail({
- where: { id: req.user_id },
- select: ["data", "email"],
- });
+ const user = await User.findOneOrFail({
+ where: { id: req.user_id },
+ select: ["data", "email"],
+ });
- // TODO: Are guests allowed to enable 2fa?
- if (user.data.hash) {
- if (!(await bcrypt.compare(body.password, user.data.hash))) {
- throw new HTTPError(req.t("auth:login.INVALID_PASSWORD"));
- }
- }
+ // TODO: Are guests allowed to enable 2fa?
+ if (user.data.hash) {
+ if (!(await bcrypt.compare(body.password, user.data.hash))) {
+ throw new HTTPError(req.t("auth:login.INVALID_PASSWORD"));
+ }
+ }
- if (!body.secret) throw new HTTPError(req.t("auth:login.INVALID_TOTP_SECRET"), 60005);
+ if (!body.secret) throw new HTTPError(req.t("auth:login.INVALID_TOTP_SECRET"), 60005);
- if (!body.code) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
+ if (!body.code) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
- if (verifyToken(body.secret, body.code)?.delta != 0) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
+ if (verifyToken(body.secret, body.code)?.delta != 0) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
- const backup_codes = generateMfaBackupCodes(req.user_id);
- await Promise.all(backup_codes.map((x) => x.save()));
- await User.update({ id: req.user_id }, { mfa_enabled: true, totp_secret: body.secret });
+ const backup_codes = generateMfaBackupCodes(req.user_id);
+ await Promise.all(backup_codes.map((x) => x.save()));
+ await User.update({ id: req.user_id }, { mfa_enabled: true, totp_secret: body.secret });
- res.send({
- token: await generateToken(user.id),
- backup_codes: backup_codes.map((x) => ({
- ...x,
- expired: undefined,
- })),
- });
- },
+ res.send({
+ token: await generateToken(user.id),
+ backup_codes: backup_codes.map((x) => ({
+ ...x,
+ expired: undefined,
+ })),
+ });
+ },
);
export default router;
diff --git a/src/api/routes/users/@me/mfa/webauthn/credentials/#key_id/index.ts b/src/api/routes/users/@me/mfa/webauthn/credentials/#key_id/index.ts
index 6dc06563..77180cac 100644
--- a/src/api/routes/users/@me/mfa/webauthn/credentials/#key_id/index.ts
+++ b/src/api/routes/users/@me/mfa/webauthn/credentials/#key_id/index.ts
@@ -22,29 +22,29 @@ import { Request, Response, Router } from "express";
const router = Router({ mergeParams: true });
router.delete(
- "/",
- route({
- responses: {
- 204: {},
- },
- }),
- async (req: Request, res: Response) => {
- const { key_id } = req.params;
+ "/",
+ route({
+ responses: {
+ 204: {},
+ },
+ }),
+ async (req: Request, res: Response) => {
+ const { key_id } = req.params;
- await SecurityKey.delete({
- id: key_id,
- user_id: req.user_id,
- });
+ await SecurityKey.delete({
+ id: key_id,
+ user_id: req.user_id,
+ });
- const keys = await SecurityKey.count({
- where: { user_id: req.user_id },
- });
+ const keys = await SecurityKey.count({
+ where: { user_id: req.user_id },
+ });
- // disable webauthn if there are no keys left
- if (keys === 0) await User.update({ id: req.user_id }, { webauthn_enabled: false });
+ // disable webauthn if there are no keys left
+ if (keys === 0) await User.update({ id: req.user_id }, { webauthn_enabled: false });
- res.sendStatus(204);
- },
+ res.sendStatus(204);
+ },
);
export default router;
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 0c4b733d..7bd29a57 100644
--- a/src/api/routes/users/@me/mfa/webauthn/credentials/index.ts
+++ b/src/api/routes/users/@me/mfa/webauthn/credentials/index.ts
@@ -26,138 +26,138 @@ import { CreateWebAuthnCredentialSchema, GenerateWebAuthnCredentialsSchema, WebA
const router = Router({ mergeParams: true });
const isGenerateSchema = (body: WebAuthnPostSchema): body is GenerateWebAuthnCredentialsSchema => {
- return "password" in body;
+ return "password" in body;
};
const isCreateSchema = (body: WebAuthnPostSchema): body is CreateWebAuthnCredentialSchema => {
- return "credential" in body;
+ return "credential" in body;
};
function toArrayBuffer(buf: Buffer) {
- const ab = new ArrayBuffer(buf.length);
- const view = new Uint8Array(ab);
- for (let i = 0; i < buf.length; ++i) {
- view[i] = buf[i];
- }
- return ab;
+ const ab = new ArrayBuffer(buf.length);
+ const view = new Uint8Array(ab);
+ for (let i = 0; i < buf.length; ++i) {
+ view[i] = buf[i];
+ }
+ return ab;
}
router.get("/", route({}), async (req: Request, res: Response) => {
- const securityKeys = await SecurityKey.find({
- where: {
- user_id: req.user_id,
- },
- });
+ const securityKeys = await SecurityKey.find({
+ where: {
+ user_id: req.user_id,
+ },
+ });
- return res.json(
- securityKeys.map((key) => ({
- id: key.id,
- name: key.name,
- })),
- );
+ return res.json(
+ securityKeys.map((key) => ({
+ id: key.id,
+ name: key.name,
+ })),
+ );
});
router.post(
- "/",
- route({
- requestBody: "WebAuthnPostSchema",
- responses: {
- 200: {
- body: "WebAuthnCreateResponse",
- },
- 400: {
- body: "APIErrorResponse",
- },
- },
- }),
- async (req: Request, res: Response) => {
- if (!WebAuthn.fido2) {
- // TODO: I did this for typescript and I can't use !
- throw new Error("WebAuthn not enabled");
- }
+ "/",
+ route({
+ requestBody: "WebAuthnPostSchema",
+ responses: {
+ 200: {
+ body: "WebAuthnCreateResponse",
+ },
+ 400: {
+ body: "APIErrorResponse",
+ },
+ },
+ }),
+ async (req: Request, res: Response) => {
+ if (!WebAuthn.fido2) {
+ // TODO: I did this for typescript and I can't use !
+ throw new Error("WebAuthn not enabled");
+ }
- const user = await User.findOneOrFail({
- where: {
- id: req.user_id,
- },
- select: ["data", "id", "disabled", "deleted", "totp_secret", "mfa_enabled", "username"],
- relations: ["settings"],
- });
+ const user = await User.findOneOrFail({
+ where: {
+ id: req.user_id,
+ },
+ select: ["data", "id", "disabled", "deleted", "totp_secret", "mfa_enabled", "username"],
+ relations: ["settings"],
+ });
- if (isGenerateSchema(req.body)) {
- const { password } = req.body;
- const same_password = await bcrypt.compare(password, user.data.hash || "");
- if (!same_password) {
- throw FieldErrors({
- password: {
- message: req.t("auth:login.INVALID_PASSWORD"),
- code: "INVALID_PASSWORD",
- },
- });
- }
+ if (isGenerateSchema(req.body)) {
+ const { password } = req.body;
+ const same_password = await bcrypt.compare(password, user.data.hash || "");
+ if (!same_password) {
+ throw FieldErrors({
+ password: {
+ message: req.t("auth:login.INVALID_PASSWORD"),
+ code: "INVALID_PASSWORD",
+ },
+ });
+ }
- const registrationOptions = await WebAuthn.fido2.attestationOptions();
- const challenge = JSON.stringify({
- publicKey: {
- ...registrationOptions,
- challenge: Buffer.from(registrationOptions.challenge).toString("base64"),
- user: {
- id: user.id,
- name: user.username,
- displayName: user.username,
- },
- },
- });
+ const registrationOptions = await WebAuthn.fido2.attestationOptions();
+ const challenge = JSON.stringify({
+ publicKey: {
+ ...registrationOptions,
+ challenge: Buffer.from(registrationOptions.challenge).toString("base64"),
+ user: {
+ id: user.id,
+ name: user.username,
+ displayName: user.username,
+ },
+ },
+ });
- const ticket = await generateWebAuthnTicket(challenge);
+ const ticket = await generateWebAuthnTicket(challenge);
- return res.json({
- ticket: ticket,
- challenge,
- });
- } else if (isCreateSchema(req.body)) {
- const { credential, name, ticket } = req.body;
+ return res.json({
+ ticket: ticket,
+ challenge,
+ });
+ } else if (isCreateSchema(req.body)) {
+ const { credential, name, ticket } = req.body;
- const verified = await verifyWebAuthnToken(ticket);
- if (!verified) throw new HTTPError("Invalid ticket", 400);
+ const verified = await verifyWebAuthnToken(ticket);
+ if (!verified) throw new HTTPError("Invalid ticket", 400);
- const clientAttestationResponse = JSON.parse(credential);
+ const clientAttestationResponse = JSON.parse(credential);
- if (!clientAttestationResponse.rawId) throw new HTTPError("Missing rawId", 400);
+ if (!clientAttestationResponse.rawId) throw new HTTPError("Missing rawId", 400);
- const rawIdBuffer = Buffer.from(clientAttestationResponse.rawId, "base64");
- clientAttestationResponse.rawId = toArrayBuffer(rawIdBuffer);
+ const rawIdBuffer = Buffer.from(clientAttestationResponse.rawId, "base64");
+ clientAttestationResponse.rawId = toArrayBuffer(rawIdBuffer);
- const attestationExpectations: ExpectedAttestationResult = JSON.parse(Buffer.from(clientAttestationResponse.response.clientDataJSON, "base64").toString());
+ const attestationExpectations: ExpectedAttestationResult = JSON.parse(Buffer.from(clientAttestationResponse.response.clientDataJSON, "base64").toString());
- const regResult = await WebAuthn.fido2.attestationResult(clientAttestationResponse, {
- ...attestationExpectations,
- factor: "second",
- });
+ const regResult = await WebAuthn.fido2.attestationResult(clientAttestationResponse, {
+ ...attestationExpectations,
+ factor: "second",
+ });
- const authnrData = regResult.authnrData;
- const keyId = Buffer.from(authnrData.get("credId")).toString("base64");
- const counter = authnrData.get("counter");
- const publicKey = authnrData.get("credentialPublicKeyPem");
+ const authnrData = regResult.authnrData;
+ const keyId = Buffer.from(authnrData.get("credId")).toString("base64");
+ const counter = authnrData.get("counter");
+ const publicKey = authnrData.get("credentialPublicKeyPem");
- const securityKey = SecurityKey.create({
- name,
- counter,
- public_key: publicKey,
- user_id: req.user_id,
- key_id: keyId,
- });
+ const securityKey = SecurityKey.create({
+ name,
+ counter,
+ public_key: publicKey,
+ user_id: req.user_id,
+ key_id: keyId,
+ });
- await Promise.all([securityKey.save(), User.update({ id: req.user_id }, { webauthn_enabled: true })]);
+ await Promise.all([securityKey.save(), User.update({ id: req.user_id }, { webauthn_enabled: true })]);
- return res.json({
- name,
- id: securityKey.id,
- });
- } else {
- throw DiscordApiErrors.INVALID_AUTHENTICATION_TOKEN;
- }
- },
+ return res.json({
+ name,
+ id: securityKey.id,
+ });
+ } else {
+ throw DiscordApiErrors.INVALID_AUTHENTICATION_TOKEN;
+ }
+ },
);
export default router;
|