diff --git a/packages/backend/src/server/api/web/controllers/auth.ts b/packages/backend/src/server/api/web/controllers/auth.ts index 8b87f2f31..c5e5b0e32 100644 --- a/packages/backend/src/server/api/web/controllers/auth.ts +++ b/packages/backend/src/server/api/web/controllers/auth.ts @@ -1,16 +1,10 @@ import { Controller, Get, Post, Body, CurrentUser, Flow } from "@iceshrimp/koa-openapi"; import type { ILocalUser } from "@/models/entities/user.js"; -import { UserHandler } from "@/server/api/web/handlers/user.js"; import type { AuthRequest, AuthResponse } from "@/server/api/web/entities/auth.js"; import type { Session } from "@/models/entities/session.js"; import { RatelimitRouteMiddleware } from "@/server/api/web/middleware/rate-limit.js"; import { CurrentSession } from "@/server/api/web/misc/decorators.js"; -import { Sessions, UserProfiles, Users } from "@/models/index.js"; -import { unauthorized, badRequest } from "@hapi/boom"; -import { comparePassword } from "@/misc/password.js"; -import { IsNull } from "typeorm"; -import { genId } from "@/misc/gen-id.js"; -import { secureRndstr } from "@/misc/secure-rndstr.js"; +import { AuthHandler } from "@/server/api/web/handlers/auth.js"; @Controller('/auth') export class AuthController { @@ -19,37 +13,12 @@ export class AuthController { @CurrentUser() me: ILocalUser | null, @CurrentSession() session: Session | null, ): Promise { - const user = me ? await UserHandler.getUser(me, me.id) : null; - return { - status: !user ? 'guest' : session?.active ? 'authenticated' : '2fa', - token: session?.token ?? null, - user: user, - }; + return AuthHandler.getAuthStatus(me, session); } @Post('/') @Flow([RatelimitRouteMiddleware("auth", 10, 60000, true)]) async login(@Body({ required: true }) request: AuthRequest): Promise { - if (request.username == null || request.password == null) throw badRequest("Missing username or password"); - - const user = await Users.findOneBy({ usernameLower: request.username.toLowerCase(), host: IsNull() }); - if (!user) throw unauthorized("Invalid username or password"); - - const profile = await UserProfiles.findOneBy( { userId: user.id }); - if (!profile || profile.password == null) throw unauthorized("Invalid username or password"); - - if (!await comparePassword(request.password, profile.password)) throw unauthorized("Invalid username or password"); - - const result = await Sessions.insert({ - id: genId(), - createdAt: new Date(), - active: !profile.twoFactorEnabled, - userId: user.id, - token: secureRndstr(32), - }); - - const session = await Sessions.findOneByOrFail(result.identifiers[0]); - - return this.getAuthStatus(user as ILocalUser, session); + return AuthHandler.login(request); } } diff --git a/packages/backend/src/server/api/web/handlers/auth.ts b/packages/backend/src/server/api/web/handlers/auth.ts new file mode 100644 index 000000000..8207ff5e9 --- /dev/null +++ b/packages/backend/src/server/api/web/handlers/auth.ts @@ -0,0 +1,45 @@ +import { ILocalUser } from "@/models/entities/user.js"; +import { Session } from "@/models/entities/session.js"; +import { AuthRequest, AuthResponse } from "@/server/api/web/entities/auth.js"; +import { UserHandler } from "@/server/api/web/handlers/user.js"; +import { Sessions, UserProfiles, Users } from "@/models/index.js"; +import { comparePassword } from "@/misc/password.js"; +import { genId } from "@/misc/gen-id.js"; +import { secureRndstr } from "@/misc/secure-rndstr.js"; +import { unauthorized, badRequest } from "@hapi/boom"; +import { IsNull } from "typeorm"; + +export class AuthHandler { + public static async getAuthStatus(me: ILocalUser | null, session: Session | null): Promise { + const user = me ? await UserHandler.getUser(me, me.id) : null; + return { + status: !user ? 'guest' : session?.active ? 'authenticated' : '2fa', + token: session?.token ?? null, + user: user, + }; + } + + public static async login(request: AuthRequest): Promise { + if (request.username == null || request.password == null) throw badRequest("Missing username or password"); + + const user = await Users.findOneBy({ usernameLower: request.username.toLowerCase(), host: IsNull() }); + if (!user) throw unauthorized("Invalid username or password"); + + const profile = await UserProfiles.findOneBy( { userId: user.id }); + if (!profile || profile.password == null) throw unauthorized("Invalid username or password"); + + if (!await comparePassword(request.password, profile.password)) throw unauthorized("Invalid username or password"); + + const result = await Sessions.insert({ + id: genId(), + createdAt: new Date(), + active: !profile.twoFactorEnabled, + userId: user.id, + token: secureRndstr(32), + }); + + const session = await Sessions.findOneByOrFail(result.identifiers[0]); + + return this.getAuthStatus(user as ILocalUser, session); + } +}