We use cookies to ensure you get the best experience on our website.

Access denied! Locking down your app

By Dave Berner — Published

A deep dive into roles, permissions, feature flags, and entitlements

Link to this section

Controlling who can access what in your application isn’t just a nice-to-have - it’s a foundational part of building secure, scalable software.

Like many foundational things, it’s much easier to get right early than to retrofit later. Wait too long, and you’ll be left untangling logic across your UI, backend, billing provider, and feature flag tool, with plenty of opportunity for leaks and mistakes.

In this post, we’ll break down the four common types of access control:

  • Entitlements - what a user has paid for
  • Permissions - what a user can do
  • Roles - how permissions are grouped
  • Feature flags - what’s temporarily available

We’ll explain the difference between each type, when to use them, and how they work together. Then we’ll show how Kinde makes implementing them seamless, with real-world examples using the has helper and ProtectedRoute component.

Entitlements - plan-based access

Link to this section

Entitlements are tied to billing. They define what a user is allowed to use based on the plan they’re on. This includes which features are available and what limits apply.

Examples:

  • Access to the “Projects” feature
  • Limit of 3 team members
  • Ability to export data

Entitlements are often surfaced on pricing pages and enforced in the product UI and backend. In Kinde, you manage them directly through the platform.

Permissions - what can this user actually do?

Link to this section

Permissions are about user actions. They define what a user is allowed to do, regardless of plan.

Examples:

  • create:project
  • edit:team
  • delete:account

Permissions are often used in team-based apps or multi-role systems. They’re the foundation of access control logic.

Roles - simplifying permission management

Link to this section

Roles are collections of permissions. Instead of assigning permissions one by one, you assign roles that bundle them.

Examples:

  • admin: full access
  • editor: can create and update content
  • viewer: read-only access

While roles make permission management easier, they should not be used directly in your app logic. Always check for specific permissions.

Feature flags - progressive and conditional rollout

Link to this section

Feature flags control whether a feature is available to a given user, environment, or organization. They’re useful for:

  • Beta programs
  • A/B tests
  • Gradual rollouts
  • Kill switches

Feature flags are not tied to billing or permissions. They are contextual and temporary.

Practical examples using Kinde

Link to this section

Now that we’ve covered the concepts, here’s how you can implement them in code using Kinde.

const access = await has({billingEntitlements: ["projects"]});

if (!access) {
    return <AccessDenied />;
}

const projects = await fetchProjects();
const maxProjects = getEntitlementValue("projects.limit");

return (
    <>
        <ul>
            {projects.map((p) => (
                <li key={p.id}>{p.name}</li>
            ))}
        </ul>
        {projects.length >= maxProjects ? (
            <div>
                <p>You’ve reached your project limit. Upgrade for more.</p>
                <UpgradeButton />
            </div>
        ) : (
            <button onClick={createProject}>Create Project</button>
        )}
    </>
);
const canCreate = await has({permissions: ["create:project"]});
if (canCreate) {
    return <CreateProjectButton />;
}
const isManager = await has({roles: ["manager"]});
if (isManager) {
    return <ManagerPanel />;
}

Or declaratively:

<ProtectedRoute has={{roles: ["admin", "manager"]}}>
    <AdminDashboard />
</ProtectedRoute>
const access = await has({featureFlags: ["new_projects_ui"]});

return <main>{access ? <NewProjectsUI /> : <LegacyProjectsUI />}</main>;
const access = await has({
    permissions: ["view:projects", "create:projects"],
    billingEntitlements: ["projects"],
    featureFlags: ["new_projects_ui"]
});

if (!access) {
    return <AccessDeniedPage />;
}

return (
    <div>
        <header>
            <h1>Projects</h1>
            <button>Create Project</button>
        </header>
        <main>
            <NewProjectsUI />
        </main>
    </div>
);

Ready to simplify access control?

Link to this section

Whether you’re scaling a new product or wrangling a mature SaaS app, Kinde makes access control simple and powerful:

  • Built-in roles and permissions
  • Flexible entitlements for plan-based features
  • Feature flags that integrate with access logic
  • One unified has helper to rule them all

Explore the docs or sign up for free and start locking down your app the smart way.