Skip to content

Commit

Permalink
Merge pull request #257 from dshanske/websignin
Browse files Browse the repository at this point in the history
Update Web Sign In and PKCE
  • Loading branch information
dshanske authored Nov 30, 2023
2 parents 23f1aed + 3651623 commit 7ba4fd2
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 18 deletions.
2 changes: 1 addition & 1 deletion includes/class-indieauth-authorization-endpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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 );
Expand Down
75 changes: 62 additions & 13 deletions includes/class-web-signin.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,40 +37,79 @@ 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',
__( '<strong>ERROR</strong>: Could not discover endpoints', 'indieauth' ),
array(
'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 ) ) {
return new WP_OAuth_Response( 'server_error', __( 'Did Not Receive a Valid Authorization Endpoint', 'indieauth' ), 500 );
}

$defaults = array(
'client_id' => home_url(),
'client_id' => home_url(),
'grant_type' => 'authorization_code',
);

$post_args = wp_parse_args( $post_args, $defaults );
Expand Down Expand Up @@ -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']
);
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions templates/indieauth-authenticate-form.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
<input type="hidden" name="me" value="<?php echo esc_url( $me ); ?>" />
<input type="hidden" name="response_type" value="<?php echo esc_attr( $response_type ); ?>" />
<input type="hidden" name="state" value="<?php echo esc_attr( $state ); ?>" />
<input type="hidden" name="code_challenge" value="<?php echo esc_attr( $code_challenge ); ?>" />
<input type="hidden" name="code_challenge_method" value="<?php echo esc_attr( $code_challenge_method ); ?>" />

<button name="wp-submit" value="authorize" class="button button-primary button-large"><?php esc_html_e( 'Allow', 'indieauth' ); ?></button>
<a name="wp-submit" value="cancel" class="button button-large" href="<?php echo esc_url( home_url() ); ?>"><?php esc_html_e( 'Cancel', 'indieauth' ); ?></a>
</p>
Expand Down
2 changes: 2 additions & 0 deletions templates/indieauth-authorize-form.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
<input type="hidden" name="state" value="<?php echo esc_attr( $state ); ?>" />
<input type="hidden" name="me" value="<?php echo esc_url( $me ); ?>" />
<input type="hidden" name="response_type" value="<?php echo esc_attr( $response_type ); ?>" />
<input type="hidden" name="code_challenge" value="<?php echo esc_attr( $code_challenge ); ?>" />
<input type="hidden" name="code_challenge_method" value="<?php echo esc_attr( $code_challenge_method ); ?>" />

<?php if ( ! is_null( $code_challenge ) ) { ?>
<input type="hidden" name="code_challenge" value="<?php echo esc_attr( $code_challenge ); ?>" />
Expand Down
8 changes: 4 additions & 4 deletions templates/indieauth-notices.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
</p>
<?php
}
if ( ! is_null( $code_challenge ) && 'S256' === $code_challenge_method ) {
if ( is_null( $code_challenge ) && 'S256' !== $code_challenge_method ) {
?>
<p class="pkce">
<strong> 🔒
<p class="pkce notice notice-error">
<strong> 🛡️
<?php
echo wp_kses(
/* translators: PKCE specification link */
sprintf( __( 'This app is using %s for security.', 'indieauth' ), '<a href="https://indieweb.org/PKCE">PKCE</a>' ),
sprintf( __( 'This app is not using %s for security which is now required for IndieAuth', 'indieauth' ), '<a href="https://indieweb.org/PKCE">PKCE</a>' ),
array(
'a' => array(
'href' => array(),
Expand Down

0 comments on commit 7ba4fd2

Please sign in to comment.