NukeBase

The admin Auth Context

Several server-side APIs receive an admin object describing the current caller. The shape is the same everywhere — only the calling context differs.

Where you'll see it

Object Shape

The admin object always contains request metadata. Identity fields (uid, username, token, claims) are present only when the caller has a valid session cookie.

Property When Authenticated When Not Authenticated
uid User's unique ID undefined
username User's username, or "" for an account that has none (demo and email-only accounts) undefined
email User's email, falling back to username if no separate email is stored undefined
token Session token from cookie undefined
claims Custom claims object (e.g., { role: "admin" }) undefined
urlParams Parsed query string parameters Parsed query string parameters
cookies Parsed cookies object Parsed cookies object
referer Referer header (or "") Referer header (or "")
userAgent User-Agent header (or "") User-Agent header (or "")
ip Client IP address Client IP address
url Request URL path Request URL path

Common Patterns

Reading admin in different contexts
// In a callable
addCallable("getProfile", (data, admin, sessionId) => {
  if (!admin.uid) return { status: "Failed", message: "Login required" };
  return get(["users", admin.uid]).data;
});

// In a connection trigger
addConnectionTrigger("open", (admin, sessionId) => {
  console.log("Connected:", admin.uid || "anonymous", "from", admin.ip);
});

// In a postWithBody handler (req.admin)
nukebase.app.postWithBody("/api/me", (res, req) => {
  if (!req.admin.uid) return res.send(JSON.stringify({ status: "Failed" }), "401 Unauthorized");
  res.send(JSON.stringify({ uid: req.admin.uid, claims: req.admin.claims }));
});

// In a raw post handler (manual checkAuth)
nukebase.app.post("/api/me-raw", (res, req) => {
  const admin = checkAuth(req, res);
  res.end(JSON.stringify({ uid: admin.uid }));
});

// In a security rule (rules.js)
module.exports = {
  "users": {
    "$userId": {
      "write": "admin.uid == $userId",
      "private": { "read": "admin.uid == $userId" }
    },
    "adminPanel": {
      "read": "admin.claims.role == 'admin'"
    }
  }
};

Anonymous callers still get an admin object. Identity fields will be undefined, but request metadata (ip, userAgent, cookies, etc.) is always populated. Always check admin.uid before assuming the caller is logged in.

"root" and the three states in a trigger

Server-side data and file operations default to admin = "root", a sentinel that bypasses security rules. Rules never see it — checkRead/checkWrite short-circuit on it before evaluating anything — so admin.uid inside a rule is always safe to reach for.

Triggers are different: they run after the write, and handing them the raw sentinel would turn ctx.admin.uid into a crash on every server-side write. So "root" becomes null there, leaving three states a handler can tell apart:

ctx.adminMeans
nullA server-side call — no request, no user
An object with no uidAn anonymous client (still has ip, userAgent, referer)
An object with a uidA signed-in client

ctx.admin is the immediate caller, not whoever set the change in motion. A write made inside a callable or a POST handler runs as root, so a trigger on it sees null even though a person plainly caused it. To carry real identity through a privileged path, pass it explicitly into the write: set(path, value, admin).