From ab1c70115b50734cc9394121414e96194991db29 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 31 Jul 2026 13:19:45 +0200 Subject: [PATCH] :bug: Prevent MCP tokens from being used as access tokens (#10962) MCP tokens now use a separate JWT issuer claim (`urn:penpot:mcp-token`) instead of `access-token`, preventing them from being validated as API access tokens. Fixes #10960 AI-assisted-by: qwen3.7-plus --- backend/src/app/rpc/commands/access_token.clj | 3 ++- .../backend_tests/rpc_access_tokens_test.clj | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/backend/src/app/rpc/commands/access_token.clj b/backend/src/app/rpc/commands/access_token.clj index 90ab826e2b..0aa20ba3c1 100644 --- a/backend/src/app/rpc/commands/access_token.clj +++ b/backend/src/app/rpc/commands/access_token.clj @@ -37,7 +37,8 @@ (let [token-id (uuid/next) expires-at (some-> expiration (ct/in-future)) created-at (ct/now) - token (tokens/generate cfg {:iss "access-token" + token-iss (if (= type "mcp") "urn:penpot:mcp-token" "access-token") + token (tokens/generate cfg {:iss token-iss :uid profile-id :iat created-at :tid token-id}) diff --git a/backend/test/backend_tests/rpc_access_tokens_test.clj b/backend/test/backend_tests/rpc_access_tokens_test.clj index c164303168..bdb2f20887 100644 --- a/backend/test/backend_tests/rpc_access_tokens_test.clj +++ b/backend/test/backend_tests/rpc_access_tokens_test.clj @@ -9,6 +9,7 @@ [app.common.uuid :as uuid] [app.db :as db] [app.http :as http] + [app.http.access-token :as actoken] [app.rpc :as-alias rpc] [app.storage :as sto] [backend-tests.helpers :as th] @@ -205,3 +206,29 @@ (t/is (not (contains? all-ids (:id first-mcp)))) (t/is (not (contains? all-ids (:id second-mcp)))) (t/is (contains? all-ids (:id third-mcp)))))))) + +(t/deftest mcp-tokens-cannot-be-used-as-access-tokens + (let [prof (th/create-profile* 1 {:is-active true}) + cfg th/*system*] + + (t/testing "MCP tokens use different issuer claim" + (let [{:keys [result]} (th/command! {::th/type :create-access-token + ::rpc/profile-id (:id prof) + :type "mcp" + :name "mcp token"}) + mcp-token (:token result) + + ;; Try to decode as access token (should fail) + decoded (actoken/decode-token cfg mcp-token)] + (t/is (nil? decoded)))) + + (t/testing "Regular access tokens use access-token issuer claim" + (let [{:keys [result]} (th/command! {::th/type :create-access-token + ::rpc/profile-id (:id prof) + :name "regular token"}) + access-token (:token result) + + ;; Should decode successfully + decoded (actoken/decode-token cfg access-token)] + (t/is (some? decoded)) + (t/is (= (:id prof) (:uid decoded)))))))