{"id":2349,"date":"2021-10-27T15:09:33","date_gmt":"2021-10-27T15:09:33","guid":{"rendered":"\/blog\/?p=2349"},"modified":"2021-10-29T13:52:04","modified_gmt":"2021-10-29T13:52:04","slug":"keycloak-cors-api-tutorial","status":"publish","type":"post","link":"\/blog\/2021\/10\/keycloak-cors-api-tutorial\/","title":{"rendered":"How to Use Keycloak with a CORS-enabled API-gateway"},"content":{"rendered":"\n<p>This article describes how you can use Keycloak behind an API-gateway that considers CORS requests, which by default does not work. It presents a tutorial for a work-around that can be used until a proper fix for the root-cause of the problem is provided by Keycloak.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">The Problem<\/h2>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"alignright size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2021\/10\/Julian-Stuecker-Thijs-Reus-trimplement-portrait-455x1024.png\" alt=\"\" class=\"wp-image-2376\" width=\"171\" height=\"386\" srcset=\"\/blog\/wp-content\/uploads\/2021\/10\/Julian-Stuecker-Thijs-Reus-trimplement-portrait-455x1024.png 455w, \/blog\/wp-content\/uploads\/2021\/10\/Julian-Stuecker-Thijs-Reus-trimplement-portrait-133x300.png 133w, \/blog\/wp-content\/uploads\/2021\/10\/Julian-Stuecker-Thijs-Reus-trimplement-portrait.png 534w\" sizes=\"auto, (max-width: 171px) 100vw, 171px\" \/><figcaption><meta charset=\"utf-8\">Julian St\u00fccker, Solution Architect, and Thijs Reus, Co-Founder of trimplement, solve a Keycloak problem.<\/figcaption><\/figure><\/div>\n\n\n\n<p>The default setup will cause an HTTP 403 Forbidden response from the API-gateway during the authenticate-step on the Keycloak login page because the browser sends the HTTP request-header &#8216;<em>origin: null<\/em>&#8216;, which is identified by the API-gateway as a CORS-request, and denied because &#8216;<em>null<\/em>&#8216; is not an allowed origin.&nbsp;<\/p>\n\n\n\n<p>The root-cause for this behavior is that Keycloak always sends the HTTP response-header &#8216;<em>Referrer-Policy: no-referrer<\/em>&#8216;. This instructs the browser to omit the Referer HTTP request-header, and send the HTTP request-header &#8216;<em>origin: null<\/em>&#8216; for subsequent requests to Keycloak.<\/p>\n\n\n\n<p>Our solution is to rewrite the HTTP response-header from Keycloak in the API-gateway, so that instead of &#8216;<em>Referrer-Policy: no-referrer<\/em>&#8216; the browser will receive &#8216;<em>Referrer-Policy: same-origin<\/em>&#8216;.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Background<\/h2>\n\n\n\n<p>An API-gateway, such as Spring Cloud Gateway, is typically used in a microservice architecture to expose multiple services at a single endpoint.<\/p>\n\n\n\n<p>It is not uncommon to expose an IAM-solution, such as Keycloak, via an API-gateway.<\/p>\n\n\n\n<p>Services may require CORS support for some endpoints, which is typically managed at the API-gateway level.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Keycloak&nbsp;<\/strong><\/h3>\n\n\n\n<p>According to <a href=\"https:\/\/www.keycloak.org\/about\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/www.keycloak.org\/about<\/a> \u201c<em>Keycloak is an open-source Identity and Access Management solution aimed at modern applications and services. It makes it easy to secure applications and services with little to no code.\u201d<\/em><\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Spring Cloud Gateway&nbsp;<\/strong><\/h3>\n\n\n\n<p>According to <a href=\"https:\/\/spring.io\/projects\/spring-cloud-gateway\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/spring.io\/projects\/spring-cloud-gateway<\/a> the Spring Cloud Gateway \u201c<em> provides a library for building an API Gateway on top of Spring WebFlux. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross-cutting concerns to them such as security, monitoring\/metrics, and resiliency.\u201d<\/em><\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>CORS&nbsp;<\/strong><\/h3>\n\n\n\n<p>According to <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/CORS\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/CORS<\/a> \u201c<em>Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources.\u201d<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How to Reproduce the Problem<\/h2>\n\n\n\n<p>In order to reproduce the problem, you need the following setup:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Keycloak version 12 or higher (version 12 introduced the Referrer-Policy response header, although there is a bug report that states it also occurs with version 11.0.2)<\/li><li>API-gateway such as Spring Cloud Gateway with CORS enabled<\/li><\/ul>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2021\/10\/Graphic-Flow-Browser-API-Keycloak-1-1024x278.jpg\" alt=\"\" class=\"wp-image-2380\" width=\"512\" height=\"139\" srcset=\"\/blog\/wp-content\/uploads\/2021\/10\/Graphic-Flow-Browser-API-Keycloak-1-1024x278.jpg 1024w, \/blog\/wp-content\/uploads\/2021\/10\/Graphic-Flow-Browser-API-Keycloak-1-300x81.jpg 300w, \/blog\/wp-content\/uploads\/2021\/10\/Graphic-Flow-Browser-API-Keycloak-1-768x208.jpg 768w, \/blog\/wp-content\/uploads\/2021\/10\/Graphic-Flow-Browser-API-Keycloak-1-1536x417.jpg 1536w, \/blog\/wp-content\/uploads\/2021\/10\/Graphic-Flow-Browser-API-Keycloak-1-2048x556.jpg 2048w\" sizes=\"auto, (max-width: 512px) 100vw, 512px\" \/><\/figure><\/div>\n\n\n\n<p><strong>The following steps will then reproduce the problem:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Navigate to the Keycloak login page with a browser (we used both Firefox and Chrome), e.g. <a href=\"http:\/\/localhost:8080\/auth\/admin\/\" target=\"_blank\" rel=\"noreferrer noopener\">http:\/\/localhost:8080\/auth\/admin\/<\/a> for a local setup.<br><\/li><li>Observe the HTTP response-header &#8216;<em>Referrer-Policy: no-referrer<\/em>&#8216; in the browser developer-tools network tab.<\/li><\/ol>\n\n\n\n<div style=\"height:40px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-1-886x1024.jpg\" alt=\"\" class=\"wp-image-2355\" width=\"665\" height=\"768\" srcset=\"\/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-1-886x1024.jpg 886w, \/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-1-260x300.jpg 260w, \/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-1-768x887.jpg 768w, \/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-1-1330x1536.jpg 1330w, \/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-1.jpg 1385w\" sizes=\"auto, (max-width: 665px) 100vw, 665px\" \/><\/figure><\/div>\n\n\n\n<div style=\"height:22px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<ol class=\"wp-block-list\" start=\"3\"><li>Provide valid login credentials, and click the <em>Sign In<\/em> button.<br><\/li><li>Observe the HTTP response <em>403 Forbidden<\/em> and the empty page in the browser.<br><\/li><li>Observe the HTTP request header &#8216;origin: null&#8217;.<\/li><\/ol>\n\n\n\n<div style=\"height:26px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"494\" src=\"\/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-2-1-1024x494.jpg\" alt=\"\" class=\"wp-image-2356\" srcset=\"\/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-2-1-1024x494.jpg 1024w, \/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-2-1-300x145.jpg 300w, \/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-2-1-768x370.jpg 768w, \/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-2-1.jpg 1375w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<div style=\"height:23px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p><strong>If you cannot reproduce the problem, be sure that:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>The browser communicates with the API-gateway, and not with Keycloak directly.<\/li><li>CORS is enabled in the API-gateway using non-wildcard &#8216;<em>Access-Control-Allow-Origin<\/em>&#8216;, e.g. using configuration properties such as<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>spring.cloud.gateway.globalcors.cors-configurations.&#91;\/**].allowedOrigins=\"https:\/\/some.domain.org\"\nspring.cloud.gateway.globalcors.cors-configurations.&#91;\/**].allowedMethods=GET<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">The Solution<\/h2>\n\n\n\n<p>The problem can be fixed (or rather, worked-around) by rewriting the HTTP response-header from Keycloak in the API-gateway, e.g. so that instead of &#8216;<em>Referrer-Policy: no-referrer<\/em>&#8216; the browser receives &#8216;<em>Referrer-Policy: same-origin<\/em>&#8216;.<br><br>Spring Cloud Gateway provides a convenient <em>RewriteResponseHeaderGatewayFilterFactory<\/em> for this, which we set up as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Bean\npublic RouteLocator theRoutes(RouteLocatorBuilder builder) {\n    return builder.routes()\n   \t .route(\"auth\", r -&gt;\n   \t\t r.path(\"\/auth\/**\")\n   \t\t .filters(f -&gt; f.rewriteResponseHeader(\"Referrer-Policy\", \"no-referrer\", \"same-origin\"))\n   \t\t .uri(\"https:\/\/keycloak\"))\n   \t .build();\n}<\/code><\/pre>\n\n\n\n<p><strong>Then you can repeat the steps to observe that the fix works<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Navigate to the Keycloak login page with a browser, e.g. <a href=\"http:\/\/localhost:8080\/auth\/admin\/\" target=\"_blank\" rel=\"noreferrer noopener\">http:\/\/localhost:8080\/auth\/admin\/<\/a> for a local setup<br><\/li><li>Observe the HTTP response-header &#8216;<em>Referrer-Policy: same-origin<\/em>&#8216; in the browser developer-tools network tab<\/li><\/ol>\n\n\n\n<div style=\"height:21px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-4-890x1024.jpg\" alt=\"\" class=\"wp-image-2357\" width=\"668\" height=\"768\" srcset=\"\/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-4-890x1024.jpg 890w, \/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-4-261x300.jpg 261w, \/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-4-768x883.jpg 768w, \/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-4-1335x1536.jpg 1335w, \/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-4.jpg 1391w\" sizes=\"auto, (max-width: 668px) 100vw, 668px\" \/><\/figure><\/div>\n\n\n\n<div style=\"height:23px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<ol class=\"wp-block-list\" start=\"3\"><li>Provide valid login credentials, and click the <em>Sign In<\/em> button.<br><\/li><li>Observe the HTTP response <em>302 Found<\/em> and the target page in the browser.<br><\/li><li>Observe the HTTP request header &#8216;<em>origin: http:\/\/localhost:8080<\/em>&#8216;.<\/li><\/ol>\n\n\n\n<div style=\"height:28px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"\/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-5-886x1024.jpg\" alt=\"\" class=\"wp-image-2358\" width=\"665\" height=\"768\" srcset=\"\/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-5-886x1024.jpg 886w, \/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-5-260x300.jpg 260w, \/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-5-768x887.jpg 768w, \/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-5-1330x1536.jpg 1330w, \/blog\/wp-content\/uploads\/2021\/10\/Keycloak-CORC-API-5.jpg 1385w\" sizes=\"auto, (max-width: 665px) 100vw, 665px\" \/><\/figure><\/div>\n\n\n\n<div style=\"height:22px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Questions and Considerations<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Why Is the API-gateway Rejecting the HTTP Request?<\/h3>\n\n\n\n<p>The browser sends the HTTP request-header &#8216;<em>origin: null<\/em>&#8216; when the &#8216;<em>Referrer-Policy<\/em>&#8216; is &#8216;<em>no-referrer<\/em>&#8216;.&nbsp;<\/p>\n\n\n\n<p>Whenever the &#8216;<em>origin<\/em>&#8216; header is present in the HTTP request, the API-gateway considers it a CORS request. A CORS request causes the API-gateway to validate if the origin is in the list of allowed origins. For &#8216;<em>null<\/em>&#8216; this is typically not the case (as it&#8217;s not recommended), leading it to reject the request with HTTP <em>403 Forbidden<\/em>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why Don&#8217;t You Just Allow null as CORS Origin?<\/h3>\n\n\n\n<p>Allowing &#8216;<em>null<\/em>&#8216; as &#8216;<em>Access-Control-Allow-Origin<\/em>&#8216; is <strong>not<\/strong> recommended, as it introduces multiple security problems. See <a href=\"https:\/\/w3c.github.io\/webappsec-cors-for-developers\/#avoid-returning-access-control-allow-origin-null\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/w3c.github.io\/webappsec-cors-for-developers\/#avoid-returning-access-control-allow-origin-null<\/a> for more details.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why Don&#8217;t You Just Allow All Origins \/ Methods for CORS?<\/h3>\n\n\n\n<p>While allowing all origins\/methods for CORS would prevent the problem, it would also introduce significant security issues. CORS settings should always be as restrictive as possible, especially when sensitive data (such as credentials) is involved. See <a href=\"https:\/\/stackoverflow.com\/a\/56457665\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/stackoverflow.com\/a\/56457665<\/a> for details.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Shouldn&#8217;t Keycloak Fix This Issue?<\/h3>\n\n\n\n<p>We would recommend Keycloak to make the &#8216;<em>Referrer-Policy<\/em>&#8216; value configurable, just as they allow for certain other headers.<\/p>\n\n\n\n<p>The following Keycloak bug-report was created (not by us) in October 2020 for this issue, suggesting exactly this: <a href=\"https:\/\/issues.redhat.com\/browse\/KEYCLOAK-16032\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/issues.redhat.com\/browse\/KEYCLOAK-16032<\/a><\/p>\n\n\n\n<p>Somehow the Keycloak devs got confused in the bug thread and closed it as &#8216;explained&#8217; by stating that &#8216;<em>null<\/em>&#8216; is a valid value for &#8216;<em>origin<\/em>&#8216; (which is not the issue), rather than actually fixing the problem (e.g. by making the &#8216;<em>Referrer-Policy<\/em>&#8216; customizable, similar to other HTTP response header values).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Doesn&#8217;t This Work-Around Reduce Overall Security?<\/h3>\n\n\n\n<p>It is true that &#8216;<em>no-referrer<\/em>&#8216; is a stricter &#8216;<em>Referrer-Policy<\/em>&#8216; compared to e.g. &#8216;<em>same-origin<\/em>&#8216;, as it causes the browser to send fewer details to the API-gateway in the &#8216;<em>Referer<\/em>&#8216; and &#8216;<em>origin<\/em>&#8216; headers.<\/p>\n\n\n\n<p>However, in combination with a CORS-enabled API-gateway, using &#8216;<em>no-referrer<\/em>&#8216; totally breaks Keycloak from a system-level perspective, rendering it completely useless. So using this setting provides the same level of security and functionality as shutting down Keycloak.<\/p>\n\n\n\n<p>In order to make this work with a CORS-enabled API-gateway, the browser must send some more details, specifically a proper value for the &#8216;<em>origin<\/em>&#8216; header for requests with the same origin. This can be achieved by using a different &#8216;<em>Referrer-Policy<\/em>&#8216;, such as &#8216;<em>same-origin<\/em>&#8216;.&nbsp;<\/p>\n\n\n\n<p>For us this is an acceptable (very limited) reduction in security to at least have the expected functionality; in addition, these details only affect the same origin, which is fully under our control.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Can I Use Other Referrer-Policy Values?<\/h3>\n\n\n\n<p>You can choose a referrer-policy that works for your setup, there is no requirement to use &#8216;<em>same-origin<\/em>&#8216;. For example, the typical browser-default &#8216;<em>strict-origin-when-cross-origin<\/em>&#8216; works fine as well.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Further Details<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Headers\/Referrer-Policy\" target=\"_blank\" rel=\"noreferrer noopener\">Referrer-Policy response header<\/a><\/li><li><a href=\"https:\/\/github.com\/keycloak\/keycloak\/blob\/f7e0af438d5a2d24b76bd3f34f71693c109738d5\/server-spi-private\/src\/main\/java\/org\/keycloak\/models\/BrowserSecurityHeaders.java#L33\" target=\"_blank\" rel=\"noreferrer noopener\">Keycloak always sending &#8216;<em>Referrer-Policy: no-referrer<\/em>&#8216;<\/a> Part 1 + <a href=\"https:\/\/github.com\/keycloak\/keycloak\/commit\/f7e0af438d5a2d24b76bd3f34f71693c109738d5\" target=\"_blank\" rel=\"noreferrer noopener\">Part 2<\/a>  <\/li><li><a href=\"https:\/\/issues.redhat.com\/browse\/KEYCLOAK-16032\" target=\"_blank\" rel=\"noreferrer noopener\">Bug report for Keycloak that was not fixed but only explained <\/a><\/li><li><a href=\"https:\/\/w3c.github.io\/webappsec-cors-for-developers\/#avoid-returning-access-control-allow-origin-null\" target=\"_blank\" rel=\"noreferrer noopener\">Why origin &#8216;<em>null<\/em>&#8216; should not be an allowed origin for CORS<\/a><\/li><li><a href=\"https:\/\/cloud.spring.io\/spring-cloud-gateway\/reference\/html\/#the-rewriteresponseheader-gatewayfilter-factory\" target=\"_blank\" rel=\"noreferrer noopener\">Spring Cloud Gateway <em>RewriteResponseHeaderGatewayFilterFactory <\/em><\/a><\/li><li><a href=\"https:\/\/github.com\/spring-projects\/spring-framework\/blob\/bd55f609a7d083e2ab9878240b278f7eaa6075f6\/spring-web\/src\/main\/java\/org\/springframework\/web\/cors\/CorsUtils.java#L42..L45\" target=\"_blank\" rel=\"noreferrer noopener\">Spring CorsUtils to determine if an HTTP request is CORS or not <\/a><\/li><li><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/CORS\" target=\"_blank\" rel=\"noreferrer noopener\">General information on CORS <\/a><\/li><li><a href=\"https:\/\/bugs.chromium.org\/p\/chromium\/issues\/detail?id=635400#c28\" target=\"_blank\" rel=\"noreferrer noopener\">Chromium devs confirming the no-referrer policy behavior <\/a><\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>This article describes how you can use Keycloak behind an API-gateway that considers CORS requests, which by default does not work. It presents a tutorial for a work-around that can be used until a proper fix for the root-cause of the problem is provided by Keycloak.<\/p>\n","protected":false},"author":5,"featured_media":2359,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[162],"tags":[9,171,260,259],"class_list":["post-2349","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software-development-tutorials","tag-api","tag-coding","tag-cors","tag-keycloak"],"_links":{"self":[{"href":"\/blog\/wp-json\/wp\/v2\/posts\/2349","targetHints":{"allow":["GET"]}}],"collection":[{"href":"\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"\/blog\/wp-json\/wp\/v2\/comments?post=2349"}],"version-history":[{"count":15,"href":"\/blog\/wp-json\/wp\/v2\/posts\/2349\/revisions"}],"predecessor-version":[{"id":2405,"href":"\/blog\/wp-json\/wp\/v2\/posts\/2349\/revisions\/2405"}],"wp:featuredmedia":[{"embeddable":true,"href":"\/blog\/wp-json\/wp\/v2\/media\/2359"}],"wp:attachment":[{"href":"\/blog\/wp-json\/wp\/v2\/media?parent=2349"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"\/blog\/wp-json\/wp\/v2\/categories?post=2349"},{"taxonomy":"post_tag","embeddable":true,"href":"\/blog\/wp-json\/wp\/v2\/tags?post=2349"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}