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
- Security Rules — referenced as
admininside rule expressions (e.g.,"admin.uid == $userId") - Database Triggers — on the context object:
context.admin - File Triggers — on the context object:
ctx.admin - Callable Functions — second argument:
function(data, admin, sessionId) - Connection Triggers — first argument:
function(admin, sessionId) - postWithBody handlers — exposed as
req.admin - Raw
posthandlers — returned bycheckAuth(req, res)
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
// 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.admin | Means |
|---|---|
null | A server-side call — no request, no user |
An object with no uid | An anonymous client (still has ip, userAgent, referer) |
An object with a uid | A 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).