{"id":89290,"date":"2020-12-15T20:10:57","date_gmt":"2020-12-15T20:10:57","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=89290"},"modified":"2022-05-02T20:38:25","modified_gmt":"2022-05-02T20:38:25","slug":"cross-origin-resource-sharing-for-cross-site-cookie-based-authentication","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/javascript\/cross-origin-resource-sharing-for-cross-site-cookie-based-authentication\/","title":{"rendered":"Cross-origin resource sharing for cross-site cookie-based authentication"},"content":{"rendered":"<p>In today\u2019s IT world, growth and competition lead industry to adopt faster releases of their software products and services to market. To avoid the cost of building services, enterprises subscribe to other readily available services. Businesses emphasize safeguarding their business data while consuming services from providers, and such service providers facilitate robust and secure communication channels to their consumers. Often enterprise business operations are fed by software applications and are secured with intranet applications contexts, where hostnames are known with domain names. There is no need for Cross-Site communication. With this new shift, services are securely exposed to consumers, but features are also consumed from different organizations over HTTP.<\/p>\n<p>As organizations are widening their software applications to consume readily available services over HTTP, the security approaches are different than what is used for intranet applications for enterprise application development. Today, you\u2019ll hear terms like Cross-Site or CORS (Cross-Origin resource sharing). CORS means that two independent applications communicate as publisher and subscriber, to secure the resources or content or data during communication (over HTTP request or response). There are different approaches, configurations, and best practices. Here authentication and authorization play a vital role to safeguard the participating applications. This article focuses on how authentication and authorization need special consideration when applications have multiple origins. It also looks at how a cookie-based authentication implementation works for a cross-origin site or under CORS.<\/p>\n<h2>Use Case: Authentication and Authorization<\/h2>\n<p>Organization \u201cCompany-E\u201d has a homegrown Enterprise Health Record (EHR) <em>E1<\/em>, and organization \u201cCompany-P\u201d has the Patient Portal <em>P1<\/em> application to allow patients to view their own health records and communicate with their providers. Company-E and Company-P come to an agreement that allows P1 to subscribe to the service of E1 to facilitate the health data to the patient or patient\u2019s responsible party. The hosting of the applications will remain the same, i.e., E1 is hosted and maintained by Company-E and P1 is hosted and maintained by Company-P. To enable this engagement, both E1 and P1 application need some enhancements to enable cross-origin communication over HTTP. Below is the simple requirement from each application<\/p>\n<p><strong>E1<\/strong>: expose secured service and allow only P1 to consume. Data send\/received can happen on this service with users who are authenticated and have been privileged to do such activities.<\/p>\n<p><strong>P1<\/strong>: ability to provide user credentials to E1 services to send and receive data<\/p>\n<h2>Solution<\/h2>\n<p>The current E1 and P1 applications have been described, and the new requirements for enabling secure communication between them are documented. The next step is to diagram what needs to be accomplished. Figure 1 is a diagram depicting the logical view of P1-WebUI and E1-RESTful API service as per the requirements.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-89338\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/12\/image-2.png\" alt=\"\" width=\"602\" height=\"238\" \/><\/p>\n<p><strong>Figure 1: Logical view of P1 and E1 requirements<\/strong><\/p>\n<p>Figure 1 can be described as follows:<\/p>\n<ul>\n<li>P1 is browser rendering web user interface created using React library, hosted by Company-P with URI <a href=\"https:\/\/healthui\">https:\/\/healthui<\/a><\/li>\n<li>E1 has RESTful API created with .NET core WebAPI, hosted by Company-E with URI <a href=\"http:\/\/healthapi\">http:\/\/healthapi<\/a><\/li>\n<li>For authentication,\n<ul>\n<li>P1 sends an HTTP request with the body encapsulating valid user credentials<\/li>\n<li>E1 validates credentials and, once successful, sends an HTTP (HTTP-only) cookie as a response to P1<\/li>\n<li>P1\u2019s HTTP client, i.e. browser, includes Auth.Cookie to each subsequent HTTP request<\/li>\n<\/ul>\n<\/li>\n<li>For authorization,\n<ul>\n<li>E1 validates each HTTP request other than sign-in, if the user does not have privileges, then response with status code 403 (Forbidden)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2>Implementation<\/h2>\n<p>Begin implementing the solution by following the captured approach notes. Note that the entire solution is not covered; only key points for implementing a solution are included. You can find the entire solution <a href=\"https:\/\/github.com\/hi10p\/CookieCORS.git\">here<\/a>.<\/p>\n<h3>RESTful API<\/h3>\n<p>The E1 API is RESTful created with .NET core 3.1 Web-API. First, you need to configure the necessary settings to enable (cross-site) CORS HTTP request processing. The <a href=\"http:\/\/healthui\">http:\/\/healthui<\/a> app should be able to read or write data based on its functional business operations. To begin, open the <em>startup.cs<\/em> file of a Web-API project. Under the <code>ConfigureService<\/code> method, add a CORS service to reference <code>IServiceCollection<\/code>. Follow Figure 2, which enables CORS middleware to handles cross-origin requests.<\/p>\n<p>Our cookie-based-authentication and authorization settings are fulfilled by the three core namespaces spaces of AspNetCore and they are listed as below in Figure 2.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"590\" height=\"73\" class=\"wp-image-89291\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/12\/word-image-23.png\" \/><\/p>\n<p><strong>Figure 2: Using statements<\/strong><\/p>\n<p>Line#1 from the above snippet facilitates all settings for enabling authentication and Line#2 facilitates enabling http cookies for authentication. Line#3 facilitates to establish authorization for each incoming request and as per needs of application authorization rules can be built.<\/p>\n<p>NOTE that the entire application is not covered in this article. It covers only the key aspects of CORS cookie-based authentication. Figure 3 contains code to enable the CORS middleware.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"922\" height=\"409\" class=\"wp-image-89292\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/12\/word-image-24.png\" \/><\/p>\n<p><strong>Figure 3: Enable CORS middleware<\/strong><\/p>\n<ul>\n<li>Line#29, AddCors extension method is setup method, Action&lt;CorsOption&gt; is parameter to configure CORS with provided option\n<ul>\n<li>Line#31, a call to method options.AddPolicy, XAuthOrigins read-only string variable passed as the first parameter to specify the name of the policy and the second parameter is CorsPolicyBuilder<\/li>\n<li>Line#34, adds hostname to the CorsPolicy.Orgins collection and specifies P1\u2019s hosted application URI, <a href=\"http:\/\/healthui\">http:\/\/healthui<\/a>. <em>This URI can be on port 80 HTTP or 443 HTTPS<\/em><\/li>\n<li>Moving ahead with the same policy builder at Line#35, AllowAnyMethod method allows any HTTP request method, i.e. GET, HEAD, or POST, etc.<\/li>\n<li>Line#36, AllowAnyHeader allows request headers such as \u201ccontent-type\u201d or \u201ccontent-language\u201d.<\/li>\n<li><a id=\"post-89290-gjdgxs\"><\/a> Line#37, AllowCredentials method is to allow cross-origin credentials. The HTTP response includes a header <em>\u201c<\/em>Access-Control-Allow-Credentials<em>\u201d<\/em> which tells the browser that the server allows credentials for cross-origin-requests. This is how it works:\n<ul>\n<li>If the E1 API sent credentials to the calling application P1\u2019s browser and the response header doesn\u2019t include <em>\u201c<\/em>Access-Control-Allow-Credentials<em>\u201d<\/em>, then the browser doesn\u2019t expose response to the P1 WebUI, and the request fails<\/li>\n<li>The CORS specification states that all origin \u2018*\u2019 is invalid if \u201cAccess-Control-Allow-Credentials\u201d header is present. In this case, at Line#34, there is only the known hostname \u201chttp:\/\/heathui.\u201d<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Once the CORS middleware policy with name is built with the required configuration, the same policy name shall be used to add the CORS middleware to P1\u2019s RESTful API to allow cross-domain HTTP requests as shown in Figure 4.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"618\" height=\"261\" class=\"wp-image-89293\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/12\/word-image-25.png\" \/><\/p>\n<p><strong>Figure 4: Add the CORS middleware to allow cross-domain HTTP requests<\/strong><\/p>\n<p>As highlighted at Line#68, it\u2019s necessary to add CORS middleware for P1 API\u2019s application builder at the first line in the configuration method.<\/p>\n<p>At this stage, CORS middleware is added with the desired configuration. The next step is to enable authentication and authorization middleware to establish secured cross-site HTTP communication. Inside the same <code>Configuration<\/code> method. Line#72 is adding authentication middleware, and Line#75 is adding authorization middleware to P1\u2019s RESTful API application builder.<\/p>\n<p>Next, go back to <code>ConfigurationServices<\/code> method, add authentication and authorization services with required configuration policy options. Figure 5 demonstrates adding services to the service collection.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"852\" height=\"637\" class=\"wp-image-89294\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/12\/word-image-26.png\" \/><\/p>\n<p><strong>Figure 5: Adding services<\/strong><\/p>\n<p>The important points to note are as follows:<\/p>\n<ul>\n<li>Line#41, call to the method AddAuthorization adds the authorization policy to the service collection parameter services. Its authorization parameter sets the default policy with new the AuthorizationPolicyBuilder instance having \u201cCookies\u201d authentication scheme. Specifies user must be authenticated with method \u201cRequireAuthenticatedUser\u201d to access any resource<\/li>\n<li>Line#46, call to the method AddAuthentication adds authentication options to service collection parameter <em>services<\/em>, \u201cDefaultAuthenticateScheme\u201d and \u201cDefaultChallengeScheme\u201d are set to \u201cCookies\u201d, which is a constant value of \u201cCookieAuthenticationDefaults.AuthenticationScheme\u201d which enables cookie-based authentication. Each request must carry the valid cookie to access the API resource<\/li>\n<li>Line#51, call to the method AddCookie adds the cookie authentication options to the authentication configuration\n<ul>\n<li>Line#53, the same site property Cookie.SameSite is set to SameSiteMode.None, i.e. to allow cross-site cookie use<\/li>\n<li>Line#54, cookie is always set to secure and, all calls to API needs to be done with HTTPS<\/li>\n<li>Line#55, Cookie.HttpOnly set to true. The cookie will only be passed to HTTP requests and is not made available to script on the page<\/li>\n<li>Line#56, UserAuthenticationEvent type is used to get events instance for authenticating incoming cookie from an HTTP request<\/li>\n<li>Rest of the lines from, #57 to #60, sets login, logout, access denied paths and sliding expiration to true which will issue new expiration time to the valid and authenticated HTTP request<\/li>\n<li>Line#62, adds the UserAuthenticationEvent type to the service collection instance<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>The code for setting and enabling the CORS plus cookie-based secured authorization and authentication validator for validating incoming requests is complete. Take a look in brief what \u201cUserAuthenticationEvent\u201d has to intercept each incoming request, and how it verify cookie and act accordingly to with HTTP response as shown in Figure 6.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1012\" height=\"456\" class=\"wp-image-89295\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/12\/word-image-27.png\" \/><\/p>\n<p><strong>Figure 6: Verify cooking and response<\/strong><\/p>\n<p>UserAuthenticationEvent overrides the ValidatePrincipal method of the base class CookieAuthenticationEvents, fetch user\u2019s email from principal claims collection and check the validity of it. If validation fails then:<\/p>\n<ul>\n<li>Reject the incoming principal from cookie<\/li>\n<li>Set the HTTP status code to 401 <em>Unauthorized<\/em><\/li>\n<li>Sign out current HTTP context with \u201cCookie\u201d authentication scheme<\/li>\n<\/ul>\n<h3>Secure API Resource<\/h3>\n<p>Only the \u201csign-in\u201d route should allow anonymous access. There is no need to have authentication and authorization checks; hence the <code>AllowAnonymous<\/code> attribute is added to the <code>Authenticate<\/code> method, i.e. Line#20 in <code>UserAccountController<\/code> shown in Figure 7.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"718\" height=\"381\" class=\"wp-image-89296\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/12\/word-image-28.png\" \/><\/p>\n<p><strong>Figure 7: Allow anonymous sign in<\/strong><\/p>\n<p>All other controllers except <code>UserAccountController<\/code> should have <code>AuthoriseUser<\/code> attribute, to validate principal from incoming HTTP request context show in Figure 8.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"626\" height=\"274\" class=\"wp-image-89297\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/12\/word-image-29.png\" \/><\/p>\n<p><strong>Figure 8: The AutorizeUser attribute<\/strong><\/p>\n<p>The code shown below in Figure 9 is the implementation of an authorization check. Here you can add your own rules to restrict the user if they do not have rights to access resources for which the HTTP request has been made.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"892\" height=\"478\" class=\"wp-image-89298\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/12\/word-image-30.png\" \/><\/p>\n<p><strong>Figure 9: The AuthorizeUserAttribute class<\/strong><\/p>\n<p>The code:<\/p>\n<ul>\n<li>Line#13, checks the user who made HTTP request. Its HTTPContext has user identity authenticated or not. If the request is not authenticated then, assign <code>UnauthorizedResult<\/code> result to <code>AuthorizationFilterContext<\/code> instance, which will add HTTP status code 401 (Unauthorized) to HTTP response.<\/li>\n<li>If the user is authenticated but not found in the system, then assign <code>ForbidResult<\/code> result to <code>AuthorizationFilterContext<\/code> instance, which will add HTTP status code 403 (Forbidden) to HTTP response.<\/li>\n<\/ul>\n<h3>Web UI<\/h3>\n<p>P1\u2019s Web UI is built with React web UI library, a browser rendering single page application, to have a component-based UI. The Axios react npm library (promise-based HTTP client) is used for making HTTP requests to E1\u2019s RESTful API. Enable consumer P1 to make calls to cross-site calls under the secured policy of E1.<\/p>\n<p><a href=\"#post-89290-gjdgxs\">Remember<\/a> E1\u2019s API has been enabled to allow credentials so the browser can consider credentials in subsequent HTTP requests. Yet to inform browsers that do include credentials, ajax HTTP request call needs to include attribute <code>withCredential<\/code>: true. In Figure 10 below, Line#15 creates and enables the HTTP-Client <code>apiClient<\/code> instance of <code>axios<\/code> to include credentials.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"898\" height=\"412\" class=\"wp-image-89299\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/12\/word-image-31.png\" \/><\/p>\n<p><strong>Figure 10: Enable HTTP client<\/strong><\/p>\n<p>P1\u2019s web UI uses <code>this.apiClient<\/code> for making an HTTP request to <a href=\"https:\/\/healthapi\">https:\/\/<\/a><a href=\"https:\/\/healthapi\"><em>healthapi<\/em><\/a>. Due to the CORS setting at E1 API, the client-side script will not be able to access the authentication cookie. Still, the browser does recognize and manage inclusion of the authentication cookie in subsequent HTTP requests. That\u2019s the only configuration you need to take care of at client-side. Now to look at HTTP request and HTTP response attributes to know what is happening under cross-site communication.<\/p>\n<h2>All working together<\/h2>\n<p>With the help of the developer toolbar of the Chrome browser, you will test, verify and see inside of outgoing requests and incoming responses over the network. The Figure below is the snapshot of a network profile captured during the sign-in attempt. The URI is <a href=\"http:\/\/healthui\/account\/useraccount\/signin\"><em>http:\/\/healthui\/account\/useraccount\/signin<\/em><\/a><em>. Many headers<\/em> can be found under request headers<em>. <\/em>The request origin is <a href=\"http:\/\/healthui\">http:\/\/healthui<\/a>; authority header is host, i.e. healthapi; the path is <em>\/account\/useraccount\/signin; the <\/em>method is POST on HTTPS scheme; header sec-fetch-site value is cross-site. This indicates the request has been made to cross-site. Figure 11 shows the Request Headers.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"603\" height=\"535\" class=\"wp-image-89300\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/12\/word-image-32.png\" \/><\/p>\n<p><strong>Figure 11: Request Headers<\/strong><\/p>\n<p>Review the response headers one by one from this HTTP request:<\/p>\n<ul>\n<li>access-control-allow-credentials: true, this covers this header while setting CORS service configuration, but here it is, now the browser will only expose the response to frontend code, in this case, JavaScript<\/li>\n<li>access-control-allow-origin: <a href=\"http:\/\/healthui\">http:\/\/healthui<\/a>, this indicates whether the response can be shared with the requesting application from the given origin(host)<\/li>\n<li>set-cookie: xxxxxxx\u2026. authentication cookie sent by <a href=\"https:\/\/healthapi\">https:\/\/healthapi<\/a> with the response object<\/li>\n<\/ul>\n<p>Figure 12 shows the Response Headers<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"532\" height=\"547\" class=\"wp-image-89301\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/12\/word-image-33.png\" \/><\/p>\n<p><strong>Figure 12: Response Headers<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p>After looking at the response header and sending the authentication cookie, review look at the attributes of the response cookie shown in Figure 13.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1147\" height=\"172\" class=\"wp-image-89302\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/12\/word-image-34.png\" \/><\/p>\n<p><strong>Figure 13: The Response Cookies<\/strong><\/p>\n<p>The name of the cookie is <em>.AspNetCore.Cookies<\/em>; value is long alphanumeric string; issuing domain is <em>healthapi<\/em>; <em>HttpOnly<\/em> and <em>Secured <\/em>are enabled, and <em>SmeSite<\/em> is none. These are all configuration settings found in startup.cs in E1\u2019s RESTful API.<\/p>\n<p>Once P1 WebUI is authenticated, the browser will add an authentication cookie to all subsequent HTTP requests. You can see this from the outgoing request\u2019s headers captured by network profile. A cookie named <em>.AspNetCare.Cookies<\/em> is an authentication cookie which is included by the browser, and the server will validate to process the response. See Figure 14.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"580\" height=\"660\" class=\"wp-image-89303\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/12\/word-image-35.png\" \/><\/p>\n<p><strong>Figure 14: The authentication cookie<\/strong><\/p>\n<p>If any other application tries to access E1\u2019s API, then it will fail with this server error:<\/p>\n<p>Access to XMLHttpRequest at &#8216;https:\/\/helathapi\/account\/UserAccount\/signin&#8217; from origin &#8216;http:\/\/DoubleCare&#8217; has been blocked by CORS policy: Response to preflight request doesn&#8217;t pass access control check: No &#8216;Access-Control-Allow-Origin&#8217; header is present on the requested resource.<\/p>\n<h2>Conclusion<\/h2>\n<p>E1\u2019s API is secured and available to P1\u2019s WebUI to get data. If any other app tries to access the RESTful API, then it won\u2019t gain access to it, and the server will throw CORS origin or cross-site exception to the caller.<\/p>\n<p><a id=\"post-89290-_30j0zll\"><\/a> Today, there are many consumable services available for business applications. Instead of inventing services, industry prefers to sign-up for services. Such solution works best for healthcare, e-commerce, financial, tours &amp; travels, hospitality and others.<\/p>\n<p>This example provides the solution and meets what is needed:<\/p>\n<ul>\n<li>Applications or distributed and owned by individual organization, one is a subscriber and other is a distributor<\/li>\n<li>Secured authentication to enable safe data communication over HTTP<\/li>\n<li>Cross-Site authentication\/authorization, let the browser handle authenticated cookies for HTTP requests<\/li>\n<li>Distributor validates incoming cookie before handling it to action for accessing resource(s)<\/li>\n<\/ul>\n<h2>Source Code<\/h2>\n<p>The source code for this project is available here <a href=\"https:\/\/github.com\/hi10p\/CookieCORS.git\">https:\/\/github.com\/hi10p\/CookieCORS.git<\/a><\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Organizations can take advantage of pre-built services to build their own software faster. In this article, Hitendra Patel demonstrates how an organization can provide authentication services for a web-based application.&hellip;<\/p>\n","protected":false},"author":222403,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[146044],"tags":[5134],"coauthors":[7536],"class_list":["post-89290","post","type-post","status-publish","format-standard","hentry","category-javascript","tag-sql-prompt"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/89290","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/222403"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=89290"}],"version-history":[{"count":3,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/89290\/revisions"}],"predecessor-version":[{"id":89339,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/89290\/revisions\/89339"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=89290"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=89290"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=89290"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=89290"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}