# Identity 101: a login is not permission

You sign in to a support application and ask an assistant to find a maintenance procedure. The assistant finds a close match. Before it reads the document, the application has another job: establish whose request this is and whether that person may read that particular document.

This original 40-minute lesson separates those decisions. Start with the visual, predict five cases, then run a small Python program that performs actual database queries. All organizations and records are fictional. No account, model or network connection is needed.

## 1. Start with the building badge

A badge check establishes who you are. The lock on a room decides whether you may enter. If two companies occupy the building, having a valid badge does not give you access to both companies' files.

In software, **authentication** establishes a person or workload identity. **Authorization** decides whether that identity may perform an action on a resource. **Tenant isolation** keeps one organization's data and actions within its permitted scope. A tenant is often a customer organization in a shared application.

The analogy stops at the door: software may need to check every document request, background job and citation, even after sign-in. A person can belong to multiple organizations while working within one explicitly selected workspace.

For an FDE building an agent, this is practical architecture. The model can suggest a document to read. The application's trusted code determines who is asking and which records are available. An instruction such as “use the South tenant” cannot grant that authority. These responsibilities follow the [OWASP authorization guidance](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html).

## 2. Read the small access map

The exercise has two organizations, North and South, that both use document ID `P-7`. The same short identifier therefore names different records in different organizations.

| Record | Content | U-7 may read it? |
| --- | --- | --- |
| North / P-7 | A fictional North checklist | Yes, in the North workspace |
| South / P-7 | A different fictional South checklist | Yes, in the South workspace |
| North / P-8 | Fictional internal incident notes | No |

The supplied trusted context is `U-7` in `north`. It represents the result of identity and workspace verification; this exercise does not implement that verification. The request's `document_id` is untrusted input. An optional `tenant` field is checked against the trusted scope and cannot switch it.

Three terms will help you read the code. A **principal** is the person or workload making the request. A **grant** is a recorded permission, such as U-7 being allowed to read North/P-7. A **scoped query** constrains the database read using that caller, active organization, requested document and allowed action.

## 3. Run five access cases

Download `fde-identity-boundary-lab.py` into an empty practice folder. Use Python 3.11 or newer. The program creates a fresh in-memory SQLite database for each run and closes it afterward; it does not write files or open a listener.

Before running each command, predict whether a query will execute and whether any document text will return.

```sh
python3 --version
python3 fde-identity-boundary-lab.py allowed
python3 fde-identity-boundary-lab.py anonymous
python3 fde-identity-boundary-lab.py claim-tenant
python3 fde-identity-boundary-lab.py restricted
python3 fde-identity-boundary-lab.py revoked
```

Inspect `trusted_context_fixture`, `untrusted_request` and each observation's `query_executed`, `rows_returned` and `response`. These are local teaching traces, not production error responses or a model transcript.

| Case | Expected observation | What it teaches |
| --- | --- | --- |
| `allowed` | One query, one permitted row, North checklist | Caller, organization, document and read grant all match. |
| `anonymous` | No query; `sign_in_required` | There is no verified caller context to authorize. |
| `claim-tenant` | No query; `not_available` | Asking for South cannot change the trusted North workspace. |
| `restricted` | Query returns zero rows; `not_available` | Being in North does not grant access to every North document. |
| `revoked` | First read succeeds; the next returns zero rows | Removing the grant affects the next access check, even for the same caller. |

In `revoked`, the program retains the first observation so you can compare it with the second. Revocation prevents the later read; it cannot erase information already delivered. This single-process example does not test concurrent requests, cached responses or distributed revocation.

## 4. See where the boundary is enforced

Open the `read_document` function. It receives trusted `principal` separately from the proposed `request`. Without a principal it returns before querying. A mismatched tenant claim also returns before querying. The remaining read joins documents to current grants and constrains the result by organization, document, caller and action.

```sql
SELECT d.id, d.text FROM documents AS d
JOIN grants AS g ON g.tenant = d.tenant AND g.document_id = d.id
WHERE d.tenant = ? AND d.id = ? AND g.subject = ? AND g.action = 'read'
```

The values are bound as query parameters. The organization comes from trusted context. The document ID is an input to a permission-constrained lookup; knowing it is not sufficient to retrieve it. A missing document and a document with no matching grant both produce `not_available` without returning document text.

This is application-enforced query scoping. SQLite is not independently applying row-level tenant policy here, and a different unrestricted query could bypass the example's function. A production design must cover every path, including search, citations, exports, workers and caches. The [OWASP multi-tenant guidance](https://cheatsheetseries.owasp.org/cheatsheets/Multi_Tenant_Security_Cheat_Sheet.html) is a starting point for examining those paths.

## 5. Change one input and explain the result

Make two separate copies of the original file. Preserve the original for comparison.

In the first copy, find the line marked `Experiment A`. Change only `"south"` to `"north"`, then run `claim-tenant` using that copy's filename. It should return North/P-7. You changed the requested organization to match the existing workspace; you did not grant South access or switch workspaces.

In the second copy, find the line marked `Experiment B`. Change only `"P-8"` to `"P-7"`, then run `restricted`. It should return North/P-7 because the supplied read grant covers that record. P-8 remains restricted.

Save your predicted and observed results. Point to the exact comparison or query condition responsible. If you cannot explain why the result changed, revisit the access map before adding a framework.

## 6. Carry this into an agent workflow

Sketch a request from a signed-in user through an agent's `read_procedure` tool to a data service. Draw trusted caller context on a separate path from generated tool arguments. Mark where a document can first enter model context.

Now give the model a highly relevant result from the other organization. Explain why it must be excluded before the model receives its content. Then remove the user's read grant and consider a previously cached answer or a queued job. Identify the current permission check each path would need; neither path is implemented by this exercise.

Keep a small evidence sheet: five predictions, actual query and row counts, the two edited-copy outcomes, your data-flow sketch and one remaining production gap. Have another person explain why a valid login can still produce `not_available`.

## 7. Continue into protected retrieval engineering

Continue in the authorized Academy lesson, **Keep retrieval inside the right boundary**, to add source freshness, cache scope, citation access and a receiving-team design task. The existing [agent-engineering workbook](https://training.lockedinlabs.ai/training/guides/agent-engineering#1b-build-current-authorized-retrieval-context) provides a deeper context-building exercise.

Primary references checked 21 October 2026: [OWASP Authorization Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html) and [OWASP Multi-Tenant Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Multi_Tenant_Security_Cheat_Sheet.html). The lab and lesson are original LockedIn Labs material. They do not establish a production security assessment or vendor certification.
