Para aquellos que llegan de Google: Probablemente no debería obtener el nonces de la API REST , a menos que realmente sepa lo que está haciendo. La autenticación basada en cookies con la API REST solo está destinada a complementos y temas. Para una sola aplicación página, probablemente debería usar OAuth .
Esta pregunta existe porque la documentación no está / no estaba clara sobre cómo debería autenticarse realmente al crear aplicaciones de una sola página, los JWT no son realmente aptos para aplicaciones web y OAuth es más difícil de implementar que la autenticación basada en cookies.
El manual tiene un ejemplo de cómo el cliente Backbone JavaScript maneja nonces, y si sigo el ejemplo, obtengo un nonce que aceptan los puntos finales integrados como / wp / v2 / posts.
\wp_localize_script("client-js", "theme", [
'nonce' => wp_create_nonce('wp_rest'),
'user' => get_current_user_id(),
]);
Sin embargo, el uso de Backbone está fuera de discusión, y también los temas, así que escribí el siguiente complemento:
<?php
/*
Plugin Name: Nonce Endpoint
*/
add_action('rest_api_init', function () {
$user = get_current_user_id();
register_rest_route('nonce/v1', 'get', [
'methods' => 'GET',
'callback' => function () use ($user) {
return [
'nonce' => wp_create_nonce('wp_rest'),
'user' => $user,
];
},
]);
register_rest_route('nonce/v1', 'verify', [
'methods' => 'GET',
'callback' => function () use ($user) {
$nonce = !empty($_GET['nonce']) ? $_GET['nonce'] : false;
return [
'valid' => (bool) wp_verify_nonce($nonce, 'wp_rest'),
'user' => $user,
];
},
]);
});
Jugué un poco en la consola de JavaScript y escribí lo siguiente:
var main = async () => { // var because it can be redefined
const nonceReq = await fetch('/wp-json/nonce/v1/get', { credentials: 'include' })
const nonceResp = await nonceReq.json()
const nonceValidReq = await fetch(`/wp-json/nonce/v1/verify?nonce=${nonceResp.nonce}`, { credentials: 'include' })
const nonceValidResp = await nonceValidReq.json()
const addPost = (nonce) => fetch('/wp-json/wp/v2/posts', {
method: 'POST',
credentials: 'include',
body: JSON.stringify({
title: `Test ${Date.now()}`,
content: 'Test',
}),
headers: {
'X-WP-Nonce': nonce,
'content-type': 'application/json'
},
}).then(r => r.json()).then(console.log)
console.log(nonceResp.nonce, nonceResp.user, nonceValidResp)
console.log(theme.nonce, theme.user)
addPost(nonceResp.nonce)
addPost(theme.nonce)
}
main()
El resultado esperado son dos publicaciones nuevas, pero obtengo Cookie nonce is invalid
de la primera, y la segunda crea la publicación con éxito. Probablemente sea porque los nonces son diferentes, pero ¿por qué? He iniciado sesión como el mismo usuario en ambas solicitudes.
Si mi enfoque es incorrecto, ¿cómo debo obtener el nonce?
Editar :
Traté de jugar con los globales sin mucha suerte . Obtuve un poco más de suerte al utilizar la acción wp_loaded:
<?php
/*
Plugin Name: Nonce Endpoint
*/
$nonce = 'invalid';
add_action('wp_loaded', function () {
global $nonce;
$nonce = wp_create_nonce('wp_rest');
});
add_action('rest_api_init', function () {
$user = get_current_user_id();
register_rest_route('nonce/v1', 'get', [
'methods' => 'GET',
'callback' => function () use ($user) {
return [
'nonce' => $GLOBALS['nonce'],
'user' => $user,
];
},
]);
register_rest_route('nonce/v1', 'verify', [
'methods' => 'GET',
'callback' => function () use ($user) {
$nonce = !empty($_GET['nonce']) ? $_GET['nonce'] : false;
error_log("verify $nonce $user");
return [
'valid' => (bool) wp_verify_nonce($nonce, 'wp_rest'),
'user' => $user,
];
},
]);
});
Ahora cuando ejecuto el JavaScript anterior, se crean dos publicaciones, ¡pero el punto final de verificación falla!
Fui a depurar wp_verify_nonce:
function wp_verify_nonce( $nonce, $action = -1 ) {
$nonce = (string) $nonce;
$user = wp_get_current_user();
$uid = (int) $user->ID; // This is 0, even though the verify endpoint says I'm logged in as user 2!
Agregué algunos registros
// Nonce generated 0-12 hours ago
$expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce'), -12, 10 );
error_log("expected 1 $expected received $nonce uid $uid action $action");
if ( hash_equals( $expected, $nonce ) ) {
return 1;
}
// Nonce generated 12-24 hours ago
$expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
error_log("expected 2 $expected received $nonce uid $uid action $action");
if ( hash_equals( $expected, $nonce ) ) {
return 2;
}
y el código JavaScript ahora da como resultado las siguientes entradas. Como puede ver, cuando se llama al punto final de verificación, uid es 0.
[01-Mar-2018 11:41:57 UTC] verify 716087f772 2
[01-Mar-2018 11:41:57 UTC] expected 1 b35fa18521 received 716087f772 uid 0 action wp_rest
[01-Mar-2018 11:41:57 UTC] expected 2 dd35d95cbd received 716087f772 uid 0 action wp_rest
[01-Mar-2018 11:41:58 UTC] expected 1 716087f772 received 716087f772 uid 2 action wp_rest
[01-Mar-2018 11:41:58 UTC] expected 1 716087f772 received 716087f772 uid 2 action wp_rest
fuente
$_GET['nonce']
, sino el encabezado o$_GET['_wpnonce']
parámetro nonce . ¿Correcto?Si bien esta solución funciona, no se recomienda . OAuth es la opción preferida.
Creo que lo tengo.
Yo creo que wp_verify_nonce se rompe, como wp_get_current_user no puede obtener el objeto de usuario adecuada.No lo es, como lo ilustra Otto.
Afortunadamente tiene un filtro:
$uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
Usando este filtro pude escribir lo siguiente, y el código JavaScript se ejecuta como debería:
Si detecta un problema de seguridad con la solución, avíseme, en este momento no puedo ver nada malo, aparte de los globales.
fuente
Mirando todo este código, parece que su problema es el uso de cierres. En la
init
etapa, solo debe establecer ganchos y no evaluar los datos, ya que no todo el núcleo había terminado de cargarse e inicializarse.En
el
$user
está obligado pronto para ser utilizado en el cierre, pero nadie promesas a usted que la cookie ya estaban manipulados y un usuario se ha autenticado en base a ellos. Un mejor código seráComo siempre con cualquier gancho en WordPress, use el último gancho posible y nunca intente calcular previamente nada que no sea necesario.
fuente