Server API: OpenAPI OAuth scopes and endpoints don't match what the server uses; a disallowed client id returns 500 instead of 401

オープン
#1,416 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
46/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
aws, clojure, openapi

調査の方向性

Start with deps/db-sync/src/logseq/db_sync/worker/routes/semantic.cljs for the OpenAPI scopes and OAuth URLs, then compare them with the pool discovery document and the existing OpenAPI tests. For the 500, read auth.cljs, authorization.cljs, and dispatch.cljs, and run the db-sync test suite, including the described auth-claims case. Done means scopes and endpoints match the pool and operations, and disallowed client IDs return 401 without debug fields.

索引モデルが issue の本文から書いたものです。

説明

Search first
  • I searched and no similar issues were found

Searched logseq/db-test and logseq/logseq (open and closed; issue search plus a title scan of the latest 1000 issues in each) for: openapi, oauth, scopes, api-docs, api.logseq.io, dispatch error, aud not found. Not the same as #1055 (the CLI derives its OAuth endpoints from a custom sync origin) or #1023 (a graph-specific Durable Object dispatch error 500).

What Happened?

Three problems with the Logseq Server API (https://api.logseq.io, added in logseq/logseq#12896), found while working out how to test it with a real OAuth token:

1. The OpenAPI security scheme declares scope names the operations don't use. components.securitySchemes.oauth declares the scopes read and write. Every operation requires logseq/read or logseq/write, and so does /.well-known/oauth-protected-resource. A client that takes its scopes from the security scheme requests names the server never checks for.

2. The OpenAPI security scheme points at OAuth endpoints that the user pool does not publish. authorizationUrl and tokenUrl are the pool issuer plus /oauth2/authorize and /oauth2/token, on cognito-idp.us-east-1.amazonaws.com. The pool's own OpenID discovery document lists the endpoints on its hosted-UI domain (logseq-prod.auth.us-east-1.amazoncognito.com). A plain request to each URL from the spec returned HTTP 400, while the hosted-UI authorize URL returned 302. I did not attempt a full authorization flow against either.

3. A well-formed token whose client id is not allowed gets HTTP 500 dispatch error, not 401. With the correct issuer and a client id that is not on the allowlist, GET /api/v1/graphs returns 500 with debug-message: "aud not found" and the internal debug-* fields from the dispatch error handler. A token with the wrong issuer, an unparseable token, and an allowed client id with a bad signature all return 401. The source lists "aud not found" as a recoverable auth error that should become a 401.

Reproduce the Bug

All requests are unauthenticated or use an unsigned, made-up token; none needs an account.

  1. Scope names (problem 1):
    curl -s https://api.logseq.io/openapi.json | jq '.components.securitySchemes.oauth.flows.authorizationCode.scopes | keys'
    curl -s https://api.logseq.io/openapi.json | jq '.paths["/api/v1/graphs"].get.security'
    
    First prints ["read","write"], second prints [{"oauth":["logseq/read"]}].
  2. Endpoints (problem 2):
    curl -s https://api.logseq.io/openapi.json | jq '.components.securitySchemes.oauth.flows.authorizationCode | .authorizationUrl, .tokenUrl'
    curl -s https://cognito-idp.us-east-1.amazonaws.com/us-east-1_dtagLnju8/.well-known/openid-configuration | jq '.authorization_endpoint, .token_endpoint'
    
    The first pair is on cognito-idp.us-east-1.amazonaws.com/.../oauth2/..., the second pair on logseq-prod.auth.us-east-1.amazoncognito.com/oauth2/.... Then curl -s -o /dev/null -w '%{http_code}\n' <each URL from the first pair> returns 400.
  3. The 500 (problem 3). Build an unsigned token with the pool issuer and a client id that is not allowed:
    TOKEN=$(node -e 'const b=o=>Buffer.from(JSON.stringify(o)).toString("base64url");console.log(b({alg:"RS256",kid:"x"})+"."+b({iss:"https://cognito-idp.us-east-1.amazonaws.com/us-east-1_dtagLnju8",client_id:"not-allowed",exp:Math.floor(Date.now()/1000)+3600})+".c2ln")')
    curl -s -w '\n%{http_code}\n' -H "Authorization: Bearer $TOKEN" https://api.logseq.io/api/v1/graphs | head -c 300
    
    Returns {"error":"dispatch error",...,"debug-message":"aud not found",...} and 500. Repeat with iss set to https://x, or with Authorization: Bearer garbage: both return {"error":"unauthorized"} and 401.
Expected Behavior
  1. The scopes in the security scheme are logseq/read and logseq/write, the same strings the operations and the protected-resource metadata use.
  2. authorizationUrl and tokenUrl are endpoints the user pool actually serves, as listed in its discovery document.
  3. A token whose client id is not allowed returns 401 unauthorized, like the other invalid-token cases, and not a 500 with debug fields.
Screenshots
Files
Browser, Desktop or Mobile Platform Information

Server side, no client involved. Probed https://api.logseq.io on 2026-10-08. Source read at logseq/logseq master ed0b0404dc (the OpenAPI document is generated from it; the live document matched). The deployed worker revision is not known.

Additional Context

Investigation (source at logseq/logseq master ed0b0404dc, checked 2026-10-08)

  1. Scope names — confirmed.
    • deps/db-sync/src/logseq/db_sync/worker/routes/semantic.cljs:395 declares the scopes as {:logseq/read "…" :logseq/write "…"}, namespaced keywords used as map keys. Operations use the strings "logseq/read" and "logseq/write" (same file, :scope entries near the top, and :security [{:oauth [scope]}]). The live document shows read and write, so the logseq/ part is dropped when the document is serialised (suspected mechanism: keyword keys lose their namespace).
    • Using string keys fixes it. I ran the db-sync suite with a test that compares the scheme's scopes with the operations' scopes: it fails before the change (#{"read" "write"} vs #{"logseq/read" "logseq/write"}) and passes after it (192 tests, 0 failures).
  2. Endpoints — confirmed for the values, suspected for impact.
    • semantic.cljs:393-394 builds both URLs as (str issuer "/oauth2/authorize") and (str issuer "/oauth2/token"), where issuer is COGNITO_ISSUER (deps/db-sync/worker/wrangler.toml, the cognito-idp URL). Nothing reads the discovery document. I did not run a client against the spec URLs, so the impact on a real OAuth client is not tested.
    • No test covers components.securitySchemes; the existing OpenAPI test only checks per-operation scopes.
  3. The 500 — confirmed live and in a unit test; cause unknown.
    • deps/db-sync/src/logseq/db_sync/worker/auth.cljs:34-35 lists "aud not found" as recoverable, and auth-claims should return nil for it. The throw is at deps/common/src/logseq/common/authorization.cljs:128.
    • In the db-sync test build (base 533ac4382a, same lines in auth.cljs), calling auth/auth-claims with an unsigned token with a valid issuer and an unknown client_id rejects with aud not found instead of resolving nil. Adding that test to the suite also made many later tests in the same run fail; I did not isolate why.
    • I did not find why the wrong-issuer case behaves differently live (401). A probe showed verify-jwt returns a promise rather than throwing synchronously for both cases.
    • The 500 body comes from deps/db-sync/src/logseq/db_sync/worker/dispatch.cljs:233-238, which returns :debug-type, :debug-message, :debug-data, :debug-json and :debug-stack for any uncaught error.
Are you willing to submit a PR?
  • I'm willing to submit a PR (Thank you!)
主要言語
言語のデータがありません
スター
29
フォーク
2
PR マージ指標
30日以内にマージされた PR はありません

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

logseq/db-test のほかの issue

logseq/db-test の issue をすべて見る

似ている issue

Backend & API Design の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。