Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 87 additions & 43 deletions includes/openid-connect-generic-client-wrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@ static public function register( OpenID_Connect_Generic_Client $client, OpenID_C
if ( is_user_logged_in() ) {
add_action( 'wp_loaded', array($client_wrapper, 'ensure_tokens_still_fresh'));
}


if ( $settings->authenticate_filter ) {
add_filter('authenticate', array( $client_wrapper, 'authenticate_filter' ), 15, 3);
}

return $client_wrapper;
}

Expand Down Expand Up @@ -273,40 +277,70 @@ function alter_request( $request, $op ) {
*/
function authentication_request_callback() {
$client = $this->client;

// start the authentication flow
$authentication_request = $client->validate_authentication_request( $_GET );

if ( is_wp_error( $authentication_request ) ){
$this->error_redirect( $authentication_request );
}

// retrieve the authentication code from the authentication request
$code = $client->get_authentication_code( $authentication_request );

if ( is_wp_error( $code ) ){
$this->error_redirect( $code );
}

// attempting to exchange an authorization code for an authentication token
$token_result = $client->request_authentication_token( $code );

if ( is_wp_error( $token_result ) ) {
$this->error_redirect( $token_result );
}

// get the decoded response from the authentication request result
$token_response = $client->get_token_response( $token_result );

if ( is_wp_error( $token_response ) ){
$this->error_redirect( $token_response );
$user = $this->validate($token_response);

if ( is_wp_error( $user ) ) {
$this->error_redirect( $user );
}

// redirect back to the origin page if enabled
$redirect_url = isset( $_COOKIE[ $this->cookie_redirect_key ] ) ? esc_url( $_COOKIE[ $this->cookie_redirect_key ] ) : false;

if( $this->settings->redirect_user_back && !empty( $redirect_url ) ) {
do_action( 'openid-connect-generic-redirect-user-back', $redirect_url, $user );
setcookie( $this->cookie_redirect_key, $redirect_url, 1, COOKIEPATH, COOKIE_DOMAIN, is_ssl() );
wp_redirect( $redirect_url );
}
// otherwise, go home!
else {
wp_redirect( home_url() );
}

exit;
}

/**
* @param $token_response
*
* @return WP_Error|WP_User
*/
function validate($token_response) {
$client = $this->client;

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

// ensure the that response contains required information
$valid = $client->validate_token_response( $token_response );

if ( is_wp_error( $valid ) ) {
$this->error_redirect( $valid );
return $valid;
}

/**
Expand All @@ -318,34 +352,34 @@ function authentication_request_callback() {
// The access_token must be used to prove access rights to protected resources
// e.g. for the userinfo endpoint
$id_token_claim = $client->get_id_token_claim( $token_response );
if ( is_wp_error( $id_token_claim ) ){
$this->error_redirect( $id_token_claim );

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

// validate our id_token has required values
$valid = $client->validate_id_token_claim( $id_token_claim );
if ( is_wp_error( $valid ) ){
$this->error_redirect( $valid );

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

// if userinfo endpoint is set, exchange the token_response for a user_claim
if ( !empty( $this->settings->endpoint_userinfo ) && isset( $token_response['access_token'] )) {
if ( ! empty( $this->settings->endpoint_userinfo ) && isset( $token_response['access_token'] ) ) {
$user_claim = $client->get_user_claim( $token_response );
} else {
$user_claim = $id_token_claim;
}
if ( is_wp_error( $user_claim ) ){
$this->error_redirect( $user_claim );

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

// validate our user_claim has required values
$valid = $client->validate_user_claim( $user_claim, $id_token_claim );
if ( is_wp_error( $valid ) ){
$this->error_redirect( $valid );

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

/**
Expand All @@ -354,14 +388,14 @@ function authentication_request_callback() {
* Request is authenticated and authorized - start user handling
*/
$subject_identity = $client->get_subject_identity( $id_token_claim );

$user = $this->get_user_by_identity( $subject_identity );

// if we didn't find an existing user, we'll need to create it
if ( ! $user ) {
$user = $this->create_new_user( $subject_identity, $user_claim );
if ( is_wp_error( $user ) ) {
$this->error_redirect( $user );
return;
return $user;
}
}
else {
Expand All @@ -371,33 +405,43 @@ function authentication_request_callback() {

// validate the found / created user
$valid = $this->validate_user( $user );
if ( is_wp_error( $valid ) ){
$this->error_redirect( $valid );

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

// login the found / created user
$this->login_user( $user, $token_response, $id_token_claim, $user_claim, $subject_identity );
$this->login_user( $user, $token_response, $id_token_claim, $user_claim, $subject_identity );

do_action( 'openid-connect-generic-user-logged-in', $user );

// log our success
$this->logger->log( "Successful login for: {$user->user_login} ({$user->ID})", 'login-success' );

// redirect back to the origin page if enabled
$redirect_url = isset( $_COOKIE[ $this->cookie_redirect_key ] ) ? esc_url( $_COOKIE[ $this->cookie_redirect_key ] ) : false;
return $user;
}

if( $this->settings->redirect_user_back && !empty( $redirect_url ) ) {
do_action( 'openid-connect-generic-redirect-user-back', $redirect_url, $user );
setcookie( $this->cookie_redirect_key, $redirect_url, 1, COOKIEPATH, COOKIE_DOMAIN, is_ssl() );
wp_redirect( $redirect_url );
function authenticate_filter($user, $username, $password) {
if ( $user instanceof WP_User ) {
return $user;
}
// otherwise, go home!
else {
wp_redirect( home_url() );

if ( is_null($user) || is_wp_error($user) && $user->get_error_code() === 'invalid_username' ) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need a little clarity on this check. Do we want to test for the exact message 'invalid_username' here?

Implication being that we only send a remote authentication request if the provided username doesn't exist in WP?

Copy link
Contributor Author

@gassan gassan Jul 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right.
In current implementation (priority 15) makes this check no sense. Its make sense only if this filter runs after the default wordpress authenticator (priority 20). the default returns this WP_Error if a user not found in [prefix_]users table.
Q: What would be better here?

  1. to modify this code and remove this check.
  2. to allow administrator to set this priority on settings page? (default 15)

if (! empty($username) && ! empty($password)) {
$token_result = $this->client->request_authentication_token_by_username_and_password($username, $password);

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

// get the decoded response from the authentication request result
$token_response = $this->client->get_token_response( $token_result );

$user = $this->validate($token_response);
}
}
exit;

return $user;
}

/**
Expand Down
36 changes: 34 additions & 2 deletions includes/openid-connect-generic-client.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,38 @@ function request_authentication_token( $code ) {
return $response;
}

/**
* Using the refresh token, request new tokens from the idp
*
* @param $username
* @param $password
*
* @return array|\WP_Error
*/
function request_authentication_token_by_username_and_password($username, $password) {
$request = array(
'body' => array(
'grant_type' => 'password',
'client_id' => $this->client_id,
'client_secret' => $this->client_secret,
'username' => $username,
'password' => $password,
'scope' => $this->scope,
),
);

// allow modifications to the request
$request = apply_filters('openid-connect-generic-alter-request', $request, 'get-authentication-token-by-username-and-password');

$response = wp_remote_post( $this->endpoint_token, $request );

if ( is_wp_error( $response ) ) {
$response->add( 'request_authentication_token' , __( 'Request for authentication token failed.' ) );
}

return $response;
}

/**
* Using the refresh token, request new tokens from the idp
*
Expand Down Expand Up @@ -331,7 +363,7 @@ function get_id_token_claim( $token_response ){
)
, true
);

return $id_token_claim;
}

Expand Down Expand Up @@ -383,7 +415,7 @@ function get_user_claim( $token_response ){
* @param $user_claim
* @param $id_token_claim
*
* @return \WP_Error
* @return true|WP_Error
*/
function validate_user_claim( $user_claim, $id_token_claim ) {
// must be an array
Expand Down
26 changes: 26 additions & 0 deletions includes/openid-connect-generic-option-settings.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
<?php
/**
* Class OpenId_Connect_Generic_Option_Settings
*
* @property $login_type
* @property $client_id
* @property $client_secret
* @property $scope
* @property $endpoint_login
* @property $endpoint_userinfo
* @property $endpoint_token
* @property $endpoint_end_session
* @property $identity_key
* @property $no_sslverify
* @property $http_request_timeout
* @property $authenticate_filter
* @property $enforce_privacy
* @property $alternate_redirect_uri
* @property $nickname_key
* @property $email_format
* @property $displayname_format
* @property $identify_with_username
* @property $state_time_limit
* @property $link_existing_users
* @property $redirect_user_back
* @property $redirect_on_logout
* @property $enable_logging
* @property $log_limit
*
*/
class OpenID_Connect_Generic_Option_Settings {

Expand Down
6 changes: 6 additions & 0 deletions includes/openid-connect-generic-settings-page.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ function __construct( OpenID_Connect_Generic_Option_Settings $settings, OpenID_C
'type' => 'text',
'section' => 'client_settings',
),
'authenticate_filter' => array(
'title' => __( 'Register authenticate filter' ),
'description' => __( 'Enables login by entering username and password on wordpress site without forwarding end user to OpenId server if allowed (grant_type: password).' ),
'type' => 'checkbox',
'section' => 'authorization_settings',
),
'enforce_privacy' => array(
'title' => __( 'Enforce Privacy' ),
'description' => __( 'Require users be logged in to see the site.' ),
Expand Down
Loading