implemented login
This commit is contained in:
parent
2feb97ddfe
commit
db44350bc9
|
|
@ -23,3 +23,12 @@ export const enum RegisterResponseCause {
|
|||
PasswordLength,
|
||||
DisplayNameLength
|
||||
}
|
||||
|
||||
export const enum LoginResponseCause {
|
||||
Server = 1,
|
||||
MalformedRequest,
|
||||
EmailLength,
|
||||
PasswordLength,
|
||||
NotFound,
|
||||
Timeout
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,80 @@
|
|||
import Bun from "bun"
|
||||
import Crypto from "node:crypto"
|
||||
import { Prisma } from "@prisma/client"
|
||||
|
||||
import { ArgumentError, DuplicateError } from "$lib/errors"
|
||||
|
||||
import Log from "$lib/server/log"
|
||||
import db from "$lib/server/database"
|
||||
import { Prisma } from "@prisma/client"
|
||||
|
||||
const SESSION_LIFETIME = 15 * 60 * 1000
|
||||
|
||||
export type User = Prisma.UserGetPayload<Prisma.UserDefaultArgs>
|
||||
|
||||
export interface SessionData {
|
||||
user: User
|
||||
token: string
|
||||
issued: Date
|
||||
expires: Date
|
||||
}
|
||||
|
||||
|
||||
interface CacheUserInfo {
|
||||
user: User
|
||||
last_update: Date
|
||||
}
|
||||
|
||||
interface CacheSessionInfo {
|
||||
user_id: number
|
||||
issued: Date
|
||||
expires: Date
|
||||
}
|
||||
|
||||
class Cache {
|
||||
private _id_map: Map<number, CacheUserInfo> = new Map()
|
||||
private _session_map: Map<string, CacheSessionInfo> = new Map()
|
||||
|
||||
add(user: User, token: string, issued: Date, expires: Date): SessionData {
|
||||
this._id_map.set(user.id, {
|
||||
user: user,
|
||||
last_update: new Date()
|
||||
})
|
||||
this._session_map.set(token, {
|
||||
user_id: user.id,
|
||||
issued: issued,
|
||||
expires: expires
|
||||
})
|
||||
|
||||
return {
|
||||
user: user,
|
||||
token: token,
|
||||
issued: issued,
|
||||
expires: expires
|
||||
}
|
||||
}
|
||||
|
||||
get_session(token: string): SessionData|null {
|
||||
const session_info = this._session_map.get(token)
|
||||
if (!session_info) return null
|
||||
|
||||
const user_info = this._id_map.get(session_info.user_id)
|
||||
if (!user_info) return null
|
||||
|
||||
return {
|
||||
user: user_info.user,
|
||||
token: token,
|
||||
issued: session_info.issued,
|
||||
expires: session_info.expires
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class UserMgmt {
|
||||
_cache: Cache = new Cache()
|
||||
|
||||
async register(email: string, password: string, display_name: string) {
|
||||
async register(email: string, password: string, display_name: string): Promise<User|null> {
|
||||
if (email.length == 0 || password.length == 0 || display_name.length == 0) {
|
||||
throw new ArgumentError("No field may be empty")
|
||||
}
|
||||
|
|
@ -23,7 +89,6 @@ class UserMgmt {
|
|||
})
|
||||
|
||||
Log.info(Log.module.USER, `Created user with id ${user.id}`)
|
||||
console.log(user)
|
||||
|
||||
return user;
|
||||
} catch (e) {
|
||||
|
|
@ -37,16 +102,32 @@ class UserMgmt {
|
|||
}
|
||||
}
|
||||
|
||||
async login(email: string, password: string) {
|
||||
async login(email: string, password: string): Promise<SessionData|null> {
|
||||
const user = await db.user.findUnique({
|
||||
where: {
|
||||
email: email
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
if (!user) {
|
||||
// throw of timing attacks
|
||||
await Bun.password.verify("a", "$argon2id$v=19$m=16,t=2,p=1$ZHB6Zjd4NXV6RXZBZk9wRg$QaYjeAGLon+x3c5I1KB7UQ")
|
||||
return null
|
||||
}
|
||||
|
||||
if (!await Bun.password.verify(password, user.password_hash)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const token = Crypto.randomBytes(32).toBase64()
|
||||
const session_info = this._cache.add(user, token, new Date(), new Date(Date.now() + SESSION_LIFETIME))
|
||||
|
||||
return session_info
|
||||
}
|
||||
|
||||
async session_login(token: string): Promise<SessionData|null> {
|
||||
return this._cache.get_session(token)
|
||||
}
|
||||
}
|
||||
|
||||
const _manager = new UserMgmt()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
import type { RequestHandler } from "./$types"
|
||||
|
||||
import { error, json, text } from "@sveltejs/kit"
|
||||
|
||||
import UserMgmt from "$lib/server/usermgmt"
|
||||
|
||||
import { LoginResponseCause } from "$lib/errors"
|
||||
|
||||
export const POST: RequestHandler = async ({ request, cookies }) => {
|
||||
|
||||
const data = await request.formData()
|
||||
|
||||
const email = data.get("email")
|
||||
const password = data.get("password")
|
||||
|
||||
if (data.keys.length > 2 || !(typeof email === "string" && typeof password === "string")) {
|
||||
return error(400, { cause: LoginResponseCause.MalformedRequest, message: "Invalid request" })
|
||||
}
|
||||
|
||||
if (email.length == 0) {
|
||||
return error(400, { cause: LoginResponseCause.EmailLength, message: "Email must be provided" })
|
||||
}
|
||||
if (password.length == 0) {
|
||||
return error(400, { cause: LoginResponseCause.PasswordLength, message: "Password must be provided" })
|
||||
}
|
||||
|
||||
const session = await UserMgmt.login(email, password)
|
||||
if (!session) {
|
||||
return error(401, { message: "Invalid username or password" })
|
||||
}
|
||||
|
||||
return json({ token: session.token, expires: session.expires })
|
||||
}
|
||||
Loading…
Reference in New Issue