Good Question and not that easy to do propperly (took me 1 week to figure that out).
Then I found 2 good summaries in WordPress docs:
Home / REST API Handbook / Extending the REST API / Routes and Endpoints
Home / REST API Handbook / Extending the REST API / Adding Custom Endpoints
There I found out how to use namespaces, routes and permission_callback correctly.
Critical part was to add the Permission Callback into the function.php
of your theme, as @Jacob Peattie mentioned correctly in his answer.
Following, I used the if ( !isset($_COOKIE['wp-postpass_'. COOKIEHASH] ))
in the Permission Callback (being the wp-postpass_
cookie the one that holds the token for password protected posts, as an alternative to is_user_logged_in()
)
/**
* This is our callback function to return (GET) our data.
*
* @param WP_REST_Request $request This function accepts a rest request to process data.
*/
function get_your_data($request) {
global $wpdb;
$yourdata = $wpdb->get_results("SELECT * FROM your_custom_table");
return rest_ensure_response( $yourdata );
};
/**
* This is our callback function to insert (POST) new data record.
*
* @param WP_REST_Request $request This function accepts a rest request to process data.
*/
function insert_your_data($request) {
global $wpdb;
$contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : '';
if ($contentType === "application/json") {
$content = trim(file_get_contents("php://input"));
$decoded = json_decode($content, true);
$newrecord = $wpdb->insert( 'your_custom_table', array( 'column_1' => $decoded['column_1'], 'column_2' => $decoded['column_2']));
};
if($newrecord){
return rest_ensure_response($newrecord);
}else{
//something gone wrong
return rest_ensure_response('failed');
};
header("Content-Type: application/json; charset=UTF-8");
};
/**
* This is our callback function to update (PUT) a data record.
*
* @param WP_REST_Request $request This function accepts a rest request to process data.
*/
function update_your_data($request) {
global $wpdb;
$contentType = isset($_SERVER["CONTENT_TYPE"]) ? trim($_SERVER["CONTENT_TYPE"]) : '';
if ($contentType === "application/json") {
$content = trim(file_get_contents("php://input"));
$decoded = json_decode($content, true);
$updatedrecord = $wpdb->update( 'your_custom_table', array( 'column_1' => $decoded['column_1'], 'column_2' => $decoded['column_2']), array('id' => $decoded['id']), array( '%s' ));
};
if($updatedrecord){
return rest_ensure_response($updatedrecord);
}else{
//something gone wrong
return rest_ensure_response('failed');
};
header("Content-Type: application/json; charset=UTF-8");
};
// Permission Callback
// 'ypp' is the Prefix I chose (ypp = Your Private Page)
function ypp_get_private_data_permissions_check() {
// 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 ) );
};
// 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;
};
// And then add the permission_callback to your POST and PUT routes:
add_action('rest_api_init', function() {
/**
* Register here your custom routes for your CRUD functions
*/
register_rest_route( 'your_private_page/v1', '/data', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => 'get_your_data',
// Always allow.
'permission_callback' => '__return_true' // <-- you can protect GET as well if your like
),
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => 'insert_your_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' => 'ypp_get_private_data_permissions_check', // <-- that was the missing part
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => 'update_your_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' => 'ypp_get_private_data_permissions_check', // <-- that was the missing part
),
));
});
If you like, I posted a Question (similar issue to yours concerning JWT Authentication) and then my findings in the answer as it worked finally.
Full story with full code at:
How to force Authentication on REST API for Password protected page using custom table and fetch() without Plugin
Hope this helps a little.