Pour ceux qui arrivent de Google: vous ne devriez probablement pas obtenir les nonces de l'API REST , sauf si vous savez vraiment ce que vous faites. L'authentification basée sur les cookies avec l'API REST est uniquement destinée aux plugins et aux thèmes. Pour une application d'une seule page, vous devriez probablement utiliser OAuth .
Cette question existe parce que la documentation n'est pas / n'était pas claire sur la façon dont vous devez réellement vous authentifier lors de la création d'applications à page unique, les JWT ne sont pas vraiment adaptés aux applications Web et OAuth est plus difficile à mettre en œuvre que l'authentification basée sur les cookies.
Le manuel contient un exemple sur la façon dont le client JavaScript Backbone gère les nonces, et si je suis l'exemple, j'obtiens un nonce que les points de terminaison intégrés tels que / wp / v2 / posts acceptent.
\wp_localize_script("client-js", "theme", [
'nonce' => wp_create_nonce('wp_rest'),
'user' => get_current_user_id(),
]);
Cependant, l'utilisation de Backbone est hors de question, tout comme les thèmes, j'ai donc écrit le plugin suivant:
<?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,
];
},
]);
});
J'ai bricolé un peu dans la console JavaScript et j'ai écrit ce qui suit:
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()
Le résultat escompté est deux nouveaux messages, mais je reçois Cookie nonce is invalid
du premier, et le second crée le message avec succès. C'est probablement parce que les nonces sont différents, mais pourquoi? Je suis connecté en tant que même utilisateur dans les deux demandes.
Si mon approche est fausse, comment dois-je obtenir le nonce?
Modifier :
J'ai essayé de jouer avec les mondiaux sans trop de chance . A obtenu un peu plus de chance en utilisant l'action 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,
];
},
]);
});
Maintenant, lorsque j'exécute le JavaScript ci-dessus, deux publications sont créées, mais le point de terminaison de vérification échoue!
Je suis allé déboguer 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!
J'ai ajouté un peu de journalisation
// 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;
}
et le code JavaScript entraîne désormais les entrées suivantes. Comme vous pouvez le voir, lorsque le point de terminaison de vérification est appelé, uid est 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
la source
$_GET['nonce']
, mais l'en-tête ou le$_GET['_wpnonce']
paramètre nonce . Correct?Bien que cette solution fonctionne, elle n'est pas recommandée . OAuth est le choix préféré.
Je crois que j'ai compris.
Je pense que wp_verify_nonce est cassé, car wp_get_current_user ne parvient pas à obtenir le bon objet utilisateur.Ce n'est pas le cas, comme illustré par Otto.
Heureusement, il a un filtre:
$uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
En utilisant ce filtre, j'ai pu écrire ce qui suit, et le code JavaScript s'exécute comme il se doit:
Si vous détectez un problème de sécurité avec le correctif, veuillez me donner un cri, pour le moment je ne vois rien de mal à cela, à part les globaux.
la source
En regardant tout ce code, il semble que votre problème soit l'utilisation de fermetures. À l'
init
étape, vous ne devez définir que des hooks et ne pas évaluer les données car tout le noyau n'a pas terminé le chargement et l'initialisation.Dans
le
$user
est destiné à être utilisé tôt dans la fermeture, mais personne ne vous promet que le cookie a déjà été traité et qu'un utilisateur a été authentifié sur la base de ceux-ci. Un meilleur code seraComme toujours avec n'importe quel crochet dans wordpress, utilisez le dernier crochet possible et n'essayez jamais de précalculer tout ce que vous n'avez pas à faire.
la source