From f320118880654d60cfb2ba45bbb30d48f45eb717 Mon Sep 17 00:00:00 2001 From: Blake Matthes Date: Fri, 21 Feb 2025 21:54:26 -0600 Subject: [PATCH] fix(auth): found some bugs in the jwt token --- apiDocs/lstV2/Auth/Login.bru | 4 +- apiDocs/lstV2/Auth/Register.bru | 7 +- .../src/components/extendedUI/LstCard.tsx | 2 +- globals.d.ts | 10 ++ package.json | 1 + server/src/services/auth/controllers/login.ts | 41 ++++-- .../services/auth/controllers/verifyToken.ts | 6 +- server/src/services/auth/routes/login.ts | 30 +--- server/src/services/auth/routes/session.ts | 130 ++++++++++++------ .../src/services/auth/utils/checkPassword.ts | 10 ++ .../src/services/auth/utils/createPassword.ts | 4 +- 11 files changed, 149 insertions(+), 96 deletions(-) create mode 100644 globals.d.ts create mode 100644 server/src/services/auth/utils/checkPassword.ts diff --git a/apiDocs/lstV2/Auth/Login.bru b/apiDocs/lstV2/Auth/Login.bru index 71070ea..b0eec0e 100644 --- a/apiDocs/lstV2/Auth/Login.bru +++ b/apiDocs/lstV2/Auth/Login.bru @@ -12,7 +12,7 @@ post { body:json { { - "username": "admin", - "password": "password123" + "username": "adm_matthes", + "password": "nova0511" } } diff --git a/apiDocs/lstV2/Auth/Register.bru b/apiDocs/lstV2/Auth/Register.bru index 5eb4361..3f7be91 100644 --- a/apiDocs/lstV2/Auth/Register.bru +++ b/apiDocs/lstV2/Auth/Register.bru @@ -12,9 +12,8 @@ post { body:json { { - "username":"matthes02", - "email": "blake@alpla.com", - "password": "Vsd!134" - + "username": "adm_matthes", + "email":"blake@alpla.com", + "password": "nNova0511!" } } diff --git a/frontend/src/components/extendedUI/LstCard.tsx b/frontend/src/components/extendedUI/LstCard.tsx index 1f3ed83..167b7dd 100644 --- a/frontend/src/components/extendedUI/LstCard.tsx +++ b/frontend/src/components/extendedUI/LstCard.tsx @@ -10,7 +10,7 @@ interface LstCardProps { export function LstCard({children, className = "", style = {}}: LstCardProps) { return (
- + {children}
diff --git a/globals.d.ts b/globals.d.ts new file mode 100644 index 0000000..603cf74 --- /dev/null +++ b/globals.d.ts @@ -0,0 +1,10 @@ +declare global { + namespace NodeJS { + interface ProcessEnv { + JWT_SECRET: string; + JWT_EXPIRES: string; + } + } +} + +export {}; diff --git a/package.json b/package.json index 184db1f..28695a9 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@hono/zod-openapi": "^0.18.4", "@scalar/hono-api-reference": "^0.5.175", "@types/bun": "^1.2.2", + "@types/jsonwebtoken": "^9.0.8", "axios": "^1.7.9", "bcrypt": "^5.1.1", "compression": "^1.8.0", diff --git a/server/src/services/auth/controllers/login.ts b/server/src/services/auth/controllers/login.ts index 0031f87..cd3484a 100644 --- a/server/src/services/auth/controllers/login.ts +++ b/server/src/services/auth/controllers/login.ts @@ -1,28 +1,39 @@ import {sign, verify} from "jsonwebtoken"; +import {db} from "../../../../database/dbClient"; +import {users} from "../../../../database/schema/users"; +import {eq} from "drizzle-orm"; +import {checkPassword} from "../utils/checkPassword"; /** * Authenticate a user and return a JWT. */ -const fakeUsers = [ - {id: 1, username: "admin", password: "password123", role: "admin"}, - {id: 2, username: "user", password: "pass", role: "user"}, - {id: 3, username: "user2", password: "password123", role: "user"}, -]; - -export function login( +export async function login( username: string, password: string -): {token: string; user: {id: number; username: string; role: string}} { - const user = fakeUsers.find((u) => u.username === username && u.password === password); - if (!user) { - throw new Error("Invalid credentials"); +): Promise<{token: string; user: {user_id: string; username: string}}> { + const user = await db.select().from(users).where(eq(users.username, username)); + + if (user.length === 0) { + throw new Error("Invalid or Missing user"); + } + // check the password + const checkedPass = await checkPassword(password, user[0]?.password); + console.log(checkedPass); + if (!checkedPass) { + throw new Error("Invalid Password"); } // Create a JWT - const token = sign({user}, process.env.JWT_SECRET, { - expiresIn: process.env.JWT_EXPIRES, - }); + const secret: string = process.env.JWT_SECRET! || "bnghsjhsd"; + const expiresIn: string = process.env.JWT_EXPIRES! || "1h"; - return {token, user: {id: user?.id, username: user.username, role: user.role}}; + const userData = { + user_id: user[0].user_id, + username: user[0].username, + email: user[0].email, + }; + const token = sign({user: userData}, secret, {expiresIn: 60 * 60}); + + return {token, user: {user_id: user[0].user_id, username: user[0].username}}; } diff --git a/server/src/services/auth/controllers/verifyToken.ts b/server/src/services/auth/controllers/verifyToken.ts index 50a278d..1d1f548 100644 --- a/server/src/services/auth/controllers/verifyToken.ts +++ b/server/src/services/auth/controllers/verifyToken.ts @@ -3,9 +3,13 @@ import {sign, verify} from "jsonwebtoken"; /** * Verify a JWT and return the decoded payload. */ + +const secret: string = process.env.JWT_SECRET! || "bnghsjhsd"; +const expiresIn: string = process.env.JWT_EXPIRES! || "1h"; + export function verifyToken(token: string): {userId: number} { try { - const payload = verify(token, process.env.JWT_SECRET) as {userId: number}; + const payload = verify(token, secret) as {userId: number}; return payload; } catch (err) { throw new Error("Invalid token"); diff --git a/server/src/services/auth/routes/login.ts b/server/src/services/auth/routes/login.ts index 0a42154..130378a 100644 --- a/server/src/services/auth/routes/login.ts +++ b/server/src/services/auth/routes/login.ts @@ -65,7 +65,7 @@ const route = createRoute({ app.openapi(route, async (c) => { const {username, password, email} = await c.req.json(); - if (!username || !password || !email) { + if (!username || !password) { return c.json( { success: false, @@ -75,8 +75,9 @@ app.openapi(route, async (c) => { ); } + const {token, user} = await login(username.toLowerCase(), password); try { - const {token, user} = login(username.toLowerCase(), password); + const {token, user} = await login(username.toLowerCase(), password); // Set the JWT as an HTTP-only cookie //c.header("Set-Cookie", `auth_token=${token}; HttpOnly; Secure; Path=/; SameSite=None; Max-Age=3600`); @@ -87,29 +88,4 @@ app.openapi(route, async (c) => { } }); -/* - let body = {username: "", password: "", error: ""}; - try { - body = await c.req.json(); - } catch (error) { - return c.json({success: false, message: "Username and password required"}, 400); - } - - if (!body?.username || !body?.password) { - return c.json({message: "Username and password required"}, 400); - } - try { - const {token, user} = login(body?.username, body?.password); - - // Set the JWT as an HTTP-only cookie - c.header("Set-Cookie", `auth_token=${token}; HttpOnly; Secure; Path=/; SameSite=None; Max-Age=3600`); - - return c.json({message: "Login successful", user}); - } catch (err) { - // console.log(err); - return c.json({message: err}, 401); - } - - -*/ export default app; diff --git a/server/src/services/auth/routes/session.ts b/server/src/services/auth/routes/session.ts index 02486ee..281a7a0 100644 --- a/server/src/services/auth/routes/session.ts +++ b/server/src/services/auth/routes/session.ts @@ -3,55 +3,97 @@ import {verify} from "hono/jwt"; const session = new OpenAPIHono(); const tags = ["Auth"]; -const JWT_SECRET = process.env.JWT_SECRET; +const JWT_SECRET = process.env.JWT_SECRET!; -const route = createRoute({ - tags: ["Auth"], - summary: "Checks a user session based on there token", - description: "Can post there via Authentiaction header or cookies", - method: "get", - path: "/", - request: {body: {content: {"application/json": {schema: {username: "", password: ""}}}}}, - responses: { - 200: { - content: { - "application/json": { - schema: {session: ""}, - }, - }, - description: "Login successful", - }, - 401: { - content: { - "application/json": { - schema: {message: ""}, - }, - }, - description: "Error of why you were not logged in.", - }, - }, +const UserSchema = z.object({ + username: z + .string() + .regex(/^[a-zA-Z0-9_]{3,30}$/) + .openapi({example: "smith034"}), + email: z.string().email().openapi({example: "smith@example.com"}), + password: z + .string() + .min(6, {message: "Passwords must be longer than 3 characters"}) + .regex(/[A-Z]/, {message: "Password must contain at least one uppercase letter"}) + .regex(/[\W_]/, {message: "Password must contain at least one special character"}) + .openapi({example: "Password1!"}), }); -session.openapi(route, async (c) => { - const authHeader = c.req.header("Authorization"); - if (authHeader?.includes("Basic")) { - // - return c.json({message: "You are a Basic user! Please login to get a token"}, 401); +session.openapi( + createRoute({ + tags, + summary: "Checks a user session based on there token", + description: "Can post there via Authentiaction header or cookies", + method: "get", + path: "/", + // request: { + // body: { + // content: { + // "application/json": {schema: UserSchema}, + // }, + // }, + // }, + responses: { + 200: { + content: { + "application/json": { + schema: z.object({ + data: z.object({ + token: z.string().openapi({example: "sdkjhgsldkvhdakl;jvhs;adkjfhvds.kvnsad;ovhads"}), + // user: z.object({ + // user_id: z.string().openapi({example: "04316c86-f086-4cc6-b3d4-cca164a26f3f"}), + // username: z.string().openapi({example: "smith"}), + // email: z.string().openapi({example: "smith@example.com"}).optional(), + // }), + }), + }), + }, + }, + description: "Login successful", + }, + 401: { + content: { + "application/json": { + schema: z.object({ + message: z.string().openapi({example: "Unathenticated"}), + }), + }, + }, + description: "Error of why you were not logged in.", + }, + }, + }), + async (c) => { + const authHeader = c.req.header("Authorization"); + + if (authHeader?.includes("Basic")) { + return c.json({message: "You are a Basic user! Please login to get a token"}, 401); + } + + if (!authHeader) { + return c.json({message: "Unauthorized"}, 401); + } + + const token = authHeader?.split("Bearer ")[1] || ""; + + try { + const payload = await verify(token, process.env.JWT_SECRET!); + return c.json({data: {token: token, user: payload.user}}, 200); + } catch (error) {} + return c.json({data: {token: "tsfds"}}, 200); } +); - if (!authHeader) { - return c.json({error: "Unauthorized"}, 401); - } +// const token = authHeader?.split("Bearer ")[1] || ""; - const token = authHeader?.split("Bearer ")[1] || ""; - - try { - const payload = await verify(token, JWT_SECRET); - //console.log(payload); - return c.json({data: {token, user: payload.user}}); - } catch (err) { - return c.json({error: "Invalid or expired token"}, 401); - } -}); +// try { +// const payload = await verify(token, process.env.JWT_SECRET!); +// //console.log(payload); +// //return c.json({data: {token, user: payload.user}}, 200); +// return c.json({message: "something"}); +// } catch (err) { +// return c.json({error: "Invalid or expired token"}, 401); +// } +// }); export default session; diff --git a/server/src/services/auth/utils/checkPassword.ts b/server/src/services/auth/utils/checkPassword.ts new file mode 100644 index 0000000..75ccdb2 --- /dev/null +++ b/server/src/services/auth/utils/checkPassword.ts @@ -0,0 +1,10 @@ +import bcrypt from "bcrypt"; + +export const checkPassword = async (currentPassword: string, dbPassword: string) => { + // encypt password + const pass: string | undefined = process.env.SECRET; + + const checked = bcrypt.compareSync(pass + currentPassword, dbPassword); + + return checked; +}; diff --git a/server/src/services/auth/utils/createPassword.ts b/server/src/services/auth/utils/createPassword.ts index 3f2fd75..f071ebd 100644 --- a/server/src/services/auth/utils/createPassword.ts +++ b/server/src/services/auth/utils/createPassword.ts @@ -8,9 +8,9 @@ export const createPassword = async (password: string) => { if (!pass || !salt) { pass = "error"; } else { - pass = bcrypt.hashSync(process.env.SECRET + password, parseInt(salt)); + pass = bcrypt.hashSync(pass + password, parseInt(salt)); - pass = btoa(pass); + // pass = btoa(pass); } return pass;