Operator limits: hand a workspace owner over to an operator-configured page
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 12/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- typescript
- Domain
- api, authentication, backend
Research direction
Start with the blocking dependencies #268 and #275, since the operator token and the /api/operator/v1 contract come from #275. Then look at src/signed-state to judge whether it fits single-use tokens, and at the existing rate_limits storage. Done means the setting, the Manage plan route, the scoped token table, the redeem endpoint and the T01 to T12 tests all pass, with no token value ever logged.
Written by the indexing model from the issue text.
Description
Part of #268. Depends on #275.
Problem
An operator who manages plans in an external service needs the person who clicks "manage plan" in a workspace to arrive there as an identified Owner or Admin of that workspace, without a second account and without the app sharing a signing secret or personal data with the service.
Behavior
- New instance setting
plan_management_url(anhttps:URL, validated, editable by the instance admin in the portal and through the operator API). Unset: nothing below appears. - In the workspace settings (and on the plan page of #282 when it exists), an Owner or Admin sees Manage plan. It posts to
POST /api/w/<slug>/plan-handoff, which creates a handoff token: 256 bits of randomness, stored only as a SHA-256 hash, valid 5 minutes, single use, bound to the workspace, the acting user and the user's role at that moment. The app redirects the browser toplan_management_urlwith the token in the query (?handoff=<token>). A Member does not see the button, and the route refuses with the role error. - The external service redeems it with
POST /api/operator/v1/handoff/redeem{ "token": "…" }using its operator token. A valid token answers{ workspaceId, workspaceSlug, userId (opaque), role }and is used up in the same statement; an unknown, used, expired or revoked token gets one answer,invalid_token. The answer holds no email, name or repository. - The role is checked again at redemption (an Owner demoted within the 5 minutes gets
invalid_token). A new token for the same user and workspace invalidates the unused ones. - The route is rate limited per user and workspace (existing
rate_limits), same-site only, and logs the workspace id and result code, never a token. - The external service returns people to
/w/<slug>/settings/plan; no open redirect exists on the app side (the return target is fixed).
Scope
Setting, route, token table (scoped), redeem endpoint and contract entry, button and copy, docs, tests. Reuse src/signed-state if it fits the single-use semantics; otherwise a small dedicated table.
Out of scope
Anything the external service does with the identity, and sessions on its side.
Test cases
| ID | kind | level | case | expected |
|---|---|---|---|---|
| T01 | regression | integration | plan_management_url unset |
no button, the route is a missing path |
| T02 | behavior | integration | Owner clicks Manage plan | redirect to the configured URL with a token; hash stored, not the token |
| T03 | behavior | integration | redeem a fresh token | workspace, role, opaque user id returned; token used up |
| T04 | security | integration | redeem twice, after 5 minutes, unknown | invalid_token, one answer for all |
| T05 | security | integration | role removed before redeem | invalid_token |
| T06 | security | integration | a Member opens the route | refused like any other role-restricted route; no token created |
| T07 | security | integration | cross-site POST | refused |
| T08 | security | integration | rate limit exceeded | refused with Retry-After |
| T09 | security | integration | redeem without the operator token | 404/401 as #275 |
| T10 | security | integration | answers and logs | no token value, no email |
| T11 | negative | unit | URL that is http:, has credentials or a fragment |
refused on save |
| T12 | behavior | integration | a newer token for the same user and workspace | the older unused one is invalid |
- Dominant language
- TypeScript
- Stars
- 0
- Forks
- 1
- Avg merge
- 1h 15m
- Merged PRs (30d)
- 217
Getting set up
- Ships a Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from ExpidetGames/code-reviewer
-
area: app type: bug
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
ExpidetGames/code-reviewer#308 ·
Maintainers usually reply within 1 day
-
type: docs
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
ExpidetGames/code-reviewer#94 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 25/100
ExpidetGames/code-reviewer#401 ·
Maintainers usually reply within 1 day
-
area: app type: chore
Difficulty 3/5 1-2 days Newbie friendliness 45/100
ExpidetGames/code-reviewer#393 ·
Maintainers usually reply within 1 day
-
area: runner type: bug
Difficulty 4/5 3-5 days Newbie friendliness 42/100
ExpidetGames/code-reviewer#390 ·
Maintainers usually reply within 1 day
All issues in ExpidetGames/code-reviewer
Similar issues
-
bug engine
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
frostney/GocciaScript#1746 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
agilepathway/label-checker#739 · 4 comments ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 68/100
CopilotKit/OpenBot#800 ·
Maintainers usually reply within 1 day
-
Endpoint links in the tag operations list are below the 24px target sizePossibly taken @AdzerKI claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 79/100
ehmpathy/rhachet-roles-bhuild#471 ·
Maintainers usually reply within 1 day