Yeti Jobs uses a stateless JWT-based authentication system. On a successful login or signup, the server signs a token withDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/tech-dipesh/yeti-Jobs/llms.txt
Use this file to discover all available pages before exploring further.
JSON_SECRET_KEY and delivers it in an HTTP-only cookie — it never touches the client’s JavaScript. Every subsequent request to a protected route is validated by the isLoggedIn middleware, which reads and verifies that cookie automatically. New accounts are issued an unverified JWT immediately after signup; email verification upgrades it to a verified one before the user can access any protected endpoint.
Full Authentication Flow
Sign Up
Send a
POST request to /api/v1/users/signup with the user’s details. The server validates the payload with a Zod schema, checks that the email domain resolves via DNS MX lookup, hashes the password with bcrypt (12 salt rounds), and inserts the new record into the users table.A 6-digit verification code is immediately generated and emailed via Nodemailer. An unverified JWT cookie is set so the client can proceed to the verification step without a separate login.Required body fields| Field | Type | Notes |
|---|---|---|
fname | string | First name |
lname | string | Last name |
email | string | Must pass DNS MX validation |
password | string | Hashed with bcrypt before storage |
education | string | Optional education level |
The
alreadyLoggedIn middleware guards this route. If a valid JWT cookie is already present, the server returns 409 Conflict before the signup logic runs.Verify Email
After signup, the user receives a 6-digit code by email. Submit it to If the code is expired or already used, the server returns
/api/v1/users/verify. The isAuthButUnverified middleware ensures only holders of an unverified JWT can reach this endpoint.The server looks up the most recent email_verified row for the user, checks the code matches, confirms it has not expired, and if valid:- Marks the
email_verifiedrow asis_verified = true - Issues a new JWT cookie with
verify: true— this is the token that unlocks all protected routes
403 Forbidden. To get a fresh code, call POST /api/v1/users/verify/resend.The
/verify, /verify/resend, /forget-password, and /forget-password/verify routes all share a strict rate limit of 2 requests per minute per IP. See Rate Limiting for details.Log In
Existing users (with a verified account) call
The token is signed with
POST /api/v1/users/login. The controller validates the body with the loginUserSchema Zod schema, fetches the hashed password from the database, and runs a bcrypt comparison. On success it issues a fresh JWT cookie.The JWT payload contains:| Claim | Description |
|---|---|
uid | User’s UUID primary key |
role | guest, recruiter, or admin |
company_id | UUID of the associated company, or null |
verify | Boolean — true once email has been confirmed |
JSON_SECRET_KEY and expires in 7 days. The cookie is set with httpOnly: true, secure: true, and sameSite: "none" so it works across the separate front-end and back-end origins.Check Auth Status
The front end can call Possible responses
GET /api/v1/users/login-status at startup to determine the current session state without requiring the user to log in again. This endpoint does not use the isLoggedIn middleware — it handles its own token inspection and returns structured status flags.| Scenario | Status | Body |
|---|---|---|
| No cookie | 401 | { "message": { "login": false, "verify": false } } |
| Cookie present, unverified | 403 | { "message": { "login": true, "verify": false } } |
| Fully authenticated | 200 | JWT payload + { "url": "<profile_pic_url>" } |
Log Out
GET /api/v1/users/logout clears the token cookie by calling res.clearCookie with the same httpOnly, secure, and sameSite options used when it was set. No database writes are needed since the system is stateless.Password Reset
Password reset is a two-step flow, both steps protected by the 2 req/min rate limit.Step 1 — Request a reset codeThe server verifies the email exists, then calls The controller checks the code against the
sendMail with type = 'forget'. A new row is inserted into email_verified with verified_type = 'forget_password'.Step 2 — Submit code and new passwordemail_verified table (joined with users for the email match), confirms it has not expired or been used, prevents reuse of the existing password via bcrypt compare, then updates users.password and marks the code as used inside a single database transaction (BEGIN / COMMIT / ROLLBACK).JWT Cookie Details
TheVerifyJwt service (src/services/verifyJwt.js) handles all token creation:
| Option | Value | Why |
|---|---|---|
httpOnly | true | Cookie is inaccessible to JavaScript — blocks XSS theft |
secure | true | Cookie only sent over HTTPS |
sameSite | "none" | Required for cross-origin front-end / back-end deployments |
maxAge | process.env.MAXAGE | Configurable lifetime; token itself expires in 7 days |
expiresIn | "7d" | JWT hard expiry, enforced on every jwt.verify call |
The isLoggedIn Middleware
Every protected route (companies, applications, admin, notifications) is guarded by authUserMiddleware (src/Middleware/isLoggedIn.js):
jwt.verify, the decoded payload is attached to req.user. All downstream middleware (isAdmin, isJobSeeker, isCompanyEmployee, isOwner) read role and identity data from req.user — they never query the database for basic identity checks.
Required Environment Variables
| Variable | Description |
|---|---|
JSON_SECRET_KEY | HMAC secret used to sign and verify all JWTs |
MAXAGE | Cookie maxAge in milliseconds |
NODEMAILER_MY_EMAIL | Sender email address for verification codes |
NODEMAILER_MY_HOST | SMTP host (e.g. smtp.gmail.com) |
NODEMAILER_MY_PASSWORD | SMTP authentication password / app password |