-
Notifications
You must be signed in to change notification settings - Fork 0
Authentication Semi-integration #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,12 +5,14 @@ import jwt from "jsonwebtoken"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { ZodError } from "zod"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import jwtConfig from "@/config/jwt"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import prisma from "@/database/prisma/client"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import redisClient from "@/database/redis/client"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import NotFoundError from "../exceptions/notfound.exception"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import UnauthorizedError from "../exceptions/unauthorized.exception"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import CredentialSchema from "../schemas/credential.schema"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import RefreshTokenSchema from "../schemas/refresh-token.schema"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import SignInSchema from "../schemas/signin.schema"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { JwtSubject } from "../types/jwt"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -87,6 +89,28 @@ export default { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async signin(req: FastifyRequest, reply: FastifyReply) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { password, ...credentials } = SignInSchema.parse(req.body); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const userCounter = await prisma.user.count({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| where: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| email: credentials.email, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (userCounter > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+96
to
+102
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const userCounter = await prisma.user.count({ | |
| where: { | |
| email: credentials.email, | |
| }, | |
| }); | |
| if (userCounter > 0) { | |
| const existingUser = await prisma.user.findUnique({ | |
| where: { | |
| email: credentials.email, | |
| }, | |
| select: { | |
| id: true, | |
| }, | |
| }); | |
| if (existingUser) { |
Copilot
AI
Apr 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
signin can complete without ever sending a response (when userCounter === 0), which will leave the client hanging or produce an unintended empty response. Ensure the handler returns/sends a success payload (and likely creates the user / issues tokens) for the non-error path.
| } catch (e) { | |
| if (e instanceof UnauthorizedError) { | |
| reply.status(401).send({ error: e.message }); | |
| } else { | |
| reply.status(500).send({ error: req.t("Server error") }); | |
| } | |
| return reply.status(204).send(); | |
| } catch (e) { | |
| if (e instanceof ZodError) { | |
| return reply.status(400).send({ | |
| error: req.t("Invalid credentials"), | |
| details: e.issues, | |
| }); | |
| } | |
| if (e instanceof UnauthorizedError) { | |
| return reply.status(401).send({ error: e.message }); | |
| } | |
| return reply.status(500).send({ error: req.t("Server error") }); |
Copilot
AI
Apr 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This handler parses SignInSchema but doesn't handle ZodError; invalid request bodies will be reported as 500 instead of a 400 with validation details (unlike login, refresh, revoke). Consider mirroring the existing error handling pattern here (including console.debug and returning e.issues).
Copilot
AI
Apr 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "Email already in use" case is returned as 401 Unauthorized. That status code is typically reserved for missing/invalid authentication; for a registration conflict, use a more appropriate status (commonly 409) and error type/message to avoid confusing API consumers.
| throw new UnauthorizedError(req.t("Email already in use")); | |
| } | |
| } catch (e) { | |
| if (e instanceof UnauthorizedError) { | |
| reply.status(401).send({ error: e.message }); | |
| } else { | |
| reply.status(500).send({ error: req.t("Server error") }); | |
| } | |
| return reply.status(409).send({ | |
| error: req.t("Email already in use"), | |
| }); | |
| } | |
| } catch (e) { | |
| reply.status(500).send({ error: req.t("Server error") }); |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,9 @@ | ||||||||||
| import { z } from "zod"; | ||||||||||
|
|
||||||||||
| const SignInSchema = z.object({ | ||||||||||
| email: z.email(), | ||||||||||
| username: z.string().min(3).max(30), | ||||||||||
| password: z.string().min(8), | ||||||||||
|
Comment on lines
+5
to
+6
|
||||||||||
| username: z.string().min(3).max(30), | |
| password: z.string().min(8), | |
| username: z.string().min(3).max(30).optional(), | |
| password: z.string().min(8).optional(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
signindestructurespasswordand requiresusernamein the schema, but neitherpasswordnorusernameis used in the handler (onlycredentials.email). This is likely a logic error (or premature schema) and also triggers unused-variable linting; either use these fields as part of the flow or remove/relax them in the schema/destructuring to match what the endpoint actually needs.