From cd4738377bb5755bdaf37467bf13d01a18dc97df Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Tue, 27 Jun 2023 06:18:19 +0300 Subject: [PATCH] implement login --- src/routers/UserRouter.ts | 28 ++++++++++++++++++++++++---- src/server/decorators/jwt.ts | 17 ++++++++++++++--- src/{ => utils}/JWT.ts | 0 src/utils/now.ts | 3 +++ 4 files changed, 41 insertions(+), 7 deletions(-) rename src/{ => utils}/JWT.ts (100%) create mode 100644 src/utils/now.ts diff --git a/src/routers/UserRouter.ts b/src/routers/UserRouter.ts index d13bb15..1a5e3e7 100644 --- a/src/routers/UserRouter.ts +++ b/src/routers/UserRouter.ts @@ -1,10 +1,13 @@ import { Collection } from "https://deno.land/x/mongo@v0.31.2/mod.ts"; import * as bcrypt from "https://deno.land/x/bcrypt@v0.4.1/mod.ts"; -import { body, rest, schema } from "../server/decorators.ts"; +import { auth, body, rest, schema } from "../server/decorators.ts"; import HttpError from "../server/HttpError.ts"; import User from "../models/User.ts"; import AppRouter from "./AppRouter.ts"; +import jwt from "../server/decorators/jwt.ts"; +import JWT, { JWTPayload } from "../utils/JWT.ts"; +import now from "../utils/now.ts"; export interface SignupRequest { username: string; @@ -16,7 +19,7 @@ export interface LoginRequest { } export default class UserRouter extends AppRouter { - @rest('GET', '/:username') + @rest('GET', '/') async get(@schema('string') username: string) { const res = await this.users.findOne({ username }); @@ -24,6 +27,15 @@ export default class UserRouter extends AppRouter { return { username: res.username }; } + @rest('GET', '/self') + async self(@jwt(self => self.salt, true) @auth() auth: JWTPayload) { + if (auth === undefined) throw new HttpError('You are not logged in.'); + const res = await this.users.findOne({ username: auth.name }); + + if (res === undefined) throw new HttpError('User not found.'); + + return { username: res.username }; + } @rest('POST', '/signup') async signup( @@ -54,8 +66,16 @@ export default class UserRouter extends AppRouter { ) { const res = await this.users.findOne({ username: body.username }); if (!res) throw new HttpError('Incorrect username or password.'); - bcrypt.hash(res.password, this.salt); - + const hashed = await bcrypt.hash(res.password, this.salt); + + if (res.password !== hashed) throw new HttpError('Incorrect username or password.'); + + const time = now(); + return JWT.encode({ + iat: time, + exp: time + 3600 * 12, + name: res.username, + }, this.salt); } public constructor(private salt: string, private users: Collection) { diff --git a/src/server/decorators/jwt.ts b/src/server/decorators/jwt.ts index 9b174bb..0530d1f 100644 --- a/src/server/decorators/jwt.ts +++ b/src/server/decorators/jwt.ts @@ -1,10 +1,21 @@ -import JWT from "../../JWT.ts"; +import JWT from "../../utils/JWT.ts"; +import HttpError from "../HttpError.ts"; import Router, { makeParameterModifier } from "../Router.ts"; -export default function jwt(salt: ((self: T) => string) | keyof T) { +export default function jwt(salt: ((self: T) => string) | keyof T, required = false) { return makeParameterModifier(function (_req, val?: string) { if (val === undefined) return undefined; const s = typeof salt === 'function' ? salt(this) : this[salt] as string; - return JWT.decode(val, s); + try { + const res = JWT.decode(val, s); + if (required && res === undefined) throw new HttpError('You are not logged in.'); + return res; + } + catch (e) { + if (e instanceof Error && !(e instanceof HttpError)) { + throw new HttpError(e.message, 400); + } + else throw e; + } }); } \ No newline at end of file diff --git a/src/JWT.ts b/src/utils/JWT.ts similarity index 100% rename from src/JWT.ts rename to src/utils/JWT.ts diff --git a/src/utils/now.ts b/src/utils/now.ts new file mode 100644 index 0000000..50379df --- /dev/null +++ b/src/utils/now.ts @@ -0,0 +1,3 @@ +export default function now() { + return new Date().getTime() / 1000; +} \ No newline at end of file