MDZelo
首页/Indie Hacker Guide/How to Build a Permission System

How to Build a Permission System

When people build their first permission system, they often start with roles: admin, regular user, member, guest, super admin. The role list grows, condition checks appear everywhere in the codebase, and eventually nobody is sure who can do what.

The core of permissions is not role names. It is controlling who can perform which action on which resource. Once this sentence is clear, permission design becomes much simpler: who is the subject, what is the resource, what are the actions, and where is the boundary?

Early products do not need complex RBAC, ABAC, multi-tenant policies, or a permission configuration admin on day one. But they should avoid hardcoding permissions into pages from the beginning. Otherwise, payment, teams, admins, and APIs become painful later.

Separate Authentication and Authorization

Before designing permissions, separate two concepts: authentication and authorization.

Authentication answers “who are you?” Users prove their identity through email, password, verification code, or Google login. Authorization answers “what can you do?” Can this user view a project, export a report, invite members, or manage billing?

Many permission problems happen because authentication is treated as authorization. A logged-in user should not access every resource. A paid user should not necessarily manage team billing. A user who can view a list should not necessarily delete data.

So the boundary must be clear: login only means identity is established. Every important operation still needs permission checks. The frontend can hide buttons based on permissions, but real authorization must happen on the backend.

Define Permissions as Resource + Action

The clearest way to express a permission is resource plus action.

Resources are objects that can be accessed or managed: projects, reports, files, teams, orders, API keys, and configuration items. Actions are what users do to resources: view, create, edit, delete, export, share, invite, pay, and cancel subscriptions.

Do not start with many abstract permission names. List your resources first, then list the allowed actions for each resource:

text
report:view
report:create
report:export
project:update
team:invite
billing:manage
admin:user_view

This naming is plain but clear. Later, roles, plans, and team identities can all map to these permissions.

The closer permission names are to resources and actions, the less likely the system is to lose control.

Roles Are Permission Bundles

A role is not the permission system itself. It is a bundle of permissions. An “admin” role may include user management, order viewing, task retry, and configuration editing. A “member” may only view and create projects. A “viewer” may only see shared results.

Early products can start with a small set of roles: Owner, Admin, Member, and Viewer. Do not design a dozen roles at the beginning. More roles mean more testing cost and more user confusion.

Each role should be explainable in one sentence: what it is responsible for and what it cannot do. Owner can manage billing and members. Admin can manage projects and content but not billing. Member can create and edit their own content. Viewer can only view.

If a role cannot be explained clearly, it is probably a patch for a special case.

Permissions Must Check Resource Ownership

Many permission bugs are not caused by wrong role names. They are caused by missing resource ownership checks.

If user A visits /reports/123, the system cannot only check whether A is logged in. It must check whether report 123 belongs to user A, belongs to A's team, or has been shared with A. Otherwise, changing an ID may expose another user's data.

This is an authorization vulnerability, and early products often miss it. Reports, files, orders, invoices, API keys, and private projects must check resource ownership.

Permission checks should happen in data access and business operation layers, not only in page routing. Hiding a button does not stop a crafted request. A secure system checks resource ownership and action permission on every critical backend operation.

Free and Paid Plans Are Also Permissions

Many builders write plan limits as scattered business logic instead of permissions. Free users cannot export, can generate only 5 times per day, cannot use advanced models, or cannot save history. These are permissions or limits.

If plan capabilities are included in the permission model from the beginning, pricing and upgrades become easier:

text
export:basic
export:watermark_free
model:advanced
history:save
quota:monthly_100

Quota-style permissions may not always belong in a fixed permission table. Some are better handled through usage and limit tables. But the idea is the same: whether a user can perform an action depends on identity, plan, resource ownership, and current usage.

Do not scatter paid checks across frontend components. Later pricing changes will become a painful full-site search.

Admin Permissions Need More Restraint

Admin permissions are more sensitive than user-facing permissions. Admins can view users, change states, handle orders, retry tasks, and modify configuration. Mistakes or over-permission can have large impact.

Early admin can start with simple levels: admin, read-only admin, and super admin. Admins handle users and tasks. Read-only admins can view only. Super admins can modify configuration and sensitive data.

Every high-risk admin action should have an operation log: changing quota, adjusting order status, retrying tasks, switching configuration, or banning users. Each record should include operator, time, target, and before-after state.

Admin permissions do not need to be complex from the beginning, but they must be traceable.

A Minimal Permission Model

An early SaaS can use this model:

text
User: user identity
Team: team or workspace, optional
Resource: project, report, file, order, etc.
Role: Owner / Admin / Member / Viewer
Permission: resource:action
Plan: Free / Pro / Team
Usage: current usage
Policy: identity + role + plan + resource ownership + usage => allowed or denied

The first version may not need Team. A personal product can start with User, Resource, Plan, Usage, and Permission. Introduce Team and member roles only after collaboration becomes a real need.

The key is to keep permission logic centralized. Ideally, all critical operations call the same permission function or policy layer.

Summary

A permission system is not a list of role names. It is the combination of subject, resource, action, and boundary. Separate authentication from authorization, define permissions by resource and action, treat roles as permission bundles, and check resource ownership, plan benefits, and usage limits.

An early product permission system should be simple but not casual. The frontend can hide entries, but the backend must enforce access. If permission checks are centralized from the first version, teams, payments, admin, and APIs will be much easier later.

Homework

  • List the 5 most important resource types in your product.
  • Write the actions users can perform on each resource.
  • Design no more than 4 roles and describe what each role cannot do.
  • Check whether every detail page and API validates resource ownership.
  • Turn plan limits into permission or quota rules.

Next Lesson

How to Build Your First Database: do not try to define every field upfront. Start with core objects and core flows, then make the data structure easy to evolve.

评论

0

分享你对这一章的想法

还没有评论成为第一个分享想法的人。