WordPress API とアプリケーションパスワード認証で画像をアップロードする

feature-image

WordPress REST API を用いて画像をアップロードするコードを書くにあたって、「そういえば WordPress の REST-API ってどうなっているんだ?認証方法は?」という疑問が出てきました。

この記事では、WordPress の REST API向けコア機能であるアプリケーションパスワードについて記載しています。

この記事で説明すること

  • WordPress の REST-API を使うための認証手段である「アプリケーションパスワード」に関する概要の説明
  • アプリケーションパスワードを使って、WordPress REST API 経由で画像をアップロードするための実行例を説明

「REST-API とは何ぞや」みたいな話はここではしません。

WordPress のアプリケーションパスワードとは

「アプリケーションパスワード」という単語そのものは抽象的でどこにでもありそうな名前ですが、WordPress 5.6 でひとつの機能としてリリースされています。

REST API authentication with Application Passwords

Thanks to the API’s new Application Passwords authorization feature, third-party apps can connect to your site seamlessly and securely. This new REST API feature lets you see what apps are connecting to your site and control what they do. News – WordPress 5.6 “Simone”― https://wordpress.org/news/2020/12/simone/

一言でいえば自信が普段WordPressの管理画面にログインするための ID・パスワードとは別に、連携するプログラム向けの専用パスワードを作成する機能です。

従来はひとつのプラグインとして提供されていたものが、本家の機能として取り込まれたようですね。

Developer 向け表現の補足

要は単純に、APIのためのパスワードです。IDは普段のログイン用と同じものを用いますので、トークンというよりはパスワードと捉えた方が近い印象です。HTTP ヘッダーにBASIC認証と同様 Authorization: Bearer base64(usernamepassword) とセットするだけです。

プラグイン時代のレポジトリはこちらにありました。

WP-API/WP-API: The WP REST API has been merged into WordPress core. Please do not create issues or send pull requests. Submit support requests to the forums or patches to Trac (see README below for links). ― https://github.com/WP-API/WP-API

OAuth、OpenID Connect、あるいは JWT などを用いた認証をしたい場合は、それぞれサードパーティプラグインが存在するようですので試してみてください。

なお反対に、REST-APIを止めるプラグインもあるようです。APIを全く使う必要がなく少しでもセキュリティリスクを小さくしたい場合は導入してもいいかもしれませんね。特に、システムの更新のための労力が大きくスピード感の劣る企業なんかでは有益かもしれません。

Disable REST API – WordPress plugin | WordPress.org https://wordpress.org/plugins/disable-json-api/

注意:私自身では未検証のプラグインです。

アプリケーションパスワードの設定方法

WordPress のバージョンが上がっていれば、ユーザー画面に作成画面が表示されているはずです。

具体的には、管理画面の「ユーザー」→「任意のユーザーを選択」→「ユーザーページの最下部にて『新しいアプリケーションパスワードを追加』」です。

設定項目が表示されない場合(Dockerとか)

自身が遭遇したケースは、ローカルの開発環境でした。具体的には、HTTPS の環境でない場合は標準設定では無効化されるようです。

errors - Application passwords not working on localhost? - WordPress Development Stack Exchange ― https://wordpress.stackexchange.com/questions/383244/application-passwords-not-working-on-localhost

本番環境では通信の暗号化(HTTPS化)は必須と言えますので、この標準設定は妥当でしょう。ですがテスト環境では必ずしもHTTPS化をしているとは限らないことから、その場合は設定値を追加することで解決できます。以下の設定を wp-config.php に書き足してください。

define( 'WP_ENVIRONMENT_TYPE', 'local' );

Docker 環境への設定方法

別の記事で紹介しているように Docker で WordPress を動かしている場合は、 WORDPRESS_CONFIG_EXTRA 環境変数に設定値を入れることで追加設定が可能です。

追加設定が1つだけでいい場合

WORDPRESS_CONFIG_EXTRA: define('WP_ENVIRONMENT_TYPE', 'local');

追加設定を2つ以上したい場合

WORDPRESS_CONFIG_EXTRA に改行込で複数の行を書き足せば問題ありません。 docker-compose でのYAML設定の場合は以下の通りです。

WORDPRESS_CONFIG_EXTRA: |
        define('WP_ENVIRONMENT_TYPE', 'local');
        define('WP_ほげほげ設定', 'xxx');        
Docker でお手軽 WordPress 練習環境の作り方 - MacBook M1/M2版
Docker でお手軽 WordPress 練習環境の作り方 - MacBook M1/M2版
https://fand.jp/technologies/2022/01/how-to-build-a-wordpress-test-environment-with-docker-on-m1-mac-book/
MacBook M1チップセットで WordPress の動作テスト環境をお手軽に構築する方法を説明します。

REST Client で画像をアップロードする方法

以下に示す動作例の前提

  • ID: username / PW: password
  • アップロード対象の画像は /Users/yourname/tmp/testFile.png に保存されているとする
  • WordPressサイトのURLは https://dev.example.com とする

RESTリクエスト例

リクエスト(以下の記載は Visual Studio Code の RESTクライアントの例)

@token = Basic username password

### メディア一覧の取得
GET http://dev.example.com/wp-json/wp/v2/media HTTP/1.1
Authorization: {{token}}

### メディアをアップロードする
POST http://dev.example.com/wp-json/wp/v2/media HTTP/1.1
Authorization: {{token}}
Content-Disposition: form-data; name="image"; filename="testFile.png"
Content-Type: image/png

< /Users/yourname/tmp/testFile.png

レスポンス

HTTP/1.1 201 Created
Date: Sat, 22 Jan 2022 13:46:58 GMT
Server: Apache/2.4.51 (Debian)
X-Powered-By: PHP/7.4.27
X-Robots-Tag: noindex
Link: <http://dev.example.com/wp-json/>; rel="https://api.w.org/"
X-Content-Type-Options: nosniff
Access-Control-Expose-Headers: X-WP-Total, X-WP-TotalPages, Link
Access-Control-Allow-Headers: Authorization, X-WP-Nonce, Content-Disposition, Content-MD5, Content-Type
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
X-WP-Upload-Attachment-ID: 56
Location: http://dev.example.com/wp-json/wp/v2/media/56
Allow: GET, POST
Content-Length: 5879
Connection: close
Content-Type: application/json; charset=UTF-8
{
  "id": 57,
  "date": "2022-01-22T22:51:12",
  "date_gmt": "2022-01-22T13:51:12",
  "guid": {
    "rendered": "http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg.png",
    "raw": "http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg.png"
  },
  "modified": "2022-01-22T22:51:12",
  "modified_gmt": "2022-01-22T13:51:12",
  "slug": "2022-testimg",
  "status": "inherit",
  "type": "attachment",
  "link": "http:\/\/dev.example.com\/2022-testimg\/",
  "title": {
    "raw": "2022-testImg",
    "rendered": "2022-testImg"
  },
  "author": 1,
  "comment_status": "open",
  "ping_status": "closed",
  "template": "",
  "meta": [],
  "permalink_template": "http:\/\/dev.example.com\/?attachment_id=57",
  "generated_slug": "2022-testimg",
  "description": {
    "raw": "",
    "rendered": "<p class=\"attachment\"><a href='http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg.png'><img width=\"300\" height=\"139\" src=\"http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg-300x139.png\" class=\"attachment-medium size-medium\" alt=\"\" loading=\"lazy\" srcset=\"http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg-300x139.png 300w, http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg-1024x476.png 1024w, http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg-768x357.png 768w, http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg-1536x714.png 1536w, http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg-2048x952.png 2048w, http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg-1568x729.png 1568w\" sizes=\"(max-width: 300px) 100vw, 300px\" style=\"width:100%;height:46.5%;max-width:2284px;\" \/><\/a><\/p>\n"
  },
  "caption": {
    "raw": "",
    "rendered": ""
  },
  "alt_text": "",
  "media_type": "image",
  "mime_type": "image\/png",
  "media_details": {
    "width": 2284,
    "height": 1062,
    "file": "2022\/01\/2022-testImg.png",
    "sizes": {
      "medium": {
        "file": "2022-testImg-300x139.png",
        "width": 300,
        "height": 139,
        "mime_type": "image\/png",
        "source_url": "http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg-300x139.png"
      },
      "large": {
        "file": "2022-testImg-1024x476.png",
        "width": 1024,
        "height": 476,
        "mime_type": "image\/png",
        "source_url": "http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg-1024x476.png"
      },
      "thumbnail": {
        "file": "2022-testImg-150x150.png",
        "width": 150,
        "height": 150,
        "mime_type": "image\/png",
        "source_url": "http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg-150x150.png"
      },
      "medium_large": {
        "file": "2022-testImg-768x357.png",
        "width": 768,
        "height": 357,
        "mime_type": "image\/png",
        "source_url": "http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg-768x357.png"
      },
      "1536x1536": {
        "file": "2022-testImg-1536x714.png",
        "width": 1536,
        "height": 714,
        "mime_type": "image\/png",
        "source_url": "http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg-1536x714.png"
      },
      "2048x2048": {
        "file": "2022-testImg-2048x952.png",
        "width": 2048,
        "height": 952,
        "mime_type": "image\/png",
        "source_url": "http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg-2048x952.png"
      },
      "post-thumbnail": {
        "file": "2022-testImg-1568x729.png",
        "width": 1568,
        "height": 729,
        "mime_type": "image\/png",
        "source_url": "http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg-1568x729.png"
      },
      "full": {
        "file": "2022-testImg.png",
        "width": 2284,
        "height": 1062,
        "mime_type": "image\/png",
        "source_url": "http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg.png"
      }
    },
    "image_meta": {
      "aperture": "0",
      "credit": "",
      "camera": "",
      "caption": "",
      "created_timestamp": "0",
      "copyright": "",
      "focal_length": "0",
      "iso": "0",
      "shutter_speed": "0",
      "title": "",
      "orientation": "0",
      "keywords": []
    }
  },
  "post": null,
  "source_url": "http:\/\/dev.example.com\/wp-content\/uploads\/2022\/01\/2022-testImg.png",
  "missing_image_sizes": [],
  "_links": {
    "self": [
      {
        "href": "http:\/\/dev.example.com\/wp-json\/wp\/v2\/media\/57"
      }
    ],
    "collection": [
      {
        "href": "http:\/\/dev.example.com\/wp-json\/wp\/v2\/media"
      }
    ],
    "about": [
      {
        "href": "http:\/\/dev.example.com\/wp-json\/wp\/v2\/types\/attachment"
      }
    ],
    "author": [
      {
        "embeddable": true,
        "href": "http:\/\/dev.example.com\/wp-json\/wp\/v2\/users\/1"
      }
    ],
    "replies": [
      {
        "embeddable": true,
        "href": "http:\/\/dev.example.com\/wp-json\/wp\/v2\/comments?post=57"
      }
    ],
    "wp:action-unfiltered-html": [
      {
        "href": "http:\/\/dev.example.com\/wp-json\/wp\/v2\/media\/57"
      }
    ],
    "wp:action-assign-author": [
      {
        "href": "http:\/\/dev.example.com\/wp-json\/wp\/v2\/media\/57"
      }
    ],
    "curies": [
      {
        "name": "wp",
        "href": "https:\/\/api.w.org\/{rel}",
        "templated": true
      }
    ]
  }
}

(参考)WP-Stateless で Cloud Storage に保存している場合

(特殊要件)この記事のケースでは WP-Stateless プラグインによりオブジェクトストレージに画像を配置しているため、一部 https://cdn.example.com というURLが出てきますが気にしないでください

{
  "id": 56,
  /* ... */
  /* 特筆すべき点がないので省略 */
  /* ... */
  "media_details": {
    "width": 2284,
    "height": 1062,
    "file": "2022\/01\/2000f656-2022-testimg.png",
    "sizes": {
      "medium": {
        "file": "2000f656-2022-testimg-300x139.png",
        "width": 300,
        "height": 139,
        "gs_name": "_develop\/2022\/01\/2000f656-2022-testimg-300x139.png",
        "gs_link": "https:\/\/cdn.example.com\/_develop\/2022\/01\/2000f656-2022-testimg-300x139.png",
        "mime_type": "image\/png",
        "source_url": "https:\/\/cdn.example.com\/_develop\/2022\/01\/2000f656-2022-testimg-300x139.png"
      },
      "large": {
        "file": "2000f656-2022-testimg-1024x476.png",
        "width": 1024,
        "height": 476,
        "gs_name": "_develop\/2022\/01\/2000f656-2022-testimg-1024x476.png",
        "gs_link": "https:\/\/cdn.example.com\/_develop\/2022\/01\/2000f656-2022-testimg-1024x476.png",
        "mime_type": "image\/png",
        "source_url": "https:\/\/cdn.example.com\/_develop\/2022\/01\/2000f656-2022-testimg-1024x476.png"
      },
      "thumbnail": {
        "file": "2000f656-2022-testimg-150x150.png",
        "width": 150,
        "height": 150,
        "gs_name": "_develop\/2022\/01\/2000f656-2022-testimg-150x150.png",
        "gs_link": "https:\/\/cdn.example.com\/_develop\/2022\/01\/2000f656-2022-testimg-150x150.png",
        "mime_type": "image\/png",
        "source_url": "https:\/\/cdn.example.com\/_develop\/2022\/01\/2000f656-2022-testimg-150x150.png"
      },
			/* ...以降省略。 gs_name, gs_link が増えたがそれ以外は同じ */

    },
    "image_meta": {
		  /* ... */
		  /* 特筆すべき点がないので省略 */
		  /* ... */
    },
    "filesize": 2733540,
    "gs_link": "https:\/\/cdn.example.com\/_develop\/2022\/01\/2000f656-2022-testimg.png",
    "gs_name": "_develop\/2022\/01\/2000f656-2022-testimg.png",
    "gs_bucket": "cdn.example.com"
  },
  "post": null,
  "source_url": "https:\/\/cdn.example.com\/_develop\/2022\/01\/2000f656-2022-testimg.png",
  "missing_image_sizes": [],
  "_links": {
		/* 変わらないので省略 */
  }
}

まとめ

WordPress REST API を使う時の認証方法をどうすべきか分からなかったので最近の傾向を調べました。

WordPress 5.6 から本体機能に組み込まれた REST-API によるアプリケーションパスワードを用いることで、「難しすぎない認証」「普段のユーザーIDとは別のパスワードにすることで事故のリスクを低減」することができる、程よい手段だと感じました。

目的に応じて使い分けていきましょう。

関連記事

記事中に出てきた Visual Studio Code の拡張機能 RESTクライアントについてはこちら。

VSCodeのRESTクライアント【めちゃお勧め】
VSCodeのRESTクライアント【めちゃお勧め】
https://fand.jp/rest-client-with-vs-code/
Visual Studio Code エディタ向けの使い勝手の良い REST クライアント拡張を紹介します。