WordPress 5.6以降から実装されるアプリケーションパスワード

Tech

WordPressのREST APIは、WordPressに保存された投稿データ等をシステム間で連携するときのインタフェースとして利用する場面が多いです。
これまでは、AWS WAFで対向システムのIPのみ許可するとか独自でOAuthを容易する等、認証周りについて頭を悩ませることが多かったのですが、アプリケーションパスワードが実装されることによって悩みが一つ減る感じになるのかなという感覚です。

5.6ベータのリリースノートによると、アプリケーションパスワードを試すことができるようになってるみたいなので、今回はコアの中身がどう変わっていてどのように利用できるかを調査してみようと思います。

※「アプリケーションパスワードが何なのか?」「今までの認証方式は?」という話は他所ですでに出てるので、ここでは言及しないつもりです。

コードの差分

default-filters.phpにユーザー情報をチェックするhookが追加されてます。

add_filter( 'determine_current_user', 'wp_validate_application_password', 20 );

callbackでは、ヘッダに PHP_AUTH_USER が設定されているときのみ整合性をチェックしているようです。
wp-json配下に別途Webサーバー側でBasic認証をかけている場合は微妙な動きになりそうではあります。

/**
 * Validates the application password credentials passed via Basic Authentication.
 *
 * @since 5.6.0
 *
 * @param int|bool $input_user User ID if one has been determined, false otherwise.
 * @return int|bool The authenticated user ID if successful, false otherwise.
 */
function wp_validate_application_password( $input_user ) {
	// Don't authenticate twice.
	if ( ! empty( $input_user ) ) {
		return $input_user;
	}

	if ( ! wp_is_application_passwords_available() ) {
		return $input_user;
	}

	// Check that we're trying to authenticate
	if ( ! isset( $_SERVER['PHP_AUTH_USER'] ) ) {
		return $input_user;
	}

	$authenticated = wp_authenticate_application_password( null, $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] );

	if ( $authenticated instanceof WP_User ) {
		return $authenticated->ID;
	}

	// If it wasn't a user what got returned, just pass on what we had received originally.
	return $input_user;
}

user-edit.phpには、アプリケーションパスワードを設定するフォームが追加されています。

		<?php if ( wp_is_application_passwords_available_for_user( $user_id ) ) : ?>
	<div class="application-passwords hide-if-no-js" id="application-passwords-section">
		<h2><?php _e( 'Application Passwords' ); ?></h2>
		<p><?php _e( 'Application passwords allow authentication via non-interactive systems, such as XML-RPC or the REST API, without providing your actual password. Application passwords can be easily revoked. They cannot be used for traditional logins to your website.' ); ?></p>
		<div class="create-application-password">
			<label for="new_application_password_name" class="screen-reader-text"><?php _e( 'New Application Password Name' ); ?></label>
			<input type="text" size="30" id="new_application_password_name" name="new_application_password_name" placeholder="<?php esc_attr_e( 'New Application Password Name' ); ?>" class="input" />

			<?php
			/**
			 * Fires in the create Application Passwords form.
			 *
			 * @since 5.6.0
			 *
			 * @param WP_User $profileuser The current WP_User object.
			 */
			do_action( 'wp_create_application_password_form', $profileuser );
			?>

			<?php submit_button( __( 'Add New' ), 'secondary', 'do_new_application_password', false ); ?>
		</div>

		<div class="application-passwords-list-table-wrapper">
			<?php
			$application_passwords_list_table = _get_list_table( 'WP_Application_Passwords_List_Table', array( 'screen' => 'application-passwords-user' ) );
			$application_passwords_list_table->prepare_items();
			$application_passwords_list_table->display();
			?>
		</div>
	</div>

使い方

実際に使ってみます。
前提としてSSL化されている状態であることとなっているので、localhostでやる場合は、
WP_ENVIRONMENT_TYPE にlocalを指定するか、アプリケーションパスワードを有効化する必要があります。

define('WP_ENVIRONMENT_TYPE', 'local');

または

add_filter('wp_is_application_passwords_available', '__return_true');


適当なアプリケーション名を入力してAdd Newすればパスワードを生成してくれるのでそれを使います。

この状態でcurlを叩いてみます。

ユーザー名と生成したパスワードの組み合わせが正しければ200が返却されます。
誤っていれば401が返却され、アプリケーションパスワードでの認証が効いていることが確認できます。

// 正しい場合
$ curl -Iu USER:PASS http://localhost/wp-json/wp/v2/posts
HTTP/1.1 200 OK

// 誤っている場合
$ curl -Iu USER:PASS http://localhost/wp-json/wp/v2/posts
HTTP/1.1 401 Unauthorized

ちなみにユーザーとパスワードがヘッダに存在しない場合も200が返却されます。

$ curl -I http://localhost/wp-json/wp/v2/posts
HTTP/1.1 200 OK

これの正しい回避方法がいまいちわかってませんが、PHP_AUTH_USER がない場合は空を代入してあげるくらいでしょうか。

タイトルとURLをコピーしました