Loopar Introduction
loopar-webpage

Overview

Loopar ships a complete authentication and authorization layer — no external auth library required. It covers users, roles, granular permissions, sessions, and CSRF protection out of the box.

PieceResponsibility
AuthLogin, logout, session lifecycle
PermissionManagerResolves what a user is allowed to do
ActionScannerDiscovers controller actions and their access rules
PermissionSyncKeeps permission records in sync with the code
CSRFCross-site request forgery protection

Passwords are never stored in plain text — Loopar hashes them with Argon2 via loopar.hash(plain).

Users & Sessions

The Session

Server-side code reads the current session through loopar.session:

import loopar from "loopar";

const userId  = loopar.session.user;       // current user id
const site    = loopar.session.site;       // current tenant
const isAdmin = loopar.session.is_admin;   // administrator flag

if (!loopar.session.user) {
loopar.throw("Authentication required", 401);
}

Looking Up Users

// Fetch a user by id or email
const user = await loopar.getUser("user@example.com");

// Disable a user account
await loopar.disabledUser(userId);

Login & Logout

Authentication is handled by the framework's Auth layer. A successful login() establishes a session and sets a CSRF cookie; logout() kills the session. You normally don't call these directly — the built-in auth pages and API routes do.

Roles & Permissions

Loopar's authorization model is role-based with granular, per-document permissions.

Levels of Control

LevelExample
DocumentWho can read / write / delete Customer records
FieldHide salary from non-HR roles
ActionWho can call actionApprove on an Invoice

Checking Permissions

PermissionManager resolves whether a user may perform an action:

import { PermissionManager } from "loopar";

// Can this user delete Customer records?
const allowed = PermissionManager.can("Customer", "delete", username);

// All resolved permissions for a user
const perms = PermissionManager.getPermissions(username);

When permissions change, PermissionManager.invalidate(username) clears the cached set for that user.

Controller Actions & Access

Only methods prefixed with action on a controller are reachable over HTTP. Everything else stays private to the server.

import { BaseController } from "loopar";

export default class InvoiceController extends BaseController {
// Reachable: GET/POST /api/Invoice/approve
async actionApprove() { /* ... */ }

// NOT reachable over HTTP — internal helper only
recalcTotals() { /* ... */ }
}

ActionScanner walks every app and registers these actions so PermissionManager can guard them. Unauthenticated actions are surfaced through ActionScanner.getPublicActions().

Bypassing Permission Checks

Trusted server-side code can skip the permission check on a save:

await doc.save({ ignore_permissions: true });

Use this sparingly — only in code paths you fully control.

CSRF & Realtime

CSRF Protection

Every authenticated session carries a CSRF token, set as a cookie on login. State-changing requests must echo it back; the csrf module validates it before the request reaches your controller.

Realtime Authentication

Loopar's realtime layer (Socket.IO) authenticates connections with a JWT scoped to the tenant namespace, so realtime events respect the same tenant isolation as the rest of the framework.

Best Practices

  • Never expose the dev site publicly — it is the admin control center for every tenant.
  • Prefer doc.save() over loopar.db.setValue() when permissions and hooks matter — the low-level DB API bypasses both.
  • Keep logic behind action-prefixed methods so ActionScanner can discover and guard it.
  • Hash secrets with loopar.hash() — never store credentials or tokens in plain text.
  • Scope realtime events to the tenant namespace; don't broadcast across tenants.