implement session/auth token login
This commit is contained in:
@@ -29,27 +29,21 @@ function authenticated<Route extends string>(
|
||||
throw new HttpError(401)
|
||||
}
|
||||
|
||||
const user = findUserById(session.userId)
|
||||
if (!user) {
|
||||
throw new HttpError(401)
|
||||
}
|
||||
|
||||
const authTokenCookie = request.cookies.get("auth-token")
|
||||
if (authTokenCookie) {
|
||||
const deleteAuthTokenQuery = db.query("DELETE FROM auth_tokens WHERE id = $id")
|
||||
|
||||
// biome-ignore lint/style/noNonNullAssertion: the cookie has already been verified by verifySession previously, therefore the cookie must be in the correct format <token-id>:<token-signature>
|
||||
const tokenId = authTokenCookie.split(":")[0]!
|
||||
deleteAuthTokenQuery.run({ id: tokenId })
|
||||
rememberLoginForUser(user, request.cookies)
|
||||
rememberLoginForUser(session.user, request.cookies)
|
||||
}
|
||||
|
||||
if (user.id !== DEMO_USER.id) {
|
||||
if (session.user.id !== DEMO_USER.id) {
|
||||
const extendedSession = extendSession(session)
|
||||
saveSession(extendedSession, request.cookies)
|
||||
}
|
||||
|
||||
return handler(request, user)
|
||||
return handler(request, session.user)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -83,6 +77,48 @@ function rememberLoginForUser(user: User, cookies: Bun.CookieMap) {
|
||||
})
|
||||
}
|
||||
|
||||
async function verifyAuthToken(cookies: Bun.CookieMap): Promise<User | null> {
|
||||
const authTokenCookie = cookies.get("auth-token")
|
||||
if (!authTokenCookie) {
|
||||
return null
|
||||
}
|
||||
|
||||
const [tokenId, providedTokenEncoded] = authTokenCookie.split(":")
|
||||
if (!tokenId || !providedTokenEncoded) {
|
||||
return null
|
||||
}
|
||||
|
||||
const findAuthTokenQuery = db.query<
|
||||
{ id: string; token: string; user_id: string; expires_at_unix_ms: number },
|
||||
{ id: string }
|
||||
>("SELECT id, token, user_id, expires_at_unix_ms FROM auth_tokens WHERE id = $id")
|
||||
const result = findAuthTokenQuery.get({ id: tokenId })
|
||||
if (!result) {
|
||||
return null
|
||||
}
|
||||
|
||||
const providedToken = Buffer.from(providedTokenEncoded, "base64url")
|
||||
|
||||
const hasher = new Bun.CryptoHasher("sha256")
|
||||
hasher.update(providedToken)
|
||||
const hashedProvidedToken = hasher.digest()
|
||||
|
||||
const storedToken = Buffer.from(result.token, "base64url")
|
||||
|
||||
if (!crypto.timingSafeEqual(hashedProvidedToken, storedToken)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const user = findUserById(result.user_id)
|
||||
if (!user) {
|
||||
const query = db.query<void, { id: string }>("DELETE FROM auth_tokens WHERE id = $id")
|
||||
query.run({ id: result.id })
|
||||
return null
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
async function signUp(request: Bun.BunRequest<"/api/sign-up">) {
|
||||
const body = await request.json().catch(() => {
|
||||
throw new HttpError(500)
|
||||
@@ -104,8 +140,24 @@ async function signUp(request: Bun.BunRequest<"/api/sign-up">) {
|
||||
}
|
||||
|
||||
async function login(request: Bun.BunRequest<"/api/login">) {
|
||||
// first, check if there is an existing session
|
||||
const session = verifySession(request.cookies)
|
||||
if (session) {
|
||||
extendSession(session)
|
||||
return Response.json(session, { status: 200 })
|
||||
}
|
||||
|
||||
// then, check if there is a valid auth token
|
||||
{
|
||||
const foundUser = await verifyAuthToken(request.cookies)
|
||||
if (foundUser) {
|
||||
await createSessionForUser(foundUser, request.cookies)
|
||||
return Response.json(foundUser, { status: 200 })
|
||||
}
|
||||
}
|
||||
|
||||
const body = await request.json().catch(() => {
|
||||
throw new HttpError(500)
|
||||
throw new HttpError(401)
|
||||
})
|
||||
|
||||
const loginRequest = LoginRequest(body)
|
||||
@@ -127,7 +179,7 @@ async function login(request: Bun.BunRequest<"/api/login">) {
|
||||
throw new HttpError(401)
|
||||
}
|
||||
|
||||
const user: User = {
|
||||
const user = {
|
||||
id: foundUser.id,
|
||||
username: foundUser.username,
|
||||
}
|
||||
|
Reference in New Issue
Block a user