Skip to content

[Product Gap] Replace full JWT query tokens with scoped ephemeral access grants #413

Description

@seonghobae

Buyer-visible problem

ScopeWeave currently accepts the user's full session JWT in the query string for project calendar, server-sent-event, and attachment-view endpoints because calendar clients, EventSource, and new-tab document viewing cannot reliably set an Authorization header:

  • /api/projects/:id/calendar.ics?token=...
  • /api/projects/:id/stream?token=...
  • /api/projects/:id/attachments/:attachment_id/view?token=...

Even after database-backed session revocation is enforced, a full bearer token in a URL can leak through browser history, reverse-proxy and access logs, monitoring traces, copied URLs, referrers, support screenshots, and incident artifacts. The leaked credential also carries broader session authority than any one endpoint requires.

This is an enterprise procurement and security-review blocker: buyers should not have to accept a seven-day general session credential in observability and URL surfaces merely to subscribe to a project stream, calendar, or converted document.

Standards basis

RFC 6750 documents the high likelihood that URI-carried bearer tokens are logged and says the URI method should not be used unless header or body transport is impossible. RFC 9700 updates the OAuth 2.0 security best current practice from deployment experience and prohibits clients from passing access tokens in URI query parameters. RFC 8725 requires explicit algorithm verification, validation of every cryptographic operation, subject validation, and explicit typing for new JWT uses.

The unavoidable URL transport should therefore contain a narrowly scoped opaque grant, not the user's general session JWT and not another general-purpose JWT.

Modular service boundary

Implement a provider-neutral access-grant domain module whose policy and state transitions do not depend on Hono, SQLite, Clearfolio, or the browser client. ScopeWeave may call it in-process, while naruon or a future gateway may consume the same contract through an adapter or standalone service.

Required ports:

  • AccessGrantRepository
  • AccessGrantClock
  • AccessGrantRandomSource
  • AccessGrantAuditSink
  • ProjectAuthorizationPort
  • MembershipRevocationPort

The initial SQLite adapter and future PostgreSQL adapter must pass the same contract test suite. Framework routes remain thin orchestration layers.

Required data objects

Every database object must contain at least two lexical words and use snake_case.

  • access_grants
  • grant_consumptions
  • grant_revocations
  • calendar_subscriptions
  • subscription_rotations
  • subscription_usage_events

Do not create single-word tables, views, indexes, triggers, or migration-ledger objects. Foreign-key and index names must remain descriptive and contain at least two lexical words.

Required design

Create an authenticated exchange endpoint that mints a short-lived, resource-bound, purpose-bound access grant:

  • POST /api/projects/:id/access-grants
  • authenticated with the normal header JWT or PAT
  • explicit purpose: stream, calendar, or attachment_view
  • grant bound to one project_id, one user, one purpose, a strict audience, and—when applicable—one attachment_id
  • maximum TTL of 5 minutes for stream bootstrap and attachment viewing, with a separately justified calendar-subscription model
  • cryptographically random opaque value; store only a hash server-side
  • one-time use for stream bootstrap and attachment redirect where practical
  • immediate revocation on logout-all, password change, membership removal, project deletion, attachment deletion, or explicit grant revocation
  • never return the full session JWT in any generated URL

For long-lived calendar subscriptions, use a separately revocable random subscription secret with project-only read authority, last-used metadata, rotation, and explicit user-facing revoke controls. Do not reuse the short-lived session JWT.

Security requirements

  • deny grants for inaccessible projects and attachments without revealing tenant existence
  • reject wrong purpose, audience, project, attachment, expired, used, revoked, or malformed grants with 401
  • require TLS in deployment documentation
  • set Cache-Control: no-store on exchange responses and Cache-Control: private, no-store on URL-token endpoint responses
  • redact grant values from structured logs, audit metadata, metrics labels, error responses, and downstream document-view URLs
  • set a strict same-origin referrer policy and assert that cross-origin navigations cannot transmit credentials
  • use constant-time hash comparison where applicable
  • cap active grants per user/resource and rate-limit minting
  • prevent an attachment-view grant from accessing JSON APIs, SSE, calendar feeds, another attachment, another project, or another tenant
  • model concurrent one-time redemption as an atomic state transition; two simultaneous consumers must produce exactly one success
  • retain only the minimum audit metadata necessary for enterprise investigation and privacy obligations

Product requirements

  • client automatically exchanges the authenticated session for a stream grant before opening EventSource
  • calendar UI creates, names, copies, rotates, and revokes subscription URLs
  • attachment UI exchanges the authenticated session for a one-time or very short-lived view grant immediately before opening a new tab
  • show creation time, last-used time, expiry, purpose, bound resource, and status without ever redisplaying stored secrets
  • explain that revocation immediately prevents future access
  • preserve PAT header access for API clients that can use headers
  • provide accessible keyboard, screen-reader, touch, narrow-viewport, loading, error, expired, revoked, and empty states
  • design the calendar-subscription and grant-management flows in Figma before UI implementation; keep generated screens aligned with the production design system

Realistic verification

  • regression tests prove the full session JWT is rejected in query parameters after migration
  • stream grant succeeds exactly for its project and purpose, then expires or becomes unusable according to policy
  • two concurrent redemptions of a one-time grant yield one success and one rejection
  • attachment-view grant redirects only for its bound attachment and cannot be replayed beyond policy
  • calendar subscription secret cannot access JSON APIs, SSE, attachments, another project, or another tenant
  • logout-all, password change, membership removal, project deletion, and attachment deletion invalidate relevant grants
  • secrets never appear in request logs, audit exports, redirect locations, metrics labels, traces, or error payloads
  • URL and response cache/referrer headers are asserted in browser and API tests
  • time tests cover leap days, clock skew policy, exact expiry boundaries, and monotonic use-duration calculations
  • restart tests prove hash-only persistence and revocation durability
  • migration tests start from a populated pre-grant database and verify idempotence, rollback, foreign keys, and integrity
  • SQLite and PostgreSQL adapters run the same repository contract suite
  • at least 100 active grants across tenants are exercised to verify tenant isolation, per-subject quotas, and bounded cleanup
  • 100% production statement, branch, function, and line coverage for every new grant module and adapter
  • complete JSDoc/docstrings and API documentation
  • threat model, architecture decision record, operational runbook, privacy analysis, and CHANGELOG.md update

Delivery order

  1. Merge the Hono/attachment refresh security train.
  2. Merge strict query-token revocation and claim validation.
  3. Produce the Figma interaction contract for grant and calendar-subscription management.
  4. Implement the framework-independent domain and repository contract tests.
  5. Implement SQLite and PostgreSQL adapters.
  6. Integrate stream, calendar, and attachment routes one purpose at a time.
  7. Reject full session JWT query transport only after each client migration is proven.
  8. Version and release only when migration, rollback, browser, API, security, and accessibility gates all pass on the exact protected head.

References (APA 7th)

Jones, M., Bradley, J., & Sakimura, N. (2015). JSON Web Token (JWT) (RFC 7519). Internet Engineering Task Force. https://doi.org/10.17487/RFC7519

Jones, M. B., & Hardt, D. (2012). The OAuth 2.0 authorization framework: Bearer token usage (RFC 6750). Internet Engineering Task Force. https://doi.org/10.17487/RFC6750

Lodderstedt, T., Bradley, J., Labunets, A., & Fett, D. (2025). Best current practice for OAuth 2.0 security (BCP 240; RFC 9700). Internet Engineering Task Force. https://doi.org/10.17487/RFC9700

Sheffer, Y., Hardt, D., & Jones, M. (2020). JSON Web Token best current practices (BCP 225; RFC 8725). Internet Engineering Task Force. https://doi.org/10.17487/RFC8725

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: apiAPI, protocol, event, or external contractarea: authAuthentication, authorization, identity, or tenant isolationarea: dataDatabase, schema, migration, ETL, or lineagearea: securitySecurity boundary, hardening, or vulnerability preventionpriority: mediumNormal-priority or P2 workscope: product-gapCustomer-visible product gapstatus: triagedOpen issue has an organization taxonomy assignmenttype: featureNew or expanded product capability

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions