Repository navigation
Open-issue triage and roadmap (post-2.9.0) #278
Copy link
Copy link
Open
Description
Snapshot as of 2026-10-02: 2.9.0 is released (#274). It ships #262, #263, #267, #268, #269, #270 (closes #228) and #271 (closes #264).
How to use this
- Each item gives a one-line reason, its status, its size, its dependencies, and whether an outside contributor can do it alone. "Alone" means implementing it and opening a PR without Harper-internal access. A maintainer still has to merge and release it.
- Sizes: S is under a day, M is a few days, L is a week or more including review.
- Security changes here go through a design note and a cross-model plan review before any code. The "Design (cleared …)" sections in Session invalidation is not atomic vs a concurrent token refresh — logout/expiry can be resurrected (no CAS) #212, A request that invalidates an OAuth session still runs once as the old user (global middleware next() without clearing request.user) #213 and Follow-up hardening after the account-adoption gate (GHSA-vf58 / 2.6.0) #231 are the approved plans, so read them before writing code.
- Each item appears in one section only. Blocked items are listed under Blocked, with their tier noted.
- When an item moves, update its line here. Close this issue when Tier 1 and Blocked are both empty.
Tier 1: security/correctness, do first
- Azure ?appid= form of the shared-alias keys URL isn't recognized #275: Azure
?appid=form of the shared-alias keys URL isn't recognized.- Why: this is a 2.9.0 regression for unpinned configs, which now get a misleading "set
issuer" warning and a doomed discovery attempt. A pinned config binds to Microsoft's shared key pool withissuerValidated: true, whichsrc/lib/DESIGN.mdsays must never happen. - Status: the fix with tests is in draft PR fix: recognize Azure's ?appid= form of the shared-alias keys URL #276. It needs the pre-push cross-model review, then review, merge and a 2.9.1 patch.
- Size: S. Deps: none. Outside contributor: yes.
- Why: this is a 2.9.0 regression for unpinned configs, which now get a misleading "set
- Follow-up hardening after the account-adoption gate (GHSA-vf58 / 2.6.0) #231 §3: bind adopted accounts to a stable
(iss, sub)in a plugin-ownedoauth_identitiestable, not to a verified email (OIDC Core §5.7).- Why: if an IdP reassigns an email, the new holder silently inherits the existing account's role.
- Status: designed and cleared, ready to implement. Ship the default policy, which requires pre-registration, first.
- Size: L. Deps: none for the default policy. The opt-in
allowFirstBindWithoutPreRegistrationneeds an atomic insert-if-absent write, which Add an optional ifVersion condition to request.session.update() harper#2990 does not provide (itsifVersionis fenced torequest.session.update). Defer that opt-in, or find another primitive for it. - Outside contributor: yes.
- Roleless OAuth session can inherit a role when a matching account is created later #273: a roleless trusted session can inherit a role when a matching
hdb_useris created later.- Why: sessions never expire by default, so someone who controls an email can hold a session until an admin happens to create that username.
- Status: needs a design note. First check whether Follow-up hardening after the account-adoption gate (GHSA-vf58 / 2.6.0) #231 §3's binding gate covers it, or whether core's session-to-role resolution bypasses it.
- Size: M. Deps: after Follow-up hardening after the account-adoption gate (GHSA-vf58 / 2.6.0) #231 §3; possibly a Harper core change (file a linked harper issue if so). Outside contributor: the design and plugin side, yes; a core change, no.
- Session docs overstate expiry: no default lifetime, and invalidation clears fields rather than deleting the row #277: session docs are wrong about expiry.
- Why: line 141 of
docs/token-refresh-and-sessions.mdsays Harper sessions "default 24 hours" and that an expired session "is deleted fromhdb_session". In fact there is no default expiry, and the plugin clears session fields rather than deleting the row. That leads operators to underestimate Follow-up hardening after the account-adoption gate (GHSA-vf58 / 2.6.0) #231 §1 and Roleless OAuth session can inherit a role when a matching account is created later #273. - Status: filed; needs the docs fix. Size: S. Deps: none. Outside contributor: yes.
- Why: line 141 of
Tier 2: UX/devexp
- Azure multi-tenant: allow account adoption for an allowlist of tenants without restricting sign-in #272: Azure multi-tenant, where only an allowlist of tenants can adopt accounts while anyone can still sign in.
- Why: today operators must choose between open sign-in and account adoption.
- Status: needs a design note and plan review. The tenant must be proven by the signing key's
issuerin Microsoft's JWKS, whichjwks-rsadoesn't expose. A bounded fetch likesrc/lib/mcp/jwksFetcher.tsis the likely route. - Size: L. Deps: rebase on Azure ?appid= form of the shared-alias keys URL isn't recognized #275. Outside contributor: yes.
- Two small sub-items can ship on their own (S each, outside contributor yes):
- warn when
azureAuthorizeIssuerHostreturns null for an Azure-looking host; - optionally, redirect a pinned v1 alias to the tenant's own keys instead of throwing.
- warn when
- PR feat: optionally serve OAuth endpoints as HTTP middleware #234: optionally serve the OAuth endpoints as HTTP middleware, so a catch-all handler such as Next.js with
files: '*'doesn't hide/oauth/*.- Why: in those apps login is unreachable.
- Status: stalled on requested changes since 2026-09-26. Registering with
urlPathroutes every/oauth/*request only through the new sub-chain. With the option off, every/oauth/*request returns 404 (8 integration failures); with it on, POST endpoints return 404. - Fix from the review: register without
urlPath, check the mount prefix in the handler, and callnext(request)otherwise. Add an integration fixture covering a GET login and one POST. - Size: S–M. Deps: none. Outside contributor: yes, as the author's update or a takeover.
- No issue tracks the underlying problem; file one so it survives the PR.
Tier 3: maintenance/perf
- Remove the transition-era typ: JWT acceptance on MCP access tokens #258: remove the transition-era
typ: JWTacceptance fromtypAccepted()insrc/lib/mcp/tokenIssuer.tsand updatedocs/mcp-oauth-conformance.md.- Why: RFC 9068 §4 is strict about
typ, and the transition branch has no expiry in code. - Status: time-gated. 2.9.0 is the first release that mints
at+jwt. Removal is safe only when no node older than 2.9.0 can still be minting tokens. - Target a later minor (at least 2.11) or the next major. Its CHANGELOG entry must warn that a rolling upgrade straight from below 2.9 gives up to one
mcp.accessTokenTtl(default 3600 s) of 401s. - Size: S. Deps: time only. Outside contributor: yes.
- Why: RFC 9068 §4 is strict about
Blocked (with the exact blocker)
- Session invalidation is not atomic vs a concurrent token refresh — logout/expiry can be resurrected (no CAS) #212 (Tier 1): a concurrent token refresh can resurrect a logged-out session.
- Blocker: Add an optional ifVersion condition to request.session.update() harper#2990 (open, closes Add an optional ifVersion condition to request.session.update() harper#2983) must merge and ship in a Harper release. It adds
request.session.update(data, { ifVersion })andrequest.session.version. The plugin then raises itsharperpeer minimum, because an older core silently ignores the second argument. - Unblocked now: design the plugin-side cross-node defense.
ifVersionis atomic on the local node only, andhdb_sessionreplicates last-write-wins. - Size: L in total (the plugin change is M once core ships). Outside contributor: the plugin side, yes; the core merge and release, no.
- Blocker: Add an optional ifVersion condition to request.session.update() harper#2990 (open, closes Add an optional ifVersion condition to request.session.update() harper#2983) must merge and ship in a Harper release. It adds
- A request that invalidates an OAuth session still runs once as the old user (global middleware next() without clearing request.user) #213 (Tier 1): a request that invalidates an OAuth session still runs once as the old user.
- Blocker: Add request.invalidateSessionAuthentication() for session-backed auth components harper#2984,
request.invalidateSessionAuthentication(). It is open and has no PR. - Plugin side afterwards: call it at the five continuation points, add a reachable capability check, and raise the peer minimum. The persistence-failure parts already shipped in fix: don't report a failed OAuth session clear as a completed logout #268.
- Size: core M, plugin S–M. Outside contributor: can write the core PR in the public harper repo; merging needs core maintainers.
- Blocker: Add request.invalidateSessionAuthentication() for session-backed auth components harper#2984,
- Follow-up hardening after the account-adoption gate (GHSA-vf58 / 2.6.0) #231 §1 (Tier 1): durably neutralize sessions from before the adoption gate, including on the operations API.
- Blocker: a Harper core product and operations decision between two candidates:
- Candidate A: a new
hdb_session_v2table and a cookie rename, for a rolling path. - Candidate B: a quiesced, cluster-wide purge of
system.hdb_session.
- Candidate A: a new
- Unblocked prep: test whether an old Harper node's operations API can write the new system table generically. The result decides whether Candidate A is viable.
- Interim mitigation: clear
system.hdb_session. - Size: L (core). Outside contributor: the prep, yes; the decision, no.
- Blocker: a Harper core product and operations decision between two candidates:
- CIMD: ChatGPT is refused with invalid_client (lists none in token_endpoint_auth_methods_supported, prefers private_key_jwt) #244 (Tier 2), remaining part: make interactive CIMD
private_key_jwton by default.- Blocker (external): ChatGPT still signs its client assertions with the token-endpoint URL as
aud, which RFC 7523bis forbids. The publication of RFC 7523bis is the most likely trigger for a change. - Re-run the live capture before each release. On deployments that opted in, watch the logged
aud form. - Size: S once unblocked. Outside contributor: yes, with their own ChatGPT account and a reachable test authorization server.
- Blocker (external): ChatGPT still signs its client assertions with the token-endpoint URL as
- MCP OAuth v1.1: remaining 2026-07-28 spec forward-work (SEP-2350 scope step-up) #156 (Tier 3): MCP SEP-2350 scope step-up, plus the 403
insufficient_scopechallenge deferred from Conformance audit: MCP authorization spec 2026-07-28 (docs pin 2025-06-18 — two revisions stale) #202.- Blocker: per-tool scopes, which have no tracking issue or design (Add MCP OAuth flow support #86 is closed). Today's token scoping is role-level.
- Size: L. Outside contributor: not until a scope model is decided.
Likely fixed / close candidates (with evidence)
- CIMD: ChatGPT is refused with invalid_client (lists none in token_endpoint_auth_methods_supported, prefers private_key_jwt) #244: the reported failure, ChatGPT refused with
invalid_client, was fixed by feat(mcp): verify private_key_jwt for interactive CIMD clients and bind the client-auth method to each grant #245 and hardened by fix(mcp): harden the interactive private_key_jwt setting; keep it opt-in (#244) #253, both shipped in 2.8.0 (see its CHANGELOG). The body's own status block says "Fixed". It stays open only for the default-flip gate.- Close it and open a narrow "default-on interactive
private_key_jwt" issue that carries the gate checklist, or retitle this one.
- Close it and open a narrow "default-on interactive
- Follow-up hardening after the account-adoption gate (GHSA-vf58 / 2.6.0) #231: most sections have shipped.
- §2 shipped in feat(hooks): expose authenticated-source evidence (authEvidence) to onLogin (follow-up #231) #232 (2.7.0).
- §4 and §5a–d shipped in fix: harden OAuth account-adoption gate (issuer fail-fast, UserInfo sub check, lookup-error quarantine) #263 (2.9.0). Derive the ID-token issuer from presets and OIDC discovery #271 then relaxed §4's startup hard-fail to background OIDC discovery.
- §6 is done: Integration test: prove a verified login adopts an existing account and inherits its role #230 was closed via test: adoption positive-path integration proof; sessionValidator asserts persistence #239.
- Split §1 and §3 into their own issues, copying the design sections verbatim, then close this umbrella.
- MCP OAuth v1.1: remaining 2026-07-28 spec forward-work (SEP-2350 scope step-up) #156: item 2 shipped in Honor the offline_access scope opt-in for refresh tokens (SEP-2207) #192. The final-spec re-verify is done: Conformance audit: MCP authorization spec 2026-07-28 (docs pin 2025-06-18 — two revisions stale) #202 is closed and the conformance matrix is in
docs/mcp-oauth-conformance.md.- What remains depends on unscheduled work. Retitle it as a per-tool-scopes umbrella, or close it as not planned until that is scheduled.
- No duplicates found.
- Roleless OAuth session can inherit a role when a matching account is created later #273 was split out of Follow-up hardening after the account-adoption gate (GHSA-vf58 / 2.6.0) #231 §3 on purpose.
- Session invalidation is not atomic vs a concurrent token refresh — logout/expiry can be resurrected (no CAS) #212 (persisted-row race) and A request that invalidates an OAuth session still runs once as the old user (global middleware next() without clearing request.user) #213 (in-request identity) complement each other.
- Azure ?appid= form of the shared-alias keys URL isn't recognized #275 (
?appid=) is distinct from the bare v1-alias case recorded as fixed in Azure multi-tenant: allow account adoption for an allowlist of tenants without restricting sign-in #272.
Suggested order of work
- Finish fix: recognize Azure's ?appid= form of the shared-alias keys URL #276 (Azure ?appid= form of the shared-alias keys URL isn't recognized #275): pre-push review, merge, and release 2.9.1.
- Fix the session-expiry docs (Session docs overstate expiry: no default lifetime, and invalidation clears fields rather than deleting the row #277).
- Split Follow-up hardening after the account-adoption gate (GHSA-vf58 / 2.6.0) #231 into separate §1 and §3 issues and close the umbrella. Close or retitle CIMD: ChatGPT is refused with invalid_client (lists none in token_endpoint_auth_methods_supported, prefers private_key_jwt) #244 and MCP OAuth v1.1: remaining 2026-07-28 spec forward-work (SEP-2350 scope step-up) #156.
- Implement Follow-up hardening after the account-adoption gate (GHSA-vf58 / 2.6.0) #231 §3 with the default pre-registration policy and without the first-bind opt-in.
- Write the Roleless OAuth session can inherit a role when a matching account is created later #273 design note, checking it against the Follow-up hardening after the account-adoption gate (GHSA-vf58 / 2.6.0) #231 §3 gate.
- Design Session invalidation is not atomic vs a concurrent token refresh — logout/expiry can be resurrected (no CAS) #212's cross-node defense now. Ship the plugin change once Add an optional ifVersion condition to request.session.update() harper#2990 is in a Harper release.
- Get Add request.invalidateSessionAuthentication() for session-backed auth components harper#2984 implemented (a core PR is welcome), then do A request that invalidates an OAuth session still runs once as the old user (global middleware next() without clearing request.user) #213's plugin side.
- Unstick PR feat: optionally serve OAuth endpoints as HTTP middleware #234, through the author's update or a takeover, and file an issue for the catch-all problem.
- Ship the two small Azure multi-tenant: allow account adoption for an allowlist of tenants without restricting sign-in #272 sub-items, then write Azure multi-tenant: allow account adoption for an allowlist of tenants without restricting sign-in #272's design note.
- Follow-up hardening after the account-adoption gate (GHSA-vf58 / 2.6.0) #231 §1, once Harper core picks Candidate A or B. Run the old-node table-write test beforehand to inform that choice.
- Remove the transition-era typ: JWT acceptance on MCP access tokens #258, once the time gate passes.
- CIMD: ChatGPT is refused with invalid_client (lists none in token_endpoint_auth_methods_supported, prefers private_key_jwt) #244's default flip, once ChatGPT signs with the issuer as
aud. - MCP OAuth v1.1: remaining 2026-07-28 spec forward-work (SEP-2350 scope step-up) #156, once per-tool scopes are scheduled.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Type
Fields
Priority
None yet
Activity