HOME / BLOG
BLOG

WordPress REST APIをnonceで安全に使う方法【wp_create_nonce・permission_callback】

デフォルト画像

WordPressのREST APIをテーマ内から使うときは、単にエンドポイントを作るだけではなく、noncepermission_callback をセットで考える必要があります。特に投稿保存、お気に入り登録、管理者向け操作など、ログインユーザーの権限に関係する処理では必須です。

この記事では、テーマ内JavaScriptから X-WP-Nonce を付けてPOSTし、REST API側で権限チェックする最小構成を作ります。

functions.php

<?php
add_action('wp_enqueue_scripts', function () {
    wp_enqueue_script(
        'theme-favorite',
        get_template_directory_uri() . '/js/favorite.js',
        [],
        '1.0.0',
        true
    );

    wp_localize_script('theme-favorite', 'themeFavorite', [
        'root' => esc_url_raw(rest_url('theme/v1/favorite')),
        'nonce' => wp_create_nonce('wp_rest'),
    ]);
});

add_action('rest_api_init', function () {
    register_rest_route('theme/v1', '/favorite', [
        'methods' => WP_REST_Server::CREATABLE,
        'callback' => 'theme_save_favorite',
        'permission_callback' => function () {
            return current_user_can('edit_posts');
        },
        'args' => [
            'post_id' => [
                'required' => true,
                'type' => 'integer',
                'sanitize_callback' => 'absint',
            ],
        ],
    ]);
});

function theme_save_favorite(WP_REST_Request $request): WP_REST_Response
{
    $post_id = (int) $request->get_param('post_id');

    if ($post_id <= 0 || get_post_status($post_id) === false) {
        return new WP_REST_Response([
            'message' => '投稿が見つかりません。',
        ], 404);
    }

    update_user_meta(get_current_user_id(), 'theme_favorite_post', $post_id);

    return new WP_REST_Response([
        'post_id' => $post_id,
        'saved' => true,
    ], 200);
}

JavaScript

const button = document.querySelector("[data-favorite-post]");

button?.addEventListener("click", async () => {
  const postId = Number(button.dataset.favoritePost);

  const response = await fetch(themeFavorite.root, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-WP-Nonce": themeFavorite.nonce,
    },
    credentials: "same-origin",
    body: JSON.stringify({ post_id: postId }),
  });

  if (!response.ok) {
    throw new Error("保存に失敗しました。");
  }

  const data = await response.json();
  button.textContent = data.saved ? "保存しました" : "保存する";
});

nonceとpermission_callbackの役割

項目役割
wp_create_nonce("wp_rest")WordPress REST API用のnonceを発行する
X-WP-NonceJavaScriptのfetchからnonceを送るためのヘッダー
permission_callbackこのエンドポイントを実行できるユーザーか判定する
current_user_can()WordPressの権限で操作可否を確認する

よくある失敗

  • permission_callback__return_true を入れたまま公開してしまう
  • ログインユーザー向けAPIなのに credentials: "same-origin" を付け忘れる
  • nonceをURLに直書きしてキャッシュやログに残しやすくする
  • REST API側で投稿IDや権限の再チェックをしない

あわせて読みたい

参考資料