HarperFast / HarperFast/harper

Token operation scope is not enforced on the REST/GraphQL resource path

Open
#2,201 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.