使用Fetch进行WordPress REST API身份验证

时间:2017-09-13 18:05:48

标签: javascript php wordpress cookies wordpress-rest-api

我尝试使用cookie authentication使用Fetch API进行WordPress REST API访问,但是auth失败并出现以下错误。

  

403:Cookie Nonce无效

我使用以下脚本连接到API。

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));

当我从使用Fetch切换到XMLHttpRequest时,它按预期工作。

{{1}}

在Fetch方法中发送标头的方式是否有问题?

3 个答案:

答案 0 :(得分:2)

WordPress nonce身份验证需要使用Cookie,默认情况下,Fetch不会发送这些身份验证。您可以使用凭据选项来完成此任务:

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

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

答案 1 :(得分:0)

从 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);

答案 2 :(得分:0)

晚了,但可能对其他读者有所帮助,因为我根据这个问题专门为 fetch() 承诺添加了代码。

据我所知,WordPress 在其 cookie 中自动使用 nonce

WordPress:版本 5.7.2
PHP:7.4 版
主机:hostmonster.com
客户端:Windows 10
浏览器:在 Chrome、Firefox、甚至 Edge 上测试过 ? 工作

代码(安装主题的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;
}

确保在您的 HTTP 请求中包含您的 HTML 页面中的 credentials: 'same-origin',如以上先前的回答和评论中所述。

代码(带有内联 <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>

最后的想法:

'Authorization': 'Bearer ' + token 是否需要在 HTTP 请求头中?

经过一些测试后,我意识到权限回调中的if ( !isset($_COOKIE['wp-postpass_'. COOKIEHASH] )) { ...不仅会检查客户端浏览器上是否设置了Cookie,而且似乎还会检查其值( JWT 令牌)

因为我像我的初始代码一样反复检查,传递错误令牌,消除 cookie,或保持会话打开但在后端更改站点的密码(因此 WordPress 会创建一个新令牌,因此值为 set wp_postpass_ cookie 会改变)并且所有测试都正确进行 - REST API 被阻止,不仅验证 cookie 的存在,还验证其价值(这很好 - 谢谢 WordPress 团队)。 >

来源:
我在 FAQ section 中找到了有关上述想法的以下资源:

Why is the REST API not verifying the incoming Origin header? Does this expose my site to CSRF attacks?

<块引用>

因为 WordPress REST API 不验证 Origin 标头 传入请求,因此可以访问公共 REST API 端点 从任何网站。这是一个有意的设计决定。

然而,WordPress 有一个现有的 CSRF 保护机制, 使用随机数。

根据我目前的测试,WP 身份验证方式运行良好

为 WordPress 团队点赞 ?

来自 WordPress REST API 手册的另外 2 个来源:

REST API Handbook / Extending the REST API / Routes and Endpoints
REST API Handbook / Extending the REST API / Adding Custom Endpoints

和 1 个源表单 WordPress 代码参考关于 rest_cookie_check_errors() 函数:

Reference / Functions / rest_cookie_check_errors()

对于那些对我的发现的完整故事感兴趣的人,请点击我的主题链接,其中包含答案、代码片段和其他发现。

How to force Authentication on REST API for Password protected page using custom table and fetch() without Plugin