auth routes

This commit is contained in:
Puyodead1 2023-03-23 10:40:37 -04:00
parent 174d34c376
commit a567ca3f51
No known key found for this signature in database
GPG Key ID: A4FA4FEC0DD353FC
25 changed files with 25602 additions and 9824 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -147,26 +147,26 @@ function apiRoutes() {
}.merge(obj.requestBody);
}
if (route.test?.response) {
const status = route.test.response.status || 200;
if (route.responses) {
for (const [k, v] of Object.entries(route.responses)) {
let schema = {
allOf: [
{
$ref: `#/components/schemas/${route.test.response.body}`,
$ref: `#/components/schemas/${v.body}`,
},
{
example: route.test.body,
example: v.body,
},
],
};
if (!route.test.body) schema = schema.allOf[0];
if (!v.body) schema = schema.allOf[0];
obj.responses = {
[status]: {
...(route.test.response.body
[k]: {
...(v.body
? {
description:
obj?.responses?.[status]?.description || "",
obj?.responses?.[k]?.description || "",
content: {
"application/json": {
schema: schema,
@ -178,6 +178,7 @@ function apiRoutes() {
}.merge(obj.responses);
delete obj.responses.default;
}
}
if (p.includes(":")) {
obj.parameters = p.match(/:\w+/g)?.map((x) => ({
name: x.replace(":", ""),

View File

@ -30,7 +30,18 @@ const router = Router();
router.post(
"/",
route({ body: "ForgotPasswordSchema" }),
route({
body: "ForgotPasswordSchema",
responses: {
204: {},
400: {
body: "APIErrorResponse",
},
500: {
body: "APIErrorResponse",
},
},
}),
async (req: Request, res: Response) => {
const { login, captcha_key } = req.body as ForgotPasswordSchema;

View File

@ -16,7 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { route, random } from "@spacebar/api";
import { random, route } from "@spacebar/api";
import { Config, ValidRegistrationToken } from "@spacebar/util";
import { Request, Response, Router } from "express";
@ -25,7 +25,10 @@ export default router;
router.get(
"/",
route({ right: "OPERATOR" }),
route({
right: "OPERATOR",
responses: { 200: { body: "GenerateRegistrationTokensResponse" } },
}),
async (req: Request, res: Response) => {
const count = req.query.count ? parseInt(req.query.count as string) : 1;
const length = req.query.length

View File

@ -16,12 +16,20 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Router, Request, Response } from "express";
import { route } from "@spacebar/api";
import { getIpAdress, IPAnalysis } from "@spacebar/api";
import { IPAnalysis, getIpAdress, route } from "@spacebar/api";
import { Request, Response, Router } from "express";
const router = Router();
router.get("/", route({}), async (req: Request, res: Response) => {
router.get(
"/",
route({
responses: {
200: {
body: "LocationMetadataResponse",
},
},
}),
async (req: Request, res: Response) => {
//TODO
//Note: It's most likely related to legal. At the moment Discord hasn't finished this too
const country_code = (await IPAnalysis(getIpAdress(req))).country_code;
@ -30,6 +38,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
country_code: country_code,
promotional_email_opt_in: { required: true, pre_checked: false },
});
});
},
);
export default router;

View File

@ -36,7 +36,17 @@ export default router;
router.post(
"/",
route({ body: "LoginSchema" }),
route({
body: "LoginSchema",
responses: {
200: {
body: "TokenResponse",
},
400: {
body: "APIErrorOrCaptchaResponse",
},
},
}),
async (req: Request, res: Response) => {
const { login, password, captcha_key, undelete } =
req.body as LoginSchema;

View File

@ -22,9 +22,19 @@ import { Request, Response, Router } from "express";
const router: Router = Router();
export default router;
router.post("/", route({}), async (req: Request, res: Response) => {
router.post(
"/",
route({
responses: {
204: {},
},
}),
async (req: Request, res: Response) => {
if (req.body.provider != null || req.body.voip_provider != null) {
console.log(`[LOGOUT]: provider or voip provider not null!`, req.body);
console.log(
`[LOGOUT]: provider or voip provider not null!`,
req.body,
);
} else {
delete req.body.provider;
delete req.body.voip_provider;
@ -32,4 +42,5 @@ router.post("/", route({}), async (req: Request, res: Response) => {
console.log(`[LOGOUT]: Extra fields sent in logout!`, req.body);
}
res.status(204).send();
});
},
);

View File

@ -16,16 +16,26 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Router, Request, Response } from "express";
import { route } from "@spacebar/api";
import { BackupCode, generateToken, User, TotpSchema } from "@spacebar/util";
import { verifyToken } from "node-2fa";
import { BackupCode, TotpSchema, User, generateToken } from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { verifyToken } from "node-2fa";
const router = Router();
router.post(
"/",
route({ body: "TotpSchema" }),
route({
body: "TotpSchema",
responses: {
200: {
body: "TokenResponse",
},
400: {
body: "APIErrorResponse",
},
},
}),
async (req: Request, res: Response) => {
// const { code, ticket, gift_code_sku_id, login_source } =
const { code, ticket } = req.body as TotpSchema;

View File

@ -41,7 +41,13 @@ function toArrayBuffer(buf: Buffer) {
router.post(
"/",
route({ body: "WebAuthnTotpSchema" }),
route({
body: "WebAuthnTotpSchema",
responses: {
200: { body: "TokenResponse" },
400: { body: "APIErrorResponse" },
},
}),
async (req: Request, res: Response) => {
if (!WebAuthn.fido2) {
// TODO: I did this for typescript and I can't use !

View File

@ -42,7 +42,13 @@ const router: Router = Router();
router.post(
"/",
route({ body: "RegisterSchema" }),
route({
body: "RegisterSchema",
responses: {
200: { body: "TokenResponse" },
400: { body: "APIErrorOrCaptchaResponse" },
},
}),
async (req: Request, res: Response) => {
const body = req.body as RegisterSchema;
const { register, security, limits } = Config.get();

View File

@ -31,9 +31,20 @@ import { Request, Response, Router } from "express";
const router = Router();
// TODO: the response interface also returns settings, but this route doesn't actually return that.
router.post(
"/",
route({ body: "PasswordResetSchema" }),
route({
body: "PasswordResetSchema",
responses: {
200: {
body: "TokenResponse",
},
400: {
body: "APIErrorOrCaptchaResponse",
},
},
}),
async (req: Request, res: Response) => {
const { password, token } = req.body as PasswordResetSchema;

View File

@ -37,9 +37,20 @@ async function getToken(user: User) {
return { token };
}
// TODO: the response interface also returns settings, but this route doesn't actually return that.
router.post(
"/",
route({ body: "VerifyEmailSchema" }),
route({
body: "VerifyEmailSchema",
responses: {
200: {
body: "TokenResponse",
},
400: {
body: "APIErrorOrCaptchaResponse",
},
},
}),
async (req: Request, res: Response) => {
const { captcha_key, token } = req.body;

View File

@ -24,7 +24,14 @@ const router = Router();
router.post(
"/",
route({ right: "RESEND_VERIFICATION_EMAIL" }),
route({
right: "RESEND_VERIFICATION_EMAIL",
responses: {
204: {},
400: {},
500: {},
},
}),
async (req: Request, res: Response) => {
const user = await User.findOneOrFail({
where: { id: req.user_id },

View File

@ -16,15 +16,21 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Router, Request, Response } from "express";
import { route } from "@spacebar/api";
import { FieldErrors, User, BackupCodesChallengeSchema } from "@spacebar/util";
import { BackupCodesChallengeSchema, FieldErrors, User } from "@spacebar/util";
import bcrypt from "bcrypt";
import { Request, Response, Router } from "express";
const router = Router();
router.post(
"/",
route({ body: "BackupCodesChallengeSchema" }),
route({
body: "BackupCodesChallengeSchema",
responses: {
200: { body: "BackupCodesChallengeResponse" },
400: { body: "APIErrorResponse" },
},
}),
async (req: Request, res: Response) => {
const { password } = req.body as BackupCodesChallengeSchema;

View File

@ -17,21 +17,21 @@
*/
import {
ajv,
DiscordApiErrors,
EVENT,
FieldErrors,
SpacebarApiErrors,
getPermission,
getRights,
normalizeBody,
PermissionResolvable,
Permissions,
RightResolvable,
Rights,
SpacebarApiErrors,
ajv,
getPermission,
getRights,
normalizeBody,
} from "@spacebar/util";
import { NextFunction, Request, Response } from "express";
import { AnyValidateFunction } from "ajv/dist/core";
import { NextFunction, Request, Response } from "express";
declare global {
// TODO: fix this
@ -53,6 +53,11 @@ export interface RouteOptions {
permission?: PermissionResolvable;
right?: RightResolvable;
body?: `${string}Schema`; // typescript interface name
responses?: {
[status: number]: {
body?: `${string}Response`;
};
};
test?: {
response?: RouteResponse;
body?: unknown;

View File

@ -58,6 +58,7 @@ export * from "./PurgeSchema";
export * from "./RegisterSchema";
export * from "./RelationshipPostSchema";
export * from "./RelationshipPutSchema";
export * from "./responses";
export * from "./RoleModifySchema";
export * from "./RolePositionUpdateSchema";
export * from "./SelectProtocolSchema";

View File

@ -0,0 +1,6 @@
import { APIErrorResponse } from "./APIErrorResponse";
import { CaptchaRequiredResponse } from "./CaptchaRequiredResponse";
export type APIErrorOrCaptchaResponse =
| CaptchaRequiredResponse
| APIErrorResponse;

View File

@ -0,0 +1,12 @@
export interface APIErrorResponse {
code: number;
message: string;
errors: {
[key: string]: {
_errors: {
message: string;
code: string;
}[];
};
};
}

View File

@ -0,0 +1,4 @@
export interface BackupCodesChallengeResponse {
nonce: string;
regenerate_nonce: string;
}

View File

@ -0,0 +1,5 @@
export interface CaptchaRequiredResponse {
captcha_key: string;
captcha_sitekey: string;
captcha_service: string;
}

View File

@ -0,0 +1,3 @@
export interface GenerateRegistrationTokensResponse {
tokens: string[];
}

View File

@ -0,0 +1,5 @@
export interface LocationMetadataResponse {
consent_required: boolean;
country_code: string;
promotional_email_opt_in: { required: true; pre_checked: false };
}

View File

@ -0,0 +1,6 @@
import { UserSettings } from "../../entities";
export interface TokenResponse {
token: string;
settings: UserSettings;
}

View File

@ -0,0 +1,7 @@
export * from "./APIErrorOrCaptchaResponse";
export * from "./APIErrorResponse";
export * from "./BackupCodesChallengeResponse";
export * from "./CaptchaRequiredResponse";
export * from "./GenerateRegistrationTokensResponse";
export * from "./LocationMetadataResponse";
export * from "./TokenResponse";