Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Validation Functions and Fix CSS #258

Merged
merged 10 commits into from
Dec 2, 2023
16 changes: 8 additions & 8 deletions includes/class-indieauth-authorization-endpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public function register_routes() {
),
// The Client URL.
'client_id' => array(
'validate_callback' => 'rest_is_valid_url',
'validate_callback' => 'indieauth_validate_client_identifier',
'sanitize_callback' => 'esc_url_raw',
'required' => true,
),
Expand Down Expand Up @@ -113,7 +113,7 @@ public function register_routes() {
/* The Profile URL the user entered. Optional.
*/
'me' => array(
'validate_callback' => 'rest_is_valid_url',
'validate_callback' => 'indieauth_validate_user_identifier',
'sanitize_callback' => 'esc_url_raw',
),
),
Expand All @@ -135,7 +135,7 @@ public function register_routes() {
/* The client's URL, which MUST match the client_id used in the authentication request.
*/
'client_id' => array(
'validate_callback' => 'rest_is_valid_url',
'validate_callback' => 'indieauth_validate_client_identifier',
'sanitize_callback' => 'esc_url_raw',
),
/* The client's redirect URL, which MUST match the initial authentication request.
Expand Down Expand Up @@ -365,11 +365,11 @@ 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();
$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();

if ( ! $token ) {
return new WP_OAuth_Response( 'invalid_grant', __( 'Invalid authorization code', 'indieauth' ), 400 );
Expand Down
23 changes: 22 additions & 1 deletion includes/class-indieauth-client-discovery.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ public function __construct( $client_id ) {
}

private function fetch( $url ) {

// Validate if this is an IP address
$ip = filter_var( wp_parse_url( $url, PHP_URL_HOST ), FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 );
$donotfetch = array(
'127.0.0.1',
'0000:0000:0000:0000:0000:0000:0000:0001',
'::1',
);

// If this is an IP address ion the donotfetch list then do not fetch.
if ( $ip && ! in_array( $ip, $donotfetch ) ) {
return new WP_Error( 'do_not_fetch', __( 'Client Identifier is localhost', 'indieauth' ) );
}

$wp_version = get_bloginfo( 'version' );
$user_agent = apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ) );
$args = array(
Expand All @@ -38,7 +52,14 @@ private function fetch( $url ) {
'redirection' => 3,
'user-agent' => "$user_agent; IndieAuth Client Information Discovery",
);
return wp_safe_remote_get( $url, $args );
$response = wp_safe_remote_get( $url, $args );
if ( ! is_wp_error( $response ) ) {
$code = wp_remote_retrieve_response_code( $response );
if ( ( $code / 100 ) !== 2 ) {
return new WP_Error( 'retrieval_error', __( 'Failed to Retrieve Client Details', 'indieauth' ), $code );
}
}
return $response;
}

private function parse( $url ) {
Expand Down
2 changes: 1 addition & 1 deletion includes/class-indieauth-token-endpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public function register_routes() {
/* The client's URL, which MUST match the client_id used in the authentication request.
*/
'client_id' => array(
'validate_callback' => 'rest_is_valid_url',
'validate_callback' => 'indieauth_validate_client_identifier',
'sanitize_callback' => 'esc_url_raw',
),
/* The client's redirect URL, which MUST match the initial authentication request.
Expand Down
123 changes: 27 additions & 96 deletions includes/class-web-signin.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,9 @@ public function __construct() {
add_action( 'init', array( $this, 'settings' ) );

add_action( 'login_form', array( $this, 'login_form' ) );
add_filter( 'login_form_defaults', array( $this, 'login_form_defaults' ), 10, 1 );
add_filter( 'gettext', array( $this, 'register_text' ), 10, 3 );
add_action( 'login_form_websignin', array( $this, 'login_form_websignin' ) );

add_action( 'authenticate', array( $this, 'authenticate' ), 20, 2 );
add_action( 'authenticate', array( $this, 'authenticate_url_password' ), 10, 3 );
}

public function settings() {
Expand All @@ -37,9 +34,27 @@ public function settings() {
* @param string $redirect_uri where to redirect
*/
public function websignin_redirect( $me, $redirect_uri ) {
$me = indieauth_validate_user_identifier( $me );
if ( ! $me ) {
return new WP_Error(
'authentication_failed',
__( '<strong>ERROR</strong>: Invalid URL', 'indieauth' ),
array(
'status' => 401,
)
);
}
$endpoints = find_rels( $me, array( 'indieauth-metadata', 'authorization_endpoint' ) );

if ( array_key_exists( 'indieauth-metadata', $endpoints ) ) {
if ( ! $endpoints ) {
return new WP_Error(
'authentication_failed',
__( '<strong>ERROR</strong>: Could not discover endpoints', 'indieauth' ),
array(
'status' => 401,
)
);
} elseif ( 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(
Expand Down Expand Up @@ -175,6 +190,9 @@ public function authenticate( $user, $url ) {
}
if ( array_key_exists( 'iss', $_REQUEST ) ) {
$iss = rawurldecode( $_REQUEST['iss'] );
if ( ! indieauth_validate_issuer_identifier( $iss ) ) {
return new WP_Error( 'indieauth_iss_error', __( 'Issuer Parameter is Not Valid', 'indieauth' ) );
}
if ( $iss !== $state['issuer'] ) {
return new WP_Error( 'indieauth_iss_error', __( 'Issuer Parameter does not Match Server Metadata', 'indieauth' ) );
}
Expand Down Expand Up @@ -208,73 +226,6 @@ public function authenticate( $user, $url ) {
}



/**
* Authenticate user to WordPress using URL and Password
*/
public function authenticate_url_password( $user, $url, $password ) {
if ( $user instanceof WP_User ) {
return $user;
}
if ( empty( $url ) || empty( $password ) ) {
if ( is_wp_error( $user ) ) {
return $user;
}
if ( is_oauth_error( $user ) ) {
return $user->to_wp_error();
}
$error = new WP_Error();

if ( empty( $url ) ) {
$error->add( 'empty_username', __( '<strong>ERROR</strong>: The URL field is empty.', 'indieauth' ) ); // Uses 'empty_username' for back-compat with wp_signon()
}

if ( empty( $password ) ) {
$error->add( 'empty_password', __( '<strong>ERROR</strong>: The password field is empty.', 'indieauth' ) );
}

return $error;
}

if ( ! wp_http_validate_url( $url ) ) {
return $user;
}
$user = get_user_by_identifier( $url );

if ( ! $user ) {
return new WP_Error(
'invalid_url',
__( '<strong>ERROR</strong>: Invalid URL.', 'indieauth' ) .
' <a href="' . wp_lostpassword_url() . '">' .
__( 'Lost your password?', 'indieauth' ) .
'</a>'
);
}

/** This filter is documented in wp-includes/user.php */
$user = apply_filters( 'wp_authenticate_user', $user, $password );

if ( is_wp_error( $user ) ) {
return $user;
}

if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
return new WP_Error(
'incorrect_password',
sprintf(
/* translators: %s: url */
__( '<strong>ERROR</strong>: The password you entered for the URL %s is incorrect.', 'indieauth' ),
'<strong>' . $url . '</strong>'
) .
' <a href="' . wp_lostpassword_url() . '">' .
__( 'Lost your password?', 'indieauth' ) .
'</a>'
);
}

return $user;
}

/**
* render the login form
*/
Expand All @@ -285,45 +236,25 @@ public function login_form() {
}
}

public function login_form_defaults( $defaults ) {
$defaults['label_username'] = __( 'Username, Email Address, or URL', 'indieauth' );
return $defaults;
}

public function register_text( $translated_text, $untranslated_text, $domain ) {
if ( 'Username or Email Address' === $untranslated_text ) {
$translated_text = __( 'Username, Email Address, or URL', 'indieauth' );
}
return $translated_text;
}

public function login_form_websignin() {
if ( 'GET' === $_SERVER['REQUEST_METHOD'] ) {
include plugin_dir_path( __DIR__ ) . 'templates/websignin-form.php';
}
$login_errors = null;
if ( 'POST' === $_SERVER['REQUEST_METHOD'] ) {
$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
$me = esc_url_raw( $_POST['websignin_identifier'] ); //phpcs:ignore
// Check for valid URLs
if ( ! wp_http_validate_url( $me ) ) {
return new WP_Error( 'websignin_invalid_url', __( 'Invalid User Profile URL', 'indieauth' ) );
}

$return = $this->websignin_redirect( $me, wp_login_url( $redirect_to ) );
if ( is_wp_error( $return ) ) {
echo '<div id="login_error">' . esc_html( $return->get_error_message() ) . "</div>\n";
return $return;
$login_errors = $return;
}
if ( is_oauth_error( $return ) ) {
$return = $return->to_wp_error();
echo '<div id="login_error">' . esc_html( $return->get_error_message() ) . "</div>\n";
return $return;
$login_errors = $return->to_wp_error();
}
}
}

include plugin_dir_path( __DIR__ ) . 'templates/websignin-form.php';
exit;
}
}
Expand Down
Loading