JCC Express

Security

Authentication

Introduction

JCC Express MVC authentication is cookie + JWT based, centered on the Authentication class and middleware exports:

  • auth (web/session-like protected routes)
  • apiAuth (API bearer/cookie token protection)
  • guest (guest-only routes)

Exported from jcc-express-mvc:

TypeScript
import { Auth, auth, apiAuth, guest } from "jcc-express-mvc";

Login flow

Typical controller/login handler usage:

TypeScript
await Auth.attempt(next);

Auth.attempt(next):

  • resolves user by email, phone, or username
  • verifies password with verifyHash(...)
  • on success issues:
    • auth_token cookie (access token, ~1 hour)
    • refresh_token cookie (~7 days)
  • on failure throws ValidationException (Invalid credentials) and forwards to next(error)

Auth middleware behavior

auth

  • reads auth_token cookie
  • verifies token type/payload
  • loads user and attaches req.user
  • sets res.locals.Auth = user
  • if invalid/missing: clears auth cookies, stores redirect target in session, redirects to /login

apiAuth

  • reads Authorization: Bearer <token> first, then auth_token cookie fallback
  • verifies JWT signature and access-token rules (typ must not be refresh)
  • strips exp, iat, and typ from the payload, then reloads the user using the remaining claims as a database lookup (for example { id: 1 } or { email: "user@example.com" })
  • returns 401 { message: "Not authorized" } on failure
  • sets req.user and req.id on success

This pairs with model-issued tokens from User.createToken() — see Defining Model.

guest

  • blocks authenticated users from guest routes
  • generally redirects back/previous if token exists

Refresh and logout

Refresh

Auth.refreshToken(req, res, next) validates/rotates refresh token (jti store), reissues cookies, and rehydrates req.user.

On failure it clears cookies and returns 401.

Logout

TypeScript
Auth.logout();
  • revokes refresh token jti if present
  • clears auth_token and refresh_token
  • redirects to /login

Auth helper methods

  • Auth.check() -> whether access cookie is valid and usable
  • Auth.user() -> current user from request
  • Auth.id() -> current user id (id or _id)
  • Auth.socialLogin(userId) -> issues auth cookies after OAuth/social flow

Route examples

TypeScript
Route.middleware(["guest"]).get("/login", loginPage);
Route.middleware(["loginThrottle"]).post("/auth/login", async (req, res, next) => {
  await Auth.attempt(next);
});
Route.middleware(["auth"]).get("/home", homeHandler);

API token login (apiAttempt)

For stateless API clients, use auth().apiAttempt() to validate credentials and issue a bearer token in one step:

TypeScript
Route.post("/api/login", async () => {
  const result = await auth().apiAttempt({ field: "email", model: "User" });

  if (!result.success) {
    return response().status(401).json({ message: result.message });
  }

  return {
    token: result.token,
    user: result.user,
  };
});

Route.middleware(["apiAuth"]).get("/api/me", async (req) => {
  return { user: req.user };
});

apiAttempt options:

FieldDefaultDescription
field"email"Request input key to match (email, phone, username, etc.)
model"User"Model name resolved via getModel()

The request body must include the credential field and password. On success, returns { success: true, token, user, message }. On failure, returns { success: false, token: null, user: null, message: "Invalid credentials" }.

The model must have protected hasToken = true. User lookup and password verification are shared with session login via findUserForAuth().

Manual token login (createToken)

You can also issue a token manually after your own credential check:

TypeScript
import { User } from "@/Models/User";

Route.post("/api/token", async (req) => {
  const user = await User.where("email", req.input("email")).first();
  return {
    token: user?.createToken({ email: user.email, typ: "access" }),
  };
});

Route.middleware(["apiAuth"]).get("/api/me", async (req) => {
  return { user: req.user };
});

Full createToken details: Defining Model.


Summary

  • Authentication uses JWT cookies with rotating refresh tokens.
  • Use Auth.attempt(next) for cookie login, auth().apiAttempt() for API email/password + bearer token, and auth/apiAuth middleware for protection.
  • For manual API tokens, enable hasToken on the model and call createToken()apiAuth reloads the user from JWT claims.
  • Auth.logout() clears auth state and redirects to login.