JCC Express

Security

Authorization

Introduction

Authorization is powered by Gate + Policy primitives:

  • GateFacade.define(...) for named abilities
  • GateFacade.policy(Model, PolicyClass) for model policies
  • GateFacade.can(...) and GateFacade.authorize(...) for checks
  • AuthorizeMiddleware.authorize(...) for route-level ability enforcement

Global helpers are also available: Gate, can(...), authorize(...).


Define gates and policies

In a provider (for example app/Providers/AuthServiceProvider.ts):

TypeScript
import { GateFacade } from "jcc-express-mvc/Authorization";
import { User } from "../Models/User";
import { UserPolicy } from "../Policies/UserPolicy";

GateFacade.policy(User, UserPolicy);

GateFacade.define("manage-users", (user) => user.role === "admin");
GateFacade.define("edit-post", async (user, post) => {
  return user.id === post.user_id || user.role === "admin";
});

Gate decisions deny by default when ability/policy is not found.


Policy classes

Extend Policy and implement ability methods:

TypeScript
import { Policy } from "jcc-express-mvc/Authorization";

export class UserPolicy extends Policy {
  update(user: any, targetUser: any): boolean {
    return user.id === targetUser.id || user.role === "admin";
  }
}

Supported conventional methods include viewAny, view, create, update, delete, restore, forceDelete.


Check permissions in code

Using helpers:

TypeScript
const allowed = await can(req.user, "update-settings");
await authorize(req.user, "manage-users");

Using facade directly:

TypeScript
const canEdit  = await GateFacade.can(user, "update", post);
const cannotEdit = await GateFacade.denies(user, "update", post); // inverse of can
const result   = await GateFacade.check(user, ["update", "delete"], post); // all must pass
await GateFacade.authorize(user, "delete", post);

authorize(...) throws AuthorizationException when denied.

  • can(user, ability, ...args) — returns true if the ability is granted.
  • denies(user, ability, ...args) — returns true if the ability is denied (inverse of can).
  • check(user, abilities[], ...args) — returns true only when all listed abilities pass.

Route middleware authorization

Use the middleware factory:

TypeScript
import { AuthorizeMiddleware } from "jcc-express-mvc/Authorization";

Route.middleware([AuthorizeMiddleware.authorize("update", Post)]).put(
  "/posts/{id}",
  [PostsController, "update"],
);

Behavior:

  • unauthenticated -> 401 JSON or redirect to login
  • missing model -> 404
  • unauthorized -> 403

Integration with error handling

Authorization failures raise AuthorizationException, handled by the framework error handler as:

  • JSON requests: 403 { message }
  • web requests: flash + redirectBack()

Summary

  • Register gates/policies in a provider.
  • Use can(...) for boolean checks and authorize(...) for enforced checks.
  • Use denies(...) as a readable inverse of can(...).
  • Use check(user, [...abilities]) when multiple abilities must all pass.
  • Use AuthorizeMiddleware when authorization should happen at route edge.