Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Operator limits: hand a workspace owner over to an operator-configured page

Closed
#284 0 comments 0 reactions 0 assignees View on GitHub

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

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

area: app type: feature

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 (an https: 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 to plan_management_url with 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

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from ExpidetGames/code-reviewer

All issues in ExpidetGames/code-reviewer

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.