Authdog
Log In

Security guide

Authorization & Access Control

Sign-in tells you who is calling.

It does not tell you whether they may do the thing they just asked for. Pick one server-side model for the environment — RBAC, ABAC, or FGA — put a deny-by-default check in front of every protected action, and prove that a valid session in one organization cannot reach another.

Prerequisites

  • An Authdog project with separate development, staging, and production environments.
  • Authentication that already works, and a backend that validates the session.
  • A list of the resources and actions you actually protect.
  • The job functions, customer-organization boundaries, and the exceptional access cases you already know about.
  • Test users for an allow and a deny, including a user who belongs to two organizations.

Do not design this around what the UI hides. A missing button is a courtesy. The backend check is the control.

Implementation

1. Choose one model and stay with it

Each environment runs exactly one model. Mixing them makes "why was this denied?" unanswerable.

RBAC decides from roles and the permissions on those roles. Use it when access maps to a job. It is the default. ABAC evaluates Rego policies over the subject, the resource, and the request. Use it when the rule depends on ownership, department, or an amount. FGA stores Zanzibar-style relationship tuples. Use it when access is per object and inherited, the way documents and folders work.

Write the choice down for the environment before you create the first role or policy. Changing it later means re-testing every protected action.

2. Start with a few permissions and a few roles

Name capabilities after the action, not the job: invoices:read, users:suspend. Bundle those into a small set of roles such as member, billing, and admin. A user's effective permissions are the union of every role they hold.

For enterprise customers, prefer roles that come from a group in their directory, so the directory stays the source of truth. See User provisioning with SCIM. Direct assignment is the exception you will have to review by hand.

3. Scope every customer check to one organization

Model each B2B customer as an organization inside the environment. A person can belong to several, with a different role in each. Your handler has to know which organization this request is for, and it has to load roles for that organization only.

Test the cross-organization case on purpose. Sign in as someone who is an admin in organization A and a stranger to organization B. A request for B's data must fail. A valid session is not a pass for every customer.

4. Check the permission before the handler runs

Validate the session, then check the exact permission, policy, or relationship this operation requires. Gate on invoices:read, not on a guess about what admin contains this week. If identity is missing, validation fails, or the policy service is down, stop. Do not fall through into the business logic.

You are done with a route when a missing session, a wrong organization, and a user without the capability each get a denial, and a user who has the capability gets through.

5. Reach for ABAC or FGA when RBAC starts lying

Stay on RBAC until a real requirement does not fit. Move to ABAC when the decision depends on attributes of both the caller and the resource. Keep each Rego policy small, and cover it with a case that should pass and a case that should fail. Move to FGA when access exists because of a relationship and inheritance, not because of a job title. If you are about to create dozens of roles to fake those rules, that is the moment to switch models, not to add role number fifty.

Security considerations

  • Default to deny. Grant named capabilities, not "authenticated."
  • Keep the backend check even when the client hides the button.
  • Pass the organization into every customer-scoped lookup and every decision.
  • Review direct role assignments. They outlive directory group membership.
  • Test policy errors and missing context, not only a clean deny.
  • Keep a recovery path for an operator who can still get in after you change production roles or policies.

Validation checklist

  • Environment uses the intended single authorization model.
  • Every protected action maps to a documented permission, policy rule, or relation.
  • Authenticated user without required access receives denial.
  • Missing and invalid sessions receive denial.
  • User with access in organization A cannot access organization B.
  • Group membership grants the mapped role after provisioning.
  • Policy or authorization-service errors fail closed.

Troubleshooting

The user is signed in and still denied. Look at the active organization, where the role came from, and whether the backend is checking the capability you think it is. A role named admin in the console does nothing if the handler checks invoices:read and that role does not have it.

A group mapping did not grant the role. Compare the group name or external ID, the target role, and the match type. Re-apply the rules to groups that already existed. A new mapping does not rewrite old memberships until you do.

The cross-customer test succeeded. The resource lookup is not scoped to the active organization. Fix that query before you change roles. Authorization cannot save a read that already ignored the tenant boundary.

Next steps