HarperFast / HarperFast/harper
Token operation scope is not enforced on the REST/GraphQL resource path
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 200
Description
Follow-up from [#2173](https://github.com/HarperFast/harper/pull/2173) (OIDC trusted publishing), raised independently by two reviewers.
## What
A token operation scope (`tokenOperations`, set from an OIDC trust policy's `operations` field) is enforced **only** by `verifyPerms` and `verifyPermsAST` — the operations API and SQL. The application REST/GraphQL resource path authorizes through table-level `checkPermission` and never consults the scope.
So a token scoped to `deploy_component` can still read and mutate data over REST/GraphQL wherever the *role* permits it.
## Traced path
Four checkpoints, none of which consult `tokenOperations`:
1. `security/auth.ts:219` — `Bearer` accepted via `validateOperationToken`; `validateToken` attaches `tokenOperations` to the principal, so the scope *is* present on the request
2. `server/REST.ts:237` — `target.checkPermission = request.user?.role?.permission`
3. `resources/Resource.ts:854` — dispatches to `allowRead`/`allowUpdate`/...
4. `resources/Table.ts:1692` / `getTablePermissions` (`Table.ts:5522`) — consults only the role
`security/auth.ts` `handleApplication` is the app-server registration (REST/GraphQL/MQTT), distinct from the operations server, and the OIDC exchange mints `subject: TOKEN_TYPE.OPERATION` — exactly what `validateOperationToken` accepts. No token type discriminates "ops-API token" from "REST token".
## Current state
This is **documented, not silently broken**. `security/authn/oidc/types.ts` scopes the guarantee to the operations API and SQL on the `operations` field itself, and `DESIGN.md` records the same boundary plus the mitigation: point a policy's `user` at a role that is itself least-privilege for the data the token can reach. The role remains the actual authorization boundary; the scope is defense in depth.
## Options
1. **Enforce at the Resource authorization boundary** with an explicit operation→verb mapping (`read`/`create`/`update`/`delete` ↔ scope entries). Closes the gap but needs a mapping that stays correct as resource verbs evolve.
2. **Make scoped credentials operations-API-only** — reject a scoped token presented to the app server. Simpler and unambiguous, but changes what the feature is for anyone wanting a scoped REST credential.
Both change user-visible behavior, which is why #2173 documented the boundary rather than picking one.
## Related
Same surface as CORE-3061 (GraphQL bypasses the operations allowlist) — worth solving together, since both are "the resource path does not consult operation-level authorization".
Whether to keep the per-policy `operations` allowlist at all is an open design question on #2173.
Contributor guide
Research direction
Start with security/auth.ts, server/REST.ts, resources/Resource.ts, and resources/Table.ts, then read security/authn/oidc/types.ts and DESIGN.md for the documented authorization boundary. Review #2173 and CORE-3061 before choosing between resource-path enforcement and operations-API-only credentials; done requires an agreed design and corresponding REST/GraphQL authorization behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- graphql, typescript
- Domain
- api, authorization, backend, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100