{"id":1074,"date":"2022-02-16T12:07:22","date_gmt":"2022-02-16T12:07:22","guid":{"rendered":"https:\/\/editjournal.redakt.eu\/faxmodem\/?p=1074"},"modified":"2022-02-16T18:17:03","modified_gmt":"2022-02-16T18:17:03","slug":"opencart-api-session-api-id-permission","status":"publish","type":"post","link":"https:\/\/editjournal.redakt.eu\/faxmodem\/blog\/development\/opencart-api-session-api-id-permission\/","title":{"rendered":"Opencart API session, api_id and permission error"},"content":{"rendered":"<p>The official Opencart API is poorly documented, with typos and omissions: <a href=\"https:\/\/docs.opencart.com\/en-gb\/system\/users\/api\/\">docs.opencart.com\/en-gb\/system\/users\/api<\/a>. Provided you have your API key, making a simple external API request using cURL or Postman to have e.g. order status updated seems impossible and overcomplicated: you'd have to use that API key to request a token first, and then make requests using that token. The token by itself is not enough, as it gets cross-validated with <code>api_id<\/code> inside <code>$this-&gt;session-&gt;data<\/code>. Or, how Daniel <a href=\"https:\/\/github.com\/opencart\/opencart\/issues\/6202#issuecomment-344993737\">puts it<\/a>:<\/p>\n<blockquote>\n<p>ok the system is a little different compared to editing orders on the same server. you have to login then get a session id or token returned. you need to include that in each request.<\/p>\n<\/blockquote>\n<p>Due to what seems like a bug, that token <a href=\"https:\/\/github.com\/opencart\/opencart\/issues\/3557\">doesn't get linked to the required <code>api_id<\/code> properly<\/a> (despite Daniel claiming it got fixed <a href=\"https:\/\/github.com\/opencart\/opencart\/issues\/3557#issuecomment-151180908\">in 2015<\/a> and then later <a href=\"https:\/\/github.com\/opencart\/opencart\/issues\/4578#ref-commit-fbfd7ce\">in 2016<\/a>), so the requests result in a permission error -- either <code>error_permission<\/code>, or <em>Warning: You do not have permission to access the API<\/em>, depending on translation availability.<\/p>\n<p>I was dealing with the code version after <a href=\"https:\/\/github.com\/opencart\/opencart\/commit\/46b7132dc21b4f34ae807c805909b0cabaa71f2e\"><code>46b7132<\/code> from 29 Jun 2016<\/a>, and despite the commit message saying literally <em>ok the API should be working again for now<\/em>, it did not.<\/p>\n<p>The advice that's the closest to a real fix <a href=\"https:\/\/stackoverflow.com\/questions\/42198431\/opencart-api-issue-with-session-app-id-how-to-get-api-id#comment89040668_43526365\">is to manually copy <code>system\/library\/session.php<\/code> and <code>catalog\/controller\/startup\/session.php<\/code> from the latest version<\/a>, but it doesn't sound like a good idea because:<\/p>\n<ul>\n<li>this doesn't answer what exactly was the problem;<\/li>\n<li>there is too much code to copy, and the more code you copy, the higher are the chances of getting something off or messed up.<\/li>\n<\/ul>\n<p>So lets take a close look at those two. The <a href=\"https:\/\/github.com\/opencart\/opencart\/tree\/3.0.3.8\/upload\/catalog\/controller\/startup\"><code>catalog\/controller\/startup<\/code><\/a> folder shows the most recent change in <code>session.php<\/code> is a 4 year old commit <a href=\"https:\/\/github.com\/opencart\/opencart\/commit\/b390799bb60aa46ef19e44de3642bcfb6767f3b1\"><code>b390799<\/code><em>Additional fixed (requires verification) to fix issues with the API login.php and also session start-up<\/em><\/a>, which looks very relevant, yet doesn't fix the issue. But going back in <a href=\"https:\/\/github.com\/opencart\/opencart\/commits\/b390799bb60aa46ef19e44de3642bcfb6767f3b1\/upload\/catalog\/controller\/startup\/session.php\">that file's history<\/a> reveals <a href=\"https:\/\/github.com\/opencart\/opencart\/commit\/b358841d09030cc0198c6c5ef836b7bb355fb3a0\"><code>b358841<\/code><em>more token changes<\/em><\/a> right after the aforementioned <em>should be working for now<\/em>, which introduces <a href=\"https:\/\/github.com\/opencart\/opencart\/commit\/b358841d09030cc0198c6c5ef836b7bb355fb3a0#diff-deafdc2202e4b135098e93359347455d7e8742ec8bcf0910e73b155a9eb9bfc6\">the following change<\/a>. Changing those exact two lines in your copy of <code>session.php<\/code> and fixes the external API that you login to using <code>api_token<\/code> <strong>but<\/strong> at the same time it breaks those admin calls to the API, such as the one that updates the order status.<\/p>\n<p>So the solution is to combine those two: keep the original <code>if<\/code> looking for <code>$this-&gt;request-&gt;get[&#039;token&#039;]<\/code>, and add an <code>else if<\/code> from the above commit that looks for <code>$this-&gt;request-&gt;get[&#039;api_token&#039;]<\/code>, with the corresponding body. That should satisfy both cases and your <code>session.php<\/code> should look like this:<\/p>\n<pre><code>if (isset($this->request->get['token']) && isset($this->request->get['route']) && substr($this->request->get['route'], 0, 4) == 'api\/') {\n    $this->db->query(\"DELETE FROM <code>&quot; . DB_PREFIX . &quot;api_session<\/code> WHERE TIMESTAMPADD(HOUR, 1, date_modified) < NOW()\");\n\n    $query = $this->db->query(\"SELECT DISTINCT * FROM <code>&quot; . DB_PREFIX . &quot;api<\/code> <code>a<\/code> LEFT JOIN <code>&quot; . DB_PREFIX . &quot;api_session<\/code> <code>as<\/code> ON (a.api_id = as.api_id) LEFT JOIN \" . DB_PREFIX . \"api_ip <code>ai<\/code> ON (as.api_id = ai.api_id) WHERE a.status = '1' AND as.token = '\" . $this->db->escape($this->request->get['token']) . \"' AND ai.ip = '\" . $this->db->escape($this->request->server['REMOTE_ADDR']) . \"'\");\n\n    if ($query->num_rows) {\n        $this->session->start('api', $query->row['session_id']);\n\n        \/\/ keep the session alive\n        $this->db->query(\"UPDATE <code>&quot; . DB_PREFIX . &quot;api_session<\/code> SET date_modified = NOW() WHERE api_session_id = '\" . (int)$query->row['api_session_id'] . \"'\");\n    }\n} elseif (isset($this->request->get['api_token']) && isset($this->request->get['route']) && substr($this->request->get['route'], 0, 4) == 'api\/') {\n    $this->db->query(\"DELETE FROM <code>&quot; . DB_PREFIX . &quot;api_session<\/code> WHERE TIMESTAMPADD(HOUR, 1, date_modified) < NOW()\");\n\n    $query = $this->db->query(\"SELECT DISTINCT * FROM <code>&quot; . DB_PREFIX . &quot;api<\/code> <code>a<\/code> LEFT JOIN <code>&quot; . DB_PREFIX . &quot;api_session<\/code> <code>as<\/code> ON (a.api_id = as.api_id) LEFT JOIN \" . DB_PREFIX . \"api_ip <code>ai<\/code> ON (as.api_id = ai.api_id) WHERE a.status = '1' AND as.token = '\" . $this->db->escape($this->request->get['api_token']) . \"' AND ai.ip = '\" . $this->db->escape($this->request->server['REMOTE_ADDR']) . \"'\");\n\n    if ($query->num_rows) {\n        $this->session->start('api', $query->row['session_id']);\n\n        \/\/ keep the session alive\n        $this->db->query(\"UPDATE <code>&quot; . DB_PREFIX . &quot;api_session<\/code> SET date_modified = NOW() WHERE api_session_id = '\" . (int)$query->row['api_session_id'] . \"'\");\n    }\n} else {\n    $this->session->start();\n}<\/code><\/pre>\n<p>Existing issues, for reference:<\/p>\n<ul>\n<li><a href=\"https:\/\/stackoverflow.com\/questions\/42198431\/opencart-api-issue-with-session-app-id-how-to-get-api-id\">https:\/\/stackoverflow.com\/questions\/42198431\/opencart-api-issue-with-session-app-id-how-to-get-api-id<\/a><\/li>\n<li><a href=\"https:\/\/stackoverflow.com\/questions\/54423754\/can-any-one-explain-opencart-session-in-api-module\">https:\/\/stackoverflow.com\/questions\/54423754\/can-any-one-explain-opencart-session-in-api-module<\/a><\/li>\n<li><a href=\"https:\/\/stackoverflow.com\/questions\/34612212\/opencart-2-api-session-issues\">https:\/\/stackoverflow.com\/questions\/34612212\/opencart-2-api-session-issues<\/a><\/li>\n<li><a href=\"https:\/\/forum.opencart.com\/viewtopic.php?t=201959\">https:\/\/forum.opencart.com\/viewtopic.php?t=201959<\/a> (the genius advice to install a 3rd party marketplace extension to fix this core bug is a bonus)<\/li>\n<li><a href=\"https:\/\/github.com\/opencart\/opencart\/issues\/7022\">https:\/\/github.com\/opencart\/opencart\/issues\/7022<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/opencart\/opencart\/issues\/6202\">https:\/\/github.com\/opencart\/opencart\/issues\/6202<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/opencart\/opencart\/issues\/4692\">https:\/\/github.com\/opencart\/opencart\/issues\/4692<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/opencart\/opencart\/issues\/4578\">https:\/\/github.com\/opencart\/opencart\/issues\/4578<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/opencart\/opencart\/issues\/3557\">https:\/\/github.com\/opencart\/opencart\/issues\/3557<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>*Warning: You do not have permission to access the API*<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[706],"tags":[745,877],"class_list":["post-1074","post","type-post","status-publish","format-standard","hentry","category-development","tag-api","tag-opencart","entry"],"_links":{"self":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts\/1074","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/comments?post=1074"}],"version-history":[{"count":11,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts\/1074\/revisions"}],"predecessor-version":[{"id":1091,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/posts\/1074\/revisions\/1091"}],"wp:attachment":[{"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/media?parent=1074"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/categories?post=1074"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/editjournal.redakt.eu\/faxmodem\/wp-json\/wp\/v2\/tags?post=1074"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}