Authentication API
Last updated August 2, 2026
Base path: /api/auth
Source: server/src/routes/auth.routes.js (112 lines) + auth.controller.js (2289 lines)
Public Endpoints (No Auth Required)
GET /system-config
Returns platform-wide feature flags from SystemSettings model.
Response:
POST /check-email
Email-first login flow. Checks if an email exists and returns the account type without revealing sensitive data. Sends a "no account" notification email if the email is not registered (Vercel-style security).
Rate Limit: emailCheckLimiter
Request Body:
Response (account exists):
Response (no account):
A "Sign-in attempt on Classgrid" email is sent to the address (fire-and-forget, rate-limited to 1 per second per email).
POST /signup-init
Step 1 of manual signup. Sends a verification email with a UUID token link.
Request Body:
Validation:
- If email is already verified →
400 "Email already registered. Please login." - If unverified user exists → deletes old record, creates fresh verification
- Rate limit: max 2 verification emails per hour per email
Response:
Email sent: "📧 Verify Email - Classgrid" with a verification link.
GET /verify-token/:token
Step 2 of signup. Validates the email verification UUID token.
Params: token — UUID from the verification email link
Response (valid): Redirects or returns verification status for frontend to show the "Set Password" form.
Response (invalid/expired): 400
POST /signup-complete
Step 3 of signup. Sets password and creates the verified user account.
Request Body:
Password Requirements: Min 8 chars, must include uppercase, lowercase, number, and special character (@$!%*?&).
Response: Returns JWT token + user object + sets token cookie.
POST /check-admin-status
Checks if an email belongs to an org_admin and whether they've activated their account.
Request Body:
Response:
isActivated = !user.mustResetPassword (true means they've set their password).
POST /validate-activation-token
Validates a staff/admin activation token or email+code pair. Used by the activation page before showing the password form.
Request Body (link mode):
Request Body (code mode):
Security:
- Tokens are SHA-256 hashed before DB lookup
- Single-use: rejected if
activationUsedAtis set - Rate limited: max 5 attempts per 15-minute window per user
410if token already used or expired (with specific messages)
Response: { "valid": true, "mode": "link" | "code" }
POST /activate-admin
Sets password for an invited staff member. Marks account as activated, auto-logs in, and sends confirmation email.
Request Body:
OR:
Workflow:
- Validates token/code (single-use, rate-limited)
- Hashes password with bcrypt (10 rounds)
- Sets
mustResetPassword: false,isEmailVerified: true - Marks token as consumed (
activationUsedAt: Date) - Syncs onboarding progress, tracks event
- Sends "Your Classgrid Admin Account is Active" email
- Generates JWT, sets cookie, returns redirect path
Response:
POST /resend-activation
Regenerates activation credentials and resends the invite email for unactivated org admins.
Rate Limit: resetPasswordLimiter. Additional: blocks if token was issued less than 2 minutes ago.
Request Body:
Response (always safe — prevents enumeration):
POST /manual-activation-link
Resolves a fresh activation link using email + activation code (fallback for users who lost the email link).
Request Body:
Response:
POST /login
Manual email+password login with device fingerprinting, reCAPTCHA, and multi-portal support.
Rate Limit: loginLimiter
Request Body:
JWT Expiry (Platform-Aware "Login Law"):
- Mobile app (
x-platform-app: android|ios): 365 days - Desktop + Remember Me: 7 days
- Desktop default: 24 hours
Cookie: token cookie set with httpOnly, secure (prod), sameSite: None (prod). Domain scoped to .classgrid.in for subdomain SSO.
Response:
Dashboard Targets by Role:
POST /request-login-otp
Requests OTP for passwordless login (device verification).
Rate Limit: loginLimiter
Request Body:
POST /resend-device-otp
Resends device verification OTP.
Rate Limit: otpSendLimiter
POST /verify-device
Verifies device OTP and completes login.
Request Body:
POST /setup-org-admin
Legacy endpoint for backward compatibility. Sets up an org admin account.
POST /logout
Clears the token cookie.
Response:
POST /forgot-password
Sends a password reset email with a token link.
Rate Limit: resetPasswordLimiter
Request Body:
Email sent: "🔐 Classgrid - Reset Your Password"
GET /verify-reset-token/:token
Validates a password reset token.
POST /reset-password
Resets password using the token from the email.
Rate Limit: resetPasswordLimiter
Request Body:
POST /faculty-activate
Faculty-specific activation endpoint (uses resetPasswordLimiter).
Authenticated Endpoints
GET /me
Returns the currently logged-in user's profile.
Auth: isAuthenticated (JWT cookie or Authorization: Bearer <token>)
Response:
POST /force-reset-password
Admin-forced password reset for a user.
Auth: isAuthenticated
POST /change-password
User changes their own password (requires current password).
Auth: isAuthenticated
Request Body:
POST /delete-account
User deletes their own account.
Auth: isAuthenticated
Google OAuth
GET /google
Initiates Google OAuth flow. Creates an encrypted OAuth state containing loginTab and host for multi-portal routing.
Query Params:
loginTab—student|faculty|admin|super_admin(default:student)host— custom domain or subdomain (e.g.,ganesha.classgrid.in)
Flow:
- Creates OAuth state with
loginTab+host(encrypted, stored in cookie) - Redirects to Google with
scope: ["profile", "email"],prompt: "select_account consent"
GET /google/callback
Google OAuth callback. Verifies OAuth state cookie, authenticates user, and redirects.
Flow:
- Verifies
x-oauth-statecookie againstreq.query.state - Resolves target URL from
hostin state (custom domain or default frontend) - On success → calls
authController.oauthCallbackwhich generates JWT, sets cookie, redirects to dashboard - On error → redirects to login page with
?error=google_blockedor?error=AuthFailed
JWT Token Structure
Signed with JWT_SECRET env variable using jsonwebtoken.