Security Overview
The RBA system uses two Firebase projects with different security models. RBAMGR (the case management system) has no authentication in Phase 1 — it relies on security-through-obscurity via hidden Duda pages and will add Firebase Auth in Phase 2. The Dev Team Portal is fully authenticated from day one using Firebase Email/Password Auth and a custom admin claim.
auth.token.admin === true
) for John's dashboard.RBAMGR — Phase 1 Security
rba-mgrPhase 1 Database Rules
During Phase 1 development, the database uses open rules to allow read/write without authentication. These must be locked down before go-live.
// Phase 1 — Development only. Replace before production.
{
"rules": {
".read": true,
".write": true
}
}
Required Before Production
Open rules are acceptable during local development but must be replaced before any real case data is stored. See Phase 2 rules below. Add this to the Integration Phase checklist.
Phase 2 Database Rules (Target)
Once Firebase Auth is added in Phase 2, rules lock each arbitrator to their own node. The [arbitratorId]
in the path matches the authenticated user's UID.
// Phase 2 — Production rules (to be applied when Auth is added)
{
"rules": {
"arbitrators": {
"$arbitratorId": {
".read": "auth != null && auth.uid === $arbitratorId",
".write": "auth != null && auth.uid === $arbitratorId"
}
},
"counters": {
"$arbitratorId": {
".read": "auth != null && auth.uid === $arbitratorId",
".write": "auth != null && auth.uid === $arbitratorId"
}
},
"resources": {
"$arbitratorId": {
".read": "auth != null && auth.uid === $arbitratorId",
".write": "auth != null && auth.uid === $arbitratorId"
}
},
".read": false,
".write": false
}
}
Phase 1 Access Model
Without Firebase Auth, Phase 1 access control relies on:
- Hidden Duda pages: All RBAMGR pages are hidden from the Duda navigation — they don't appear in any site menu or sitemap. Access is by direct URL only.
- No public link exposure: The arbitrator bookmarks each page. URLs are not shared publicly and not linked from any public-facing page.
- Arbitrator ID in code: Each component hardcodes the arbitrator's ID string. Without knowing both the URL and the arbitrator ID, data cannot be targeted.
- Single-user system: Phase 1 is built for one arbitrator. No login, no multi-user surface, no exposed credential entry points.
Phase 1 Limitation
Phase 1 security is adequate for a single-user private tool during development. It is not suitable for a multi-user or public-facing system. Firebase Auth and per-user rules are mandatory before Phase 2 launch.
Dev Team Portal — Full Auth Model
rba-dev-team-portalAuthentication Setup
The portal uses Firebase Authentication with Email/Password only. No Google, GitHub, or other OAuth providers. Every user account is created by John — volunteers receive an invite code and register with it.
admin: true
set via Node.js script.admin/invites/[code]/used
set true on first login.Database Security Rules
{
"rules": {
"volunteers": {
"$volunteerId": {
".read": "auth != null && auth.uid === $volunteerId",
".write": "auth != null && auth.uid === $volunteerId"
}
},
"admin": {
".read": "auth != null && auth.token.admin === true",
".write": "auth != null && auth.token.admin === true"
},
"assignments": {
".read": "auth != null",
".write": "auth != null && auth.token.admin === true"
},
".read": false,
".write": false
}
}
Rule-by-Rule Explanation
volunteers/$volunteerId
admin
assignments
/
(root)Admin Custom Claim
John's Firebase Auth account has a custom claim { admin: true }
set via a Node.js script using the Firebase Admin SDK. This claim is checked on the database rules side ( auth.token.admin === true
) to grant access to the admin node and write access to assignments.
How the Claim Was Set
// Node.js script — run once, outside the browser
// Uses Firebase Admin SDK (service account credentials required)
const admin = require('firebase-admin');
admin.initializeApp({ credential: admin.credential.cert(serviceAccount) });
// Set custom claim on John's UID
admin.auth().setCustomUserClaims('JOHNS_UID_HERE', { admin: true })
.then(() => console.log('Admin claim set.'));
Important: Token Refresh
Custom claims are embedded in the Firebase ID token, which is issued on login and cached for up to one hour. If the claim was just set, John must sign out and sign back in before the claim takes effect. The admin dashboard will show "access denied" until the token refreshes.
Checking the Claim in Client Code
// In portal pages that require admin access
auth.onAuthStateChanged(function (user) {
if (!user) { showView('view-unauth'); return; }
user.getIdTokenResult().then(function (tokenResult) {
if (tokenResult.claims.admin !== true) {
showView('view-unauth');
return;
}
// Admin confirmed — show dashboard
showView('view-dashboard');
loadVolunteers();
});
});
API Key Handling
Anthropic API Key (RBAMGR)
The Claude API key used for AI image intake is entered by the arbitrator in the Settings page and stored in localStorage
— never in Firebase. This keeps it off the network and out of the database entirely.
localStorage
(browser only)sk-ant-••••••••[last4]
after save. The full value is never re-rendered in the UI.Phase 1 Limitation
localStorage is device-specific. If the arbitrator uses RBAMGR on a different browser or device, they must re-enter the API key in Settings. This is documented behavior, not a bug.
Brevo API Key (Dev Team Portal)
The Brevo API key for the Dev Team Portal is hardcoded in the portal admin pages. This is acceptable because:
- The admin pages are hidden from navigation and accessible only to John
- The Brevo key only permits sending email — it cannot read existing contacts or perform destructive operations
- Brevo allows IP allowlisting and rate limiting as an additional mitigation if desired
Firebase Configuration Keys
Both Firebase projects expose their configuration objects (including API keys) in client-side JavaScript. This is expected and safe — Firebase client API keys are not secret credentials. They identify the Firebase project to the Firebase SDK and are always visible in browser source.
Security for Firebase is enforced entirely by the database security rules (which run server-side) and by Firebase Auth token validation. The API key alone cannot bypass these rules.
Firebase API Key vs. Secret Key
Firebase client config keys (the apiKey
in firebaseConfig
) are project identifiers, not authentication secrets. They restrict usage via Firebase's allowed domains, but the real protection is in security rules. The Anthropic API key, by contrast, is
a secret — hence its storage in localStorage, not in code.
Duda-Specific Security
Duda's platform introduces a few security considerations that differ from a standard web host.
- Hidden pages: All RBAMGR and portal pages are set to "Hidden from Navigation" in Duda's page manager. They do not appear in sitemaps, navigation menus, or Duda's search index. Access is URL-only.
- No server-side code: Duda widgets run entirely client-side. All Firebase interactions happen in the browser. There is no server that could log or intercept data.
- Duda password protection: Duda's Business plan supports page-level password protection as an additional optional layer. This is not currently configured but can be added to admin pages.
- Source code visibility: Because Duda renders HTML in the browser, all JavaScript (including Firebase config and logic) is visible in browser DevTools. This is inherent to client-side web apps and mitigated by robust security rules.
- Content Security Policy: Duda manages CSP headers at the platform level. Custom CSP configuration is not available on the Business plan. Firebase SDK and Google Fonts CDN domains are permitted by default.
Pre-Production Security Checklist
Run this checklist before any real case data enters RBAMGR, and before the Dev Team Portal goes live with real volunteers.
-
RBAMGR:
Replace open
".read": true, ".write": truerules with per-arbitrator UID rules - RBAMGR: Confirm Firebase Auth is enabled and arbitrator UID is set in all component files
- Portal: Verify admin custom claim is active — sign out and back in, then confirm admin dashboard is accessible
- Portal: Test that a volunteer account cannot read another volunteer's node (attempt with browser DevTools)
-
Portal:
Test that a volunteer account cannot write to
admin/or create assignments -
Both:
Confirm all invite codes are single-use and marked
used: trueafter first login - Both: Verify browser console shows zero Firebase permission errors on normal workflows
- Both: Confirm all pages are set to hidden in Duda's navigation settings
