Your AI tested one user. Add another.
The Other User makes coding agents test authorization by changing the identity while keeping the resource and action fixed.
An open-source Agent Skill for Claude Code, OpenAI Codex, Cursor, and compatible AI coding agents.
🌐 English · 简体中文 · Português · 日本語 · Español · Русский
If your app has accounts, private data, teams, tenants, sharing, or roles, this skill is for you.
Install · See the failure · Four rules · Benchmark · Read the skill
Authentication asks who are you? Authorization asks may this identity do this to that resource?
An AI agent can build a perfect one-user happy path and never ask the second question.
Alice creates it. Alice reads it. Alice updates it. Tests pass.
That proves Alice's flow. It says nothing about Bob.
Alice created it. Make Bob try to read, change, delete, list, export, download, and subscribe to it.
Install for Claude Code, OpenAI Codex, Cursor, or another compatible agent:
The skill is listed on skills.sh:
npx skills add Pablo-aps/the-other-userThen invoke it on an authorization boundary:
# Claude Code / Cursor
/the-other-user test whether a second account can read or update the first user's invoices
# OpenAI Codex
$the-other-user review this Supabase RLS policy with an owner and a different user
The skill can also activate when you ask an agent to test access control, object ownership, IDOR/BOLA, tenant isolation, roles, RLS, or cross-user access.
Manual install
Copy SKILL.md into a the-other-user skill directory:
| Agent | Project location | Invoke |
|---|---|---|
| Claude Code | .claude/skills/the-other-user/SKILL.md |
/the-other-user |
| OpenAI Codex | .agents/skills/the-other-user/SKILL.md |
$the-other-user |
| Cursor | .cursor/skills/the-other-user/SKILL.md or .agents/skills/the-other-user/SKILL.md |
/the-other-user |
Created invoice inv_42 as Alice.
Alice can list it, read it, and update it.
All tests pass. Authorization looks good.
VERDICT: LEAKS
Policy: only the invoice owner may read or update it.
Identity A: Alice — verified with /me
Identity B: Bob — verified with a separate session
Alice creates inv_42 -> 201
Bob lists invoices -> 200 []
Bob reads Alice's exact invoice inv_42 -> 200 LEAK
Bob updates Alice's exact invoice inv_42 -> 200 LEAK
Alice reads inv_42 after Bob's update -> changed
The list was scoped. The object endpoint was not.
The database confirms Bob changed Alice's record.
The bug was not authentication. Bob was correctly logged in. The bug appeared only when a valid identity touched another identity's resource.
| Your app has… | The second identity tests… |
|---|---|
| user-owned records | another user reading, updating, or deleting the exact object |
| teams or workspaces | a member, outsider, owner, and admin against the same resource |
| multi-tenant data | the same local ID across two tenants, caches, queues, and exports |
| sharing or collaborators | legitimate access stays allowed while unrelated access is denied |
| Supabase RLS | owner, authenticated non-owner, anonymous, service role, and intended collaborators |
| REST or GraphQL APIs | list vs direct object routes, nested resources, bulk actions, and alternate resolvers |
| files or media | metadata, signed URLs, storage paths, and the downloaded bytes |
| realtime or background work | subscriptions, events, workers, jobs, and persisted actor context |
| admin features | client-hidden buttons vs server-enforced role checks |
This is useful for vibe-coded SaaS, internal tools, marketplaces, fintech, healthcare, social apps, dashboards, and any product where one account should not silently become every account.
| 01 — NAME | 02 — SWAP |
|---|---|
Name the actors, resource, action, and intended ALLOW, DENY, or UNKNOWN policy. Do not invent product policy from the implementation. |
Keep the exact resource and operation. Change the authenticated session, token, tenant, role, or database principal—and verify the identity really changed. |
| 03 — CROSS | 04 — VERDICT |
|---|---|
| Try every real door the resource already has: list, object, write, bulk, GraphQL, signed URL, export, realtime, cache, or worker. Check authoritative state after denied mutations. | Return ISOLATED, LEAKS, NOT TESTED, or BLOCKED from evidence. Scope the claim; never say the whole app is secure. |
same resource + same action + different verified actor = authorization test
This is not a second-user test:
Alice session + Alice's token + Bob written in a test variable
Neither is this:
Alice session + a different object ID
The skill requires separate sessions, cookie jars, tokens, database roles, or request contexts, plus an authoritative identity signal such as /me, token subject, server trace, or database principal.
Authorization bugs hide in mismatched paths:
list is scoped but GET /objects/:id is global
REST is protected but GraphQL calls findById()
UI hides the button but the API accepts the request
row read is protected but signed URL minting is not
request is tenant-scoped but cache key is only object_id
single delete is checked but bulk delete skips per-item policy
API verifies the actor but the worker trusts job.userId
A 403 is not the end of a write test. The skill verifies that the database row, file, event, job, or other authoritative effect did not change.
The goal is not to deny everyone. Public profiles should stay public. Collaborators should still collaborate. Admins should retain explicitly granted power.
Existing code is not automatically the policy; it may contain the bug. The skill derives expected access from requirements, permission tables, route contracts, RLS rules, or explicit direction. If the intended rule is genuinely unresolved, the correct verdict is BLOCKED, not a guessed security policy.
| Verdict | Meaning |
|---|---|
ISOLATED |
Direct evidence covers the frozen actor × resource × action scope, including intended allowed controls, and observed access matches policy. |
LEAKS |
A denied identity reads protected data, obtains a capability, changes authoritative state, triggers a protected effect, or crosses a tenant or role boundary. |
NOT TESTED |
Only static code, mocks, one-user tests, incomplete logs, or unverified identity evidence are available. Risks remain risks—not observed leaks. |
BLOCKED |
The required policy, approved second identity, environment, data, or access is unavailable, so safe verification cannot continue. |
Every verdict stays narrow:
Verdict: ISOLATED | LEAKS | NOT TESTED | BLOCKED
Scope: <environment, resource, policy source>
Actors: <owner; second user / tenant / role>
Matrix: <actor × action × expected × observed × evidence>
Finding: <first contradiction or why proof is incomplete>
Authoritative effect: <state before and after denied mutations>
Residual risk: <untested relevant path and safest next proof>
Use only systems and accounts you own or are explicitly authorized to test. Prefer local, test, or isolated staging fixtures. Never discover or probe real third-party user IDs, files, tenants, or tokens.
The skill does not grant permission to test a target. It does not turn your coding agent into a pentest framework. If a safe second identity or isolated environment is unavailable, it stops with BLOCKED rather than experimenting on production users.
In one reproducible paired snapshot, the same model reviewed the same 16 synthetic repository fixtures with and without the skill:
| Metric | Without skill | With The Other User |
|---|---|---|
| Correct verdict | 14 / 16 (87.5%) | 16 / 16 (100%) |
Correct NOT TESTED restraint |
2 / 3 (66.7%) | 3 / 3 (100%) |
Correct BLOCKED restraint |
1 / 2 (50%) | 2 / 2 (100%) |
| Intended-access controls | 4 / 4 | 4 / 4 |
| Observed-leak cases | 7 / 7 | 7 / 7 |
| Decisive-signal recall | 77.5% | 86.3% |
| Decisive-evidence coverage | 98.4% | 97.9% |
The interesting result is not that the skill shouted “leak” more often. Both arms found all seven observed leaks and preserved all four intended-access controls. The skill fixed two different confidence errors: baseline called a static risk an observed leak, and treated an unavailable second account as merely untested instead of an explicit blocking dependency.
The snapshot used gpt-5.6-luna at low reasoning through codex-cli 0.147.0, one run per cell, on August 19, 2026. Both arms received the same model, repository artifacts, neutral task prompt, and compact verdict schema. Treatment additionally received the committed skill and the separately recorded directive Use $the-other-user for this review.
Inspect the 32 raw outputs, 16 cases, output contract, and methodology. Recompute every hash and score locally:
node benchmark/check.mjsThis is one run per cell on synthetic, development-authored cases with a declared non-secret holdout. It is evidence that the skill changed this model's behavior on this fixture set—not an independent study, a security guarantee, a model ranking, or a stable estimate of future performance.
The Other User is not:
- a penetration-testing tool or permission to probe third-party systems;
- an automated vulnerability scanner;
- an authentication or authorization framework;
- a replacement for threat modeling, code review, or domain-specific tests;
- a claim that two accounts prove an entire application secure;
- a reason to weaken legitimate public, shared, support, or administrator access.
It is one small behavioral skill:
Change the actor. Keep the resource. Test the boundary.
What is The Other User?
The Other User is an open-source Agent Skill for authorization testing. It makes an AI coding agent repeat the same resource operation with a distinct, verified test identity and compare the result with the intended access policy.
Who should use it?
Anyone using Claude Code, OpenAI Codex, Cursor, or another coding agent to build an application with accounts, user-owned data, teams, tenants, sharing, roles, RLS, private files, exports, or realtime features.
Is this the same as authentication testing?
No. Authentication verifies identity. Authorization decides whether that identity may perform a specific action on a specific resource. A correctly authenticated Bob reading Alice's private record is an authorization failure.
Does a second user prove my app is secure?
No. A verdict covers only the tested actors, resources, actions, environment, and time. The skill is designed to expose a common missing dimension, not certify an application.
Does it work with Supabase RLS?
Yes. Use separate approved identities and test the exact row through the client path and, where appropriate, the database policy context. Include legitimate collaborator, anonymous, and service-role controls only when the intended policy grants them.
Will it test production users?
It should not. The safe boundary requires owned or explicitly authorized accounts and prefers local, test, or isolated staging fixtures. Without safe access, the verdict is BLOCKED.
- OWASP API1:2023 — Broken Object Level Authorization — object-level checks for every endpoint that acts on an object ID, plus authorization tests.
- OWASP Authorization Cheat Sheet — actor × resource × operation design, per-request enforcement, and positive/negative tests.
- MITRE CWE-639 — authorization bypass through user-controlled object keys.
- Open Agent Skills specification — the portable
SKILL.mdformat. - OpenAI: Build skills — Codex skill structure, discovery, and invocation.
- Skills CLI — cross-agent installation and discovery.