Core ConceptsAuthenticationPermissions

Static

Static mode is Arkos's code-based permission system. Roles are assigned on the User model as an enum field, and permissions are declared via ArkosPolicy (v1.6+, recommended) or .auth.ts files (v1.1+, still supported). Arkos enforces them automatically across all routes.

Before using Static mode make sure you have authentication configured. See Authentication Setup.

User Model

Static mode requires a role or roles enum field on your User model:

prisma/schema.prisma
enum UserRole {
  Admin
  Editor
  User
}

model User {
  // ... required Arkos fields
  role  UserRole  @default(User)  // single role
  // roles UserRole[]             // multiple roles
}

See the full required User model at User Model Authentication Setup.

ArkosPolicy

ArkosPolicy is the recommended API introduced in v1.6. It provides a fluent interface for defining permissions, works for any module (Prisma models, auth, file upload, or custom), and supports imperative can* checks for Fine-Grained Access Control.

Defining a Policy

src/modules/post/post.policy.ts
import { ArkosPolicy } from "arkos";

const postPolicy = ArkosPolicy("post")
  .rule("Create", { roles: ["Admin", "Editor"], name: "Create Post", description: "Create new posts" })
  .rule("Update", { roles: ["Admin", "Editor"], name: "Update Post" })
  .rule("Delete", { roles: ["Admin"], name: "Delete Post" })
  .rule("View", { roles: ["*"] }); // all authenticated users

export default postPolicy;

.rule(action, rule) accepts:

ValueBehavior
{ roles: ["*"] }All authenticated users
{ roles: [...], name?, description? }Role-restricted
["Admin", "Editor"]Shorthand for roles array

Using Permissions in Routes

Permissions wire the same way whether you're on a custom route or a built-in one. For built-in routes (Prisma model, auth, file upload) use a Route Hook alongside your ArkosRouter export.

src/modules/post/post.router.ts
import { ArkosRouter } from "arkos";
import postPolicy from "@/src/modules/post/post.policy";
import postController from "@/src/modules/post/post.controller";

const router = ArkosRouter();

router.post(
  { path: "/api/posts", authentication: postPolicy.Create },
  postController.createOne
);

router.get(
  { path: "/api/posts", authentication: postPolicy.View },
  postController.findMany
);

export default router;
src/modules/post/post.router.ts
import { ArkosRouter, RouteHook } from "arkos";
import postPolicy from "@/src/modules/post/post.policy";

export const hook: RouteHook = {
  createOne: { authentication: postPolicy.Create },
  findMany: { authentication: postPolicy.View },
  updateOne: { authentication: postPolicy.Update },
  deleteOne: { authentication: postPolicy.Delete },
};

const router = ArkosRouter();

export default router;

RouteHook is the new name for export const config: RouterConfig. If you have existing code using the old name it still works but will log a deprecation warning. See Route Hook for full details.

Using Permissions in Express Router

If you're using a plain Express Router instead of ArkosRouter, wire authentication manually via authService:

src/modules/post/post.router.ts
import { Router } from "express";
import { authService } from "arkos/services";
import postPolicy from "@/src/modules/post/post.policy";
import postController from "@/src/modules/post/post.controller";

const router = Router();

// login required only
router.get("/api/posts", authService.authenticate, postController.findMany);

// role-based — v1.6+
router.post(
  "/api/posts",
  authService.authenticate,
  authService.authorize("Create", "post", postPolicy.Create),
  postController.createOne
);
MethodSignatureVersion
authService.authenticatemiddlewareall
authService.authorize(action, resource, rule)v1.6+
authService.handleAccessControl(action, resource, accessControl)before v1.6

Imperative Checks

ArkosPolicy exposes can* methods for use inside services, interceptors, or any custom logic — this is the foundation of Fine-Grained Access Control:

src/modules/post/post.service.ts
import postPolicy from "@/src/modules/post/post.policy";

if (await postPolicy.canDelete(req.user)) {
  // proceed
}

Imperative checks in fine-grained access control also work with auth config files. See the full guide at Fine-Grained Access Control.

CLI Generation (v1.6+)

arkos generate policy --module post
# or
arkos g p -m post

Auth Config Files

.auth.ts auto-loading is deprecated as of v1.6 and will be removed in v2.0. New projects should use ArkosPolicy. Existing projects can migrate gradually — see the migration guide.

.auth.ts files have been the standard since v1.0 and remain fully supported. They define authenticationControl (who needs to be logged in) and accessControl (which roles can act) separately.

Creating an Auth File

arkos generate auth-configs --module post
# or
arkos g a -m post

This generates src/modules/post/post.auth.ts:

src/modules/post/post.auth.ts
import { AuthConfigs } from "arkos/auth";

export const postAccessControl = {
  Create: {
    roles: ["Admin", "Editor"],
    name: "Create Post",
    description: "Permission to create new post records",
  },
  Update: {
    roles: ["Admin", "Editor"],
    name: "Update Post",
    description: "Permission to update existing post records",
  },
  Delete: {
    roles: ["Admin"],
    name: "Delete Post",
    description: "Permission to delete post records",
  },
  View: {
    roles: ["*"],
    name: "View Post",
    description: "Permission to view post records",
  },
} as const satisfies AuthConfigs["accessControl"];

export const postAuthenticationControl = {
  Create: true,
  Update: true,
  Delete: true,
  View: true,
};

const postAuthConfigs: AuthConfigs = {
  authenticationControl: postAuthenticationControl,
  accessControl: postAccessControl,
};

export default postAuthConfigs;

Auth Config File Structure

authenticationControl — whether a route requires login:

ValueBehavior
trueLogin required
falsePublic — no authentication

accessControl — which roles can act:

FormatExample
Simple["Admin", "Editor"]
Detailed{ roles: [...], name: "...", description: "..." }
All authenticated["*"]

Using Auth Config Files in Routes

The same auth config file works on both custom and built-in routes. For built-in routes use a Route Hook alongside your ArkosRouter export.

src/modules/post/post.router.ts
import { ArkosRouter } from "arkos";
import postAuthConfigs from "@/src/modules/post/post.auth";
import postController from "@/src/modules/post/post.controller";

const router = ArkosRouter();

// login required only
router.get(
  {
    path: "/api/posts",
    authentication: true,
  },
  postController.findMany
);

// role-based
router.post(
  {
    path: "/api/posts",
    authentication: {
      resource: "post",
      action: "Create",
      rule: postAuthConfigs.accessControl.Create,
    },
  },
  postController.createOne
);

export default router;
src/modules/post/post.router.ts
import { ArkosRouter, RouteHook } from "arkos";
import postAuthConfigs from "@/src/modules/post/post.auth";

export const hook: RouteHook = {
  createOne: {
    authentication: {
      resource: "post",
      action: "Create",
      rule: postAuthConfigs.accessControl.Create,
    },
  },
  deleteOne: {
    authentication: {
      resource: "post",
      action: "Delete",
      rule: postAuthConfigs.accessControl.Delete,
    },
  },
};

const router = ArkosRouter();

export default router;

RouteHook is the new name for export const config: RouterConfig. If you have existing code using the old name it still works but will log a deprecation warning. See Route Hook for full details.

Using Auth Config Files in Express Router

src/modules/post/post.router.ts
import { Router } from "express";
import { authService } from "arkos/services";
import postAuthConfigs from "@/src/modules/post/post.auth";
import postController from "@/src/modules/post/post.controller";

const router = Router();

// login required only
router.get("/api/posts", authService.authenticate, postController.findMany);

// role-based — v1.6+
router.post(
  "/api/posts",
  authService.authenticate,
  authService.authorize("Create", "post", postAuthConfigs.accessControl.Create),
  postController.createOne
);

// role-based — before v1.6
router.post(
  "/api/posts",
  authService.authenticate,
  authService.handleAccessControl("Create", "post", postAuthConfigs.accessControl),
  postController.createOne
);

Auth Actions Discovery

Both ArkosPolicy and .auth.ts feed the /api/auth-actions endpoint — used by frontends to discover available permissions:

[
  {
    "resource": "post",
    "action": "Create",
    "roles": ["Admin", "Editor"],
    "name": "Create Post",
    "description": "Permission to create new post records"
  }
]

Export them to a file for frontend integration:

arkos export auth-action
arkos export auth-action --overwrite
arkos export auth-action --path src/constants

See CLI reference for full options.