From 5eadc07aee72a293c3f78248857e705b408ca0f1 Mon Sep 17 00:00:00 2001
From: David Shanske
Date: Wed, 15 Nov 2023 01:21:20 +0000
Subject: [PATCH 1/3] Fix PKCE issue where parameters were not passed for
verification
---
includes/class-indieauth-authorization-endpoint.php | 2 +-
templates/indieauth-authenticate-form.php | 3 +++
templates/indieauth-authorize-form.php | 2 ++
3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/includes/class-indieauth-authorization-endpoint.php b/includes/class-indieauth-authorization-endpoint.php
index 2767bbb..0eb0d7f 100644
--- a/includes/class-indieauth-authorization-endpoint.php
+++ b/includes/class-indieauth-authorization-endpoint.php
@@ -320,6 +320,7 @@ public function authorization_code( $params ) {
}
$code = $params['code'];
+ $code_verifier = isset( $params['code_verifier'] ) ? $params['code_verifier'] : null;
$params = wp_array_slice_assoc( $params, array( 'client_id', 'redirect_uri' ) );
$token = $this->get_code( $code );
$scopes = isset( $token['scope'] ) ? array_filter( explode( ' ', $token['scope'] ) ) : array();
@@ -335,7 +336,6 @@ public function authorization_code( $params ) {
unset( $token['exp'] );
// If there is a code challenge
if ( isset( $token['code_challenge'] ) ) {
- $code_verifier = $request->get_param( 'code_verifier' );
if ( ! $code_verifier ) {
$this->delete_code( $code, $token['user'] );
return new WP_OAuth_Response( 'invalid_grant', __( 'Failed PKCE Validation', 'indieauth' ), 400 );
diff --git a/templates/indieauth-authenticate-form.php b/templates/indieauth-authenticate-form.php
index 1c9d0a8..d759424 100644
--- a/templates/indieauth-authenticate-form.php
+++ b/templates/indieauth-authenticate-form.php
@@ -55,6 +55,9 @@
+
+
+
diff --git a/templates/indieauth-authorize-form.php b/templates/indieauth-authorize-form.php
index b816c4c..e636385 100644
--- a/templates/indieauth-authorize-form.php
+++ b/templates/indieauth-authorize-form.php
@@ -69,6 +69,8 @@
Date: Wed, 15 Nov 2023 01:21:44 +0000
Subject: [PATCH 2/3] Switch PKCE from displaying when used to displaying when
not used
---
templates/indieauth-notices.php | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/templates/indieauth-notices.php b/templates/indieauth-notices.php
index 488f4ad..cb10fba 100644
--- a/templates/indieauth-notices.php
+++ b/templates/indieauth-notices.php
@@ -7,14 +7,14 @@
-
- 🔒
+
+ 🛡️
PKCE' ),
+ sprintf( __( 'This app is not using %s for security which is now required for IndieAuth', 'indieauth' ), 'PKCE' ),
array(
'a' => array(
'href' => array(),
From 36516230a13090d0b2b52ea882c8d0d7dc889af1 Mon Sep 17 00:00:00 2001
From: David Shanske
Date: Wed, 15 Nov 2023 01:40:30 +0000
Subject: [PATCH 3/3] Update Web Sign In to the Latest Specification
---
includes/class-web-signin.php | 75 +++++++++++++++++++++++++++++------
1 file changed, 62 insertions(+), 13 deletions(-)
diff --git a/includes/class-web-signin.php b/includes/class-web-signin.php
index 6c4a8e5..4d666ae 100644
--- a/includes/class-web-signin.php
+++ b/includes/class-web-signin.php
@@ -37,8 +37,11 @@ public function settings() {
* @param string $redirect_uri where to redirect
*/
public function websignin_redirect( $me, $redirect_uri ) {
- $authorization_endpoint = find_rels( $me, 'authorization_endpoint' );
- if ( ! $authorization_endpoint ) {
+ $endpoints = find_rels( $me, array( 'indieauth-metadata', 'authorization_endpoint' ) );
+
+ if ( array_key_exists( 'indieauth-metadata', $endpoints ) ) {
+ $state = $this->get_indieauth_metadata( $endpoints['indieauth-metadata'] );
+ } elseif ( ! array_key_exists( 'authorization_endpoint', $endpoints ) ) {
return new WP_Error(
'authentication_failed',
__( 'ERROR: Could not discover endpoints', 'indieauth' ),
@@ -46,23 +49,58 @@ public function websignin_redirect( $me, $redirect_uri ) {
'status' => 401,
)
);
+ } else {
+ $state = array(
+ 'me' => $me,
+ 'authorization_endpoint' => $endpoints['authorization_endpoint'],
+ );
}
- $state = compact( 'me', 'authorization_endpoint' );
+ $state['me'] = $me;
+ $state['code_verifier'] = wp_generate_password( 128, false );
+
$token = new Token_Transient( 'indieauth_state' );
$query = add_query_arg(
array(
- 'me' => rawurlencode( $me ),
- 'redirect_uri' => rawurlencode( $redirect_uri ),
- 'client_id' => rawurlencode( home_url() ),
- 'state' => $token->set_with_cookie( $state, 120 ),
- 'response_type' => 'id',
+ 'response_type' => 'code', // In earlier versions of the specification this was ID.
+ 'client_id' => rawurlencode( home_url() ),
+ 'redirect_uri' => rawurlencode( $redirect_uri ),
+ 'state' => $token->set_with_cookie( $state, 120 ),
+ 'code_challenge' => base64_urlencode( indieauth_hash( $state['code_verifier'] ) ),
+ 'code_challenge_method' => 'S256',
+ 'me' => rawurlencode( $me ),
),
- $authorization_endpoint
+ $endpoints['authorization_endpoint']
);
// redirect to authentication endpoint
wp_redirect( $query );
}
+ // Retrieves the Metadata from an IndieAuth Metadata Endpoint.
+ public function get_indieauth_metadata( $url ) {
+ $resp = wp_remote_get(
+ $url,
+ array(
+ 'headers' => array(
+ 'Accept' => 'application/json',
+ ),
+ )
+ );
+ if ( is_wp_error( $resp ) ) {
+ return $resp;
+ }
+
+ $code = (int) wp_remote_retrieve_response_code( $resp );
+
+ if ( ( $code / 100 ) !== 2 ) {
+ return new WP_Error( 'no_metadata_endpoint', __( 'No Metadata Endpoint Found', 'indieauth' ) );
+ }
+
+ $body = wp_remote_retrieve_body( $resp );
+ return json_decode( $body, true );
+ }
+
+
+
// $args must consist of redirect_uri, client_id, and code
public function verify_authorization_code( $post_args, $endpoint ) {
if ( ! wp_http_validate_url( $endpoint ) ) {
@@ -70,7 +108,8 @@ public function verify_authorization_code( $post_args, $endpoint ) {
}
$defaults = array(
- 'client_id' => home_url(),
+ 'client_id' => home_url(),
+ 'grant_type' => 'authorization_code',
);
$post_args = wp_parse_args( $post_args, $defaults );
@@ -134,10 +173,20 @@ public function authenticate( $user, $url ) {
if ( is_wp_error( $state ) ) {
return $state;
}
+ if ( array_key_exists( 'iss', $_REQUEST ) ) {
+ $iss = rawurldecode( $_REQUEST['iss'] );
+ if ( $iss !== $state['issuer'] ) {
+ return new WP_Error( 'indieauth_iss_error', __( 'Issuer Parameter does not Match Server Metadata', 'indieauth' ) );
+ }
+ } elseif ( array_key_exists( 'issuer', $state ) ) {
+ return new WP_Error( 'indieauth_iss_error', __( 'Issuer Parameter Present in Metadata Endpoint But Not Returned by Authorization Endpoint', 'indieauth' ) );
+ }
+
$response = $this->verify_authorization_code(
array(
- 'code' => $_REQUEST['code'],
- 'redirect_uri' => wp_login_url( $redirect_to ),
+ 'code' => $_REQUEST['code'],
+ 'redirect_uri' => wp_login_url( $redirect_to ),
+ 'code_verifier' => $state['code_verifier'],
),
$state['authorization_endpoint']
);
@@ -253,7 +302,7 @@ public function login_form_websignin() {
include plugin_dir_path( __DIR__ ) . 'templates/websignin-form.php';
}
if ( 'POST' === $_SERVER['REQUEST_METHOD'] ) {
- $redirect_to = array_key_exists( 'redirect_to', $_REQUEST ) ? $_REQUEST['redirect_to'] : null;
+ $redirect_to = array_key_exists( 'redirect_to', $_REQUEST ) ? $_REQUEST['redirect_to'] : '';
$redirect_to = rawurldecode( $redirect_to );
if ( array_key_exists( 'websignin_identifier', $_POST ) ) { // phpcs:ignore