How to debug customer permission issues without admin shortcuts

How to debug customer permission issues when a feature works for admins but not customers: compare role, tenant, flag and entitlement inputs safely.

When a customer can’t use a feature that works for you, the tempting fix is to log in as an admin, confirm it works, and bump the customer’s role. That hides the bug instead of explaining it. To debug a customer permission issue properly, start from the exact action the customer can’t perform and compare every input to that authorization decision, while leaving the customer’s permissions untouched.

This guide is about user permissions inside your own SaaS application. Cloud IAM, database roles and admin consoles such as ServiceNow have their own debugging tools.

Problems like this are a common reason teams want to log in as the customer. One Directus developer asking for impersonation on GitHub (opens in a new tab) described regularly facing role- or user-specific problems, with dummy users or a temporary password reset as the only workarounds. Often you don’t need account access at all, as long as you work through the evidence in order.

Define the missing capability precisely

Turn “permissions are broken” into something testable:

A member of workspace Cedar can’t open a saved report they should be able to view. The report menu is missing, and the report’s read endpoint returns 403.

Name the resource, the action, the workspace, the time and the rule you expect to allow it. Viewing a report, creating one and exporting its rows are different permissions. Don’t lump them together as “report access”.

Then confirm the intended behavior with the product owner. A correct denial under a plan rule nobody remembered isn’t an authorization bug. Note where the expectation comes from: a spec, a configured role policy or the customer’s contract.

Start with the backend decision

Find the failing request by timestamp and request ID, and check whether it reached the backend at all. If it didn’t, look at UI gating, client configuration or how the request is built before blaming server permissions.

If the backend denied it, capture the denial reason and policy version. A 403 is a symptom, not a root cause. Which check failed: tenant membership, resource ownership, role grant, entitlement or something else?

Compare what the UI believed with what the backend resolved. Keep role names separate from effective grants, because a role label doesn’t describe every object-level restriction or per-user policy.

If your logs can answer these questions, you may not need to open the customer’s account at all. Keep tokens, cookies, private request bodies and full customer records out of the ticket. Store identifiers in an access-controlled investigation record instead.

Worked example: a stale entitlement

Here’s a made-up example to show the method. Cedar and Birch are fictional workspaces.

Say the rule for reading a saved report requires four things: tenant membership, a role grant, an enabled feature flag and an active entitlement. Assume the real cause is a stale entitlement projection, not a missing role.

InputCedar memberYour admin accountWhat to check
TenantMember of Cedar; report belongs to CedarDifferent admin contextMembership and the resource’s tenant, independently
RoleAnalyst with reports.readAdministratorEffective grants, not role labels
Feature flagsaved_reports on for CedarOn for the admin workspaceEvaluation context and flag version
EntitlementSubscription active; permission projection says inactiveActiveEntitlement source and when the projection last updated

Making the customer an admin would bypass the entitlement check, so the report would appear and the real problem would stay hidden. Instead, keep the role as it is and check whether the subscription record and the authorization projection agree on the workspace, feature and effective time.

The stale projection is still only a hypothesis at this point. The next step is to test it.

Use a diagnostic worksheet

Fill in one worksheet per failing action. Leave unknowns blank rather than guessing from an admin session.

FieldWhat to enter
Expected behaviorExact action and the rule that should allow it
Incident contextTimestamp with timezone, app version, request ID
Subject and tenantCustomer user ID, verified membership, resource’s tenant
AuthorizationRole, effective grants, object restrictions, policy version
EntitlementSource value, derived value, effective time, last refresh
Feature flagKey, evaluated result, evaluation context, rule version
UI and backendWhat the UI showed, the actual request, the denial reason
Then vs nowInputs at incident time compared with inputs today
Next experimentOne input to change, expected result, reviewer
ResultWhat happened, or “not run yet”

If someone does inspect the live account, record who it was and the approved scope too. This identifies the investigator. It doesn’t change the customer’s permissions.

Test one input at a time in staging

Rebuild the situation with synthetic data and change one input per run. Keep the analyst role constant unless the role is what you’re testing. The expected results below are pass conditions to check, not results we’ve run.

ExperimentExpected resultWhat it tells you
Baseline: membership, read grant, flag on, entitlement activeReport loadsYour fixture is valid
Same, but entitlement projection inactiveDeniedEntitlement is part of the decision
Same, but flag offFeature hidden per flag policyRollout gating is separate from entitlement
Same, but read grant removedDeniedRole enforcement works
Cedar user requests a Birch reportDeniedTenant isolation holds even with other inputs valid
Fix only the entitlement projectionReport loads, if the hypothesis is rightSupports the hypothesis; it doesn’t prove the fix
Set the projection back to inactiveDenied againThe changed input explains the difference

Record the backend decision after each run and check for side effects. Before changing production, find out why the projection went stale, and test that the normal update path won’t let it happen again.

When to look at the live account

Sometimes the worksheet still has a gap that only the customer’s current state can fill, such as which records they can see or what a tenant setting is set to right now. That’s when an authorized, read-only look at the account helps.

Do it without shortcuts:

  • Don’t raise the customer’s role, borrow an admin credential or reset their password.
  • Use a time-limited, approved, read-only session and inspect only what the hypothesis needs.
  • Keep exports and sensitive actions blocked. See read-only user impersonation for what to block.
  • End the session and revoke its credential when you’re done.

With Devora, the session’s read-only scope is enforced by a backend guard that reads verified context (opens in a new tab) on your protected routes. Your app still runs its normal tenant and permission checks, and must revoke its own credential (opens in a new tab) when the session ends.

Remember that what you see today describes today’s state. It won’t recreate last week’s flag rollout, entitlement value, device or network. If the open question is historical, read when session replay isn’t enough.

Hand engineering an explanation, not a workaround

Send the completed worksheet, the denial evidence, the fixture inputs, the actual test results and what’s still unknown. Say clearly what’s confirmed, what’s a hypothesis and what hasn’t been run. If historical evidence is missing, say so.

Often, logs and a narrow support view are enough. When you do need to inspect a live account, review the security model and evaluate a read-only Devora session in staging. That way you get the access without changing the customer’s privileges.

Sources

Quick answers

Common questions, answered clearly.

Direct answers to the questions teams usually ask when evaluating this approach.

Why does a feature work for an admin but not a customer?

The two accounts usually differ in role, tenant membership, resource grants, plan entitlements or feature-flag results. Identify the exact action that fails and compare each input to its authorization decision. Admin success only shows the feature works for admins.

Should we give a customer admin access to debug a permission issue?

No. Elevating the customer changes the conditions you are investigating and can expose unrelated data or actions. Keep their permissions unchanged and test one hypothesis at a time with synthetic accounts in staging.

What should a permission investigation record?

The action, timestamp, resource and tenant, a request ID, the effective role and grants, entitlement and flag results, the policy version and the backend's denial reason. Don't copy tokens, cookies or full customer records into tickets.

Does impersonating the customer reproduce the original incident?

No. Logging in as the customer shows how the account behaves now. To explain what happened earlier, you need evidence from that time, such as backend logs, configuration history and recorded diagnostics.

Put it into practice

Give your team context.Keep control of access.

Bring approvals, scoped sessions, and access history into your support workflow.