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

Change auto-login action #449

Merged
merged 2 commits into from
May 2, 2018
Merged

Change auto-login action #449

merged 2 commits into from
May 2, 2018

Conversation

joshcanhelp
Copy link
Contributor

  • Change auto-login logic to be more clear and explicit
  • Change implicit to handle nonce correctly
  • Add authorize URL param builder
  • Fix callback error handling for implicit and redirect

@joshcanhelp joshcanhelp added this to the v3-Next milestone Apr 24, 2018
@@ -156,8 +147,8 @@ public function init_auth0() {
// Catch any incoming errors and stop the login process.
// See https://auth0.com/docs/libraries/error-messages for more info.
if ( $this->query_vars( 'error' ) || $this->query_vars( 'error_description' ) ) {
$error_msg = sanitize_text_field( $this->query_vars( 'error_description' ) );
$error_code = sanitize_text_field( $this->query_vars( 'error' ) );
$error_msg = sanitize_text_field( rawurldecode( $_REQUEST[ 'error_description' ] ) );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

$this->query_vars() checks for WP query vars first, which was leaving out the error parameter for some reason. Switched to a more explicit check and now pulls error more consistently.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why does this call rawurldecode first when the line below doesn't?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My thought was that the error code is usually a machine name without any characters that need decoding but that's a little bit of wishful thinking ... and no harm decoding that as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

Honestly, the errors returned by the server should be used directly. No need to parse nor decode them at all (besides JSON if you need to translate them to an Object structure). Some error descriptions might include URLs to help articles. I wouldn't do any urldecoding here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Error descriptions typically include at least spaces, if not other characters. Without urldecoding it, you'll get "This%20is%20an%20error" and such. URLs in a URL definitely need to be encoded.

I've never seen an error code have decode-able characters, though, which was why I left it out.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah yes, you're right! I thought you were obtaining this from a JSON body, not from the query. Now that I read, the param says "query_vars".

__( 'Invalid nonce', 'wp-auth0' )
);
}
$nonce = isset( $decoded_token->nonce ) ? $decoded_token->nonce : null;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nonce now works for any implicit login 🎉

*
* @return string
*/
public static function get_userinfo_scope( $context = '' ) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved default scopes to here so they can be filtered

Copy link
Contributor Author

@joshcanhelp joshcanhelp Apr 24, 2018

Choose a reason for hiding this comment

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

Also $context is only for that filter. I pass in a context when I request those scopes so the filter function can decide to do something different based on where it's being used.

Copy link
Contributor

Choose a reason for hiding this comment

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

how does this method work? I saw you call it passing sso and lock. The method should have a single known behavior. What's the input/output?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On a basic install with no customizations, 'openid email name nickname picture' will always be returned. If someone wants to modify the scopes that are being requested, they can use the auth0_auth_token_scope filter to add/remove. The $context is there to provide ... context for where those scopes are used. This is a pattern used in i18n in WordPress where a word might be different depending on context.

Adjusted this to use an array for filtering ... better data structure for what's being done here.

@@ -12,7 +12,7 @@
/**
*
*/
const COOKIE_EXPIRES = MINUTE_IN_SECONDS;
const COOKIE_EXPIRES = HOUR_IN_SECONDS;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Longer state cookie, was failing if you left the login page open for more than a minute.

@@ -156,8 +147,8 @@ public function init_auth0() {
// Catch any incoming errors and stop the login process.
// See https://auth0.com/docs/libraries/error-messages for more info.
if ( $this->query_vars( 'error' ) || $this->query_vars( 'error_description' ) ) {
$error_msg = sanitize_text_field( $this->query_vars( 'error_description' ) );
$error_code = sanitize_text_field( $this->query_vars( 'error' ) );
$error_msg = sanitize_text_field( rawurldecode( $_REQUEST[ 'error_description' ] ) );
Copy link
Contributor

Choose a reason for hiding this comment

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

Why does this call rawurldecode first when the line below doesn't?

);
}
$nonce = isset( $decoded_token->nonce ) ? $decoded_token->nonce : null;
if ( ! WP_Auth0_Nonce_Handler::getInstance()->validate( $nonce ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Will this work fine (fail) if the nonce was not found (null) in the id token and the flow was implicit?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. Regardless of whether we have a value or not, that validate() method checks against what's stored. Since null is not equal to whatever string we stored, this will return false.

*
* @return string
*/
public static function get_userinfo_scope( $context = '' ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

how does this method work? I saw you call it passing sso and lock. The method should have a single known behavior. What's the input/output?

}

/**
* Get a base authorize URL for handling HLP redirects.
Copy link
Contributor

Choose a reason for hiding this comment

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

HLP -> Universal Login Page

/**
* Get a base authorize URL for handling HLP redirects.
*
* @param null|string $connection - specific connection to use, pass null to use all enabled.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it reads better this way:

specific connection to use, pass null to use all the enabled connections.

$params[ 'response_type' ] = $is_implicit ? 'id_token': 'code';
$params[ 'redirect_uri' ] = $is_implicit
? $lock_options->get_implicit_callback_url()
: $options->get_wp_auth0_url( null, $is_implicit );
Copy link
Contributor

Choose a reason for hiding this comment

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

why use $is_implicit if you already know the value?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You know, when I create a variable, I don't want it to just do a job and go away ... I want that variable to experience it's short time in memory.

}

// Get the telemetry header.
$telemetry = WP_Auth0_Api_Client::get_info_headers();
Copy link
Contributor

Choose a reason for hiding this comment

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

this object generates headers with lots of information, right? why use only Auth0-Client? what about the rest?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you look at what WP_Auth0_Api_Client::get_info_headers() returns, you'll see. That's meant to built telemetry headers for API calls but the value of the array it returns can be used here. That was part of my comment about it being a good candidate for refactoring but beyond the scope of this release.

$params[ 'auth0Client' ] = $telemetry[ 'Auth0-Client' ];

// Where should the user be redirected after logging in?
if ( empty( $redirect_to ) && ! empty( $_GET['redirect_to'] ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Line 608 talks about a redirect_uri. What would be the difference with this redirect_to parameter, and shouldn't they be merged or at least be closer? (lines distance in this file)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The redirect_uri is the one that /authorize is looking for whereas redirect_to is where the plugin sends them after they authenticate (sent with state below).

$params[ 'state' ] = base64_encode( json_encode( array(
'interim' => false,
'nonce' => $nonce,
'redirect_to' => filter_var( $redirect_to, FILTER_SANITIZE_URL ),
Copy link
Contributor

Choose a reason for hiding this comment

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

why do you need to manipulate the redirect_to value? can't you use directly the one defined just a few lines above this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since this could come from a URL param or a settings field, I want to make sure that it's an actual URL.

Copy link
Contributor

@lbalmaceda lbalmaceda Apr 26, 2018

Choose a reason for hiding this comment

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

then why you don't do the same in the lines above?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you clarify what your question is here? Do what to which lines?

Copy link
Contributor

Choose a reason for hiding this comment

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

Line 628 or 630 define what $redirect_to is going to be. Then in this line (637) you set as the redirect_to property (inside the state object) filter_var( $redirect_to, FILTER_SANITIZE_URL ) as value. What's the difference and why do you need to call filter_var first this time?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Line 628 and 630 pull a raw value from the URL or from the database, could be anything. So if a redirect URL is not passed and there's one in the URL, get that. Otherwise if it's not passed but nothing in the URL, get the default. Then take that value, get rid of non-URL-y characters, and pack it up into state to be sent to Auth0 and returned. If that's null/empty in the callback then it will go to the homepage.

Hopefully that answers your question? If not, ask another way and I'll try again 😀

$site_url = site_url( 'index.php', $protocol );
return add_query_arg( 'auth0', '1', $site_url );
return add_query_arg( 'auth0', ( $implicit ? 'implicit' : '1' ), $site_url );
Copy link
Contributor

Choose a reason for hiding this comment

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

how are you handling this with a query param?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

- Change auto-login logic to be more clear and explicit
- Change implicit to handle nonce correctly
- Add authorize URL param builder
Copy link
Contributor

@lbalmaceda lbalmaceda left a comment

Choose a reason for hiding this comment

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

LGTM 🎉

@joshcanhelp joshcanhelp merged commit 10c2794 into dev May 2, 2018
@joshcanhelp joshcanhelp deleted the change-auto-login-refactor branch May 2, 2018 22:03
@joshcanhelp joshcanhelp mentioned this pull request Jun 5, 2018
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 19, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants