简体   繁体   English

使用 Fetch 的 WordPress REST API 身份验证

[英]WordPress REST API Authentication Using Fetch

I'm attempting to use cookie authentication for WordPress REST API access using the Fetch API , however the auth is failing with the following error.我正在尝试使用Fetch API对 WordPress REST API 访问使用cookie 身份验证,但是身份验证失败并出现以下错误。

403: Cookie Nonce is Invalid 403:Cookie Nonce 无效

I'm using the following script to connect to the API.我正在使用以下脚本连接到 API。

const headers = new Headers({
   'Content-Type': 'application/json',
   'X-WP-Nonce': WPAPI.nonce
});  

fetch(WPAPI.root + 'my-endpoint/upload/', {
    method: 'POST',
    headers: headers,
    body: JSON.stringify(data)
})

When I switch from using Fetch toXMLHttpRequest it works as expected.当我从使用 Fetch 切换到XMLHttpRequest 时,它按预期工作。

let request = new XMLHttpRequest();
request.open('POST', WPAPI.root + 'my-endpoint/upload/', true);
request.setRequestHeader('X-WP-Nonce', WPAPI.nonce);
request.setRequestHeader('Content-Type', 'application/json');
request.send(JSON.stringify(data));

Is it possible there an issue with the way headers are being sent in the Fetch method?在 Fetch 方法中发送标头的方式是否可能存在问题?

WordPress nonce authentication requires the use of cookies and by default Fetch doesn't send those along. WordPress nonce 身份验证需要使用 cookie,默认情况下 Fetch 不会发送这些。 You can use the credentials option to make this work:您可以使用凭证选项来完成这项工作:

fetch(endpoint, {
  credentials: 'same-origin'
})

https://github.com/github/fetch#sending-cookies https://github.com/github/fetch#sending-cookies

Came across my post from 4 years ago looking for the same issue :) This solves the problem.从 4 年前看到我的帖子寻找同样的问题:) 这解决了问题。

const response = await fetch(url, {
    method: 'POST',
    credentials: 'same-origin',
    headers: {
        'Content-Type': 'application/json',
        'X-WP-Nonce' : my_var.nonce
    },
    body: JSON.stringify(data),
});
const content = await response.json();
console.log(content);

Late, but maybe helpful for other readers as I added code specifically for fetch() promise according to this question.晚了,但可能对其他读者有所帮助,因为我根据这个问题专门为fetch()承诺添加了代码。

WordPress uses nonce automatically within their cookies, as I found out.正如我发现的那样,WordPress 在其 cookie 中自动使用随机数

WordPress : version 5.7.2 WordPress :版本 5.7.2
PHP : version 7.4 PHP :7.4 版
host : hostmonster.com主机:hostmonster.com
client : Windows 10客户端:Windows 10
browsers : tested on Chrome, Firefox, even Edge 😜 worked浏览器:在 Chrome、Firefox、甚至 Edge 上测试过 😜 工作

Code ( PHP code in function.php of your installed theme):代码(已安装主题的 function.php 中的PHP代码):

add_action('rest_api_init', function() {
    /**
     * Register here your custom routes for your CRUD functions
     */
    register_rest_route( 'my-endpoint/v1', '/upload/', array(
        array(
            'methods'  => WP_REST_Server::READABLE, // = 'GET'
            'callback' => 'get_data',
            // Always allow, as an example
            'permission_callback' => '__return_true'
        ),
        array(
            'methods'  => WP_REST_Server::CREATABLE, // = 'POST'
            'callback' => 'create_data',
            // Here we register our permissions callback
            // The callback is fired before the main callback to check if the current user can access the endpoint
            'permission_callback' => 'prefix_get_private_data_permissions_check',
        ),
    ));
});

// The missing part:
// Add your Permission Callback function here, that checks for the cookie
// You should define your own 'prefix_' name, though

function prefix_get_private_data_permissions_check() {
    
    // Option 1: Password Protected post or page:
    // Restrict endpoint to browsers that have the wp-postpass_ cookie.
    if ( !isset($_COOKIE['wp-postpass_'. COOKIEHASH] )) {
        return new WP_Error( 'rest_forbidden', esc_html__( 'OMG you can not create or edit private data.', 'my-text-domain' ), array( 'status' => 401 ) );
    };

    // Option 2: Authentication based on logged-in user:
    // Restrict endpoint to only users who have the edit_posts capability.
    if ( ! current_user_can( 'edit_posts' ) ) {
        return new WP_Error( 'rest_forbidden', esc_html__( 'OMG you can not create or edit private data.', 'my-text-domain' ), array( 'status' => 401 ) );
    };
 
    // This is a black-listing approach. You could alternatively do this via white-listing, by returning false here and changing the permissions check.
    return true;
};

function create_data() {
    global $wpdb;

    $result = $wpdb->query(...);

    return $result;
}

function get_data() {
    global $wpdb;

    $data = $wpdb->get_results('SELECT * from `data`');

    return $data;
}

Make sure to include in your HTML page credentials: 'same-origin' in your HTTP request, as stated correctly in previous answers and comments above.确保在您的 HTML 页面credentials: 'same-origin'在您的 HTTP 请求中,如上述先前的答案和评论中所述。

Code ( HTML with inline <script> ... </script> ):代码(带有内联<script> ... </script> HTML ):

<script>

// Here comes the REST API part:
// HTTP requests with fetch() promises

function getYourData() {
  let url = 'https://example.com/wp-json/my-endpoint/v1/upload/';
  fetch(url, {
    method: 'GET',
    credentials: 'same-origin', // <-- make sure to include credentials
    headers:{
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        //'Authorization': 'Bearer ' + token  <-- not needed, WP does not check for it
    }
  }).then(res => res.json())
  .then(response => get_success(response))
  .catch(error => failure(error));
};

function insertYourData(data) {
  let url = 'https://example.com/wp-json/my-endpoint/v1/upload/';
  fetch(url, {
    method: 'POST',
    credentials: 'same-origin', // <-- make sure to include credentials
    headers:{
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        //'Authorization': 'Bearer ' + token  <-- not needed, WP does not check for it
    },
    body: JSON.stringify(data)
  }).then(res => res.json())
  .then(response => create_success(response))
  .catch(error => failure(error));
};

// your Success and Failure-functions:

function get_success(json) {
  // do something here with your returned data ....
  console.log(json);
};

function create_success(json) {
  // do something here with your returned data ....
  console.log(json);
};

function failure(error) {
  // do something here ....
  console.log("Error: " + error);
};

</script>

Final thoughts:最后的想法:

Is 'Authorization': 'Bearer ' + token necessary in header of HTTP request? 'Authorization': 'Bearer ' + token需要在 HTTP 请求的标头中?

After some testing, I realized that if ( !isset($_COOKIE['wp-postpass_'. COOKIEHASH] )) { ... within the Permission Callback not only checks if the Cookie is set on client browser, but it seems also to check its value (the JWT token) .经过一些测试,我意识到if ( !isset($_COOKIE['wp-postpass_'. COOKIEHASH] )) { ...权限回调中不仅检查 Cookie 是否在客户端浏览器上设置,而且似乎也检查其值(JWT 令牌)

Because I dobble checked as with my initial code, passing a false token, eliminating the cookie, or leaving session open but changing in the back-end the password of site (hence WordPress would create a new token, hence value of set wp_postpass_ cookie would change) and all test went correctly - REST API blocked, not only verifying presence of cookie, but also its value (which is good - thank you WordPress team).因为我像我的初始代码一样检查,传递错误令牌,消除 cookie,或保持会话打开但在后端更改站点的密码(因此 WordPress 将创建一个新令牌,因此 set wp_postpass_ cookie 的值将更改)并且所有测试都正确进行 - REST API 被阻止,不仅验证 cookie 的存在,还验证其价值(这很好 - 谢谢 WordPress 团队)。

Sources:资料来源:
I found following resource concerning above thoughts in the FAQ section :我在常见问题部分找到了有关上述想法的以下资源:

Why is the REST API not verifying the incoming Origin header? 为什么 REST API 不验证传入的 Origin 标头? Does this expose my site to CSRF attacks? 这会使我的网站受到 CSRF 攻击吗?

Because the WordPress REST API does not verify the Origin header of incoming requests, public REST API endpoints may therefore be accessed from any site.由于 WordPress REST API 不验证传入请求的 Origin 标头,因此可以从任何站点访问公共 REST API 端点。 This is an intentional design decision.这是一个有意的设计决定。

However, WordPress has an existing CSRF protection mechanism which uses nonces.然而,WordPress 有一个现有的 CSRF 保护机制,它使用 nonce。

And according to my testing so far, the WP-way of authentication works perfectly well .根据我到目前为止的测试, WP 身份验证方式运行良好

Thumbs up 👍 for the WordPress team为 WordPress 团队点赞👍

Additional 2 sources from the WordPress REST API Handbook :来自 WordPress REST API 手册的另外 2 个来源:

REST API Handbook / Extending the REST API / Routes and Endpoints REST API 手册 / 扩展 REST API / 路由和端点
REST API Handbook / Extending the REST API / Adding Custom Endpoints REST API 手册 / 扩展 REST API / 添加自定义端点

And 1 source form WordPress Code Reference concerning rest_cookie_check_errors() function:和 1 个源表单 WordPress代码参考关于rest_cookie_check_errors()函数:

Reference / Functions / rest_cookie_check_errors() 参考资料 / 函数 / rest_cookie_check_errors()

For those interested in full story of my findings, following link to my thread with answers, code snippets and additional findings.对于那些对我的发现的完整故事感兴趣的人,请点击我的主题链接,其中包含答案、代码片段和其他发现。

How to force Authentication on REST API for Password protected page using custom table and fetch() without Plugin 如何在没有插件的情况下使用自定义表和 fetch() 在受密码保护的页面上强制对 REST API 进行身份验证

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM