# Auth System Quick Reference

## Grass headers (`verify_grass_access`)

Grass injects these headers when a user authenticates via a frontend app (Workstation/ALW):

```
Grass-Account-Type: vendor | subaccount
Grass-Account-Id: <integer ID>
Orchard-Identity-UUID: <user UUID>
Orchard-Identity-Id: <user ID>
Orchard-Roles: <comma-separated roles>
```

`verify_grass_access(request, vendor=<id>, subaccount=<id>, required=False)` returns a truthy
validation object on success and a falsy one on failure. When `required=False` (default),
requests without grass headers are **implicitly authorized** — this is the most common source
of "no auth" posture.

## Access rules (`access_rules.yml`)

Each entry maps a URL path + HTTP methods to allowed `(ProfileType, roles)` pairs.
Enforcement requires `verify_access=True` AND `access_log_only=False` in `flask_request.setup`.
Many services have an `access_rules.yml` with no enforcement (`verify_access=False`) — these
are treated as 🔴 disabled for the purposes of this scan.

## PP auth (`python-pdp-sdk`)

Calls `ows-pdp` using the JWT identity from the request. Does not depend on HTTP headers for
identity — the JWT is the identity. Authorization decisions are backed by Cerbos policies in
`theorchard/ows-pdp`. See `python-microservice-add-authorization-backend` skill for wiring
details.

The PP + grass fallback pattern (PP-1518) — **compatible-mode** (in-migration):

```python
def assert_authorization(...) -> bool:
    tenant = get_tenant(...)
    if tenant and is_authorized_for_tenant(...):   # PP check
        return True
    return bool(flask_request.verify_grass_access(...))  # grass fallback
```

**⚠ Implicit-allow warning**: When `verify_grass_access` uses `required=False` (default), this
fallback still passes requests with no auth headers. This compatible-mode pattern is intentional
during migration — it lets PP-enabled callers be served by PP while grass callers continue to
work. It does **not** close the implicit-allow gap until all callers have been migrated off
grass headers and the fallback is removed or `required=True` is set.

Deploy this pattern only after baseline tests and caller analysis confirm that callers
without JWTs have a supported fallback path.
