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

Fix Implicit login handling #426

Merged
merged 3 commits into from
Apr 5, 2018
Merged

Fix Implicit login handling #426

merged 3 commits into from
Apr 5, 2018

Conversation

joshcanhelp
Copy link
Contributor

  • Fixed error handling for implicit login
  • Moved implicit handling JS to own file; refactored
  • Added nonce handling for auto-login
  • Added state handling for auto-login
  • Better nonce generator if < PHP 7 (openssl_random_pseudo_bytes())

@joshcanhelp joshcanhelp added this to the v3-Next milestone Apr 3, 2018
@joshcanhelp joshcanhelp self-assigned this Apr 3, 2018
Copy link
Member

@cocojoe cocojoe left a comment

Choose a reason for hiding this comment

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

LGTM, presume still going through your extensive manual testing?

@joshcanhelp
Copy link
Contributor Author

@cocojoe - as usual 😄 working on a Docker setup now to make life a little easier.

WP_Auth0.php Outdated
}

public function render_form( $html ) {
if ( isset( $_GET['action'] ) && $_GET['action'] == 'lostpassword' ) {
if ( ( isset( $_GET['action'] ) && $_GET['action'] == 'lostpassword' ) || ! empty( $_GET[ 'auth0' ] ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

what does this change represent?

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 the auth0 URL param is set, don't load the form. It's only sent when coming back from an implicit grant. Auth code grant goes straight to index.php?auth0=1

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 I load the form, I get a new state set so I don't want to do that ... also, no need to load the form at all since we're just passing through.

Copy link
Contributor

Choose a reason for hiding this comment

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

Alright, I remember the auth0 param now. Are you planning to refactor this "auth0 hack" anytime soon?

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 param can show up on the login page and on the index.php entry point.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not loading the form in this case does solve the problem of re-setting a state cookie but we don't need to load the login form at all if this is a response from Auth0. So, I would say that the URL parameter check will stay but state checking will evolve to handle this better (by abstracting that WP_Auth0_Nonce_Handler class to handle both scenarios)

@@ -0,0 +1,35 @@
document.addEventListener( 'DOMContentLoaded', function() {
if ( ! window.location.hash || window.location.hash.indexOf('id_token') < 0 ) {
return;
Copy link
Contributor

Choose a reason for hiding this comment

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

If the token is missing you return. Does that mean that you are ignoring a possible auth error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question and I don't know if I have a good answer. The core login page processing does not use anchors at all and I don't want this to run unless I have something to do here. I'm not sure of another situation where we would have a hash and want to do something with it. I don't see anything else in the implicit grant doc.

Is there an error message that I might get back? error or 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.

Yeah. You might receive error and error_description in the hash/query params. There are a few knowns errors, I'll share privately a reference.

return;
}

var hash = window.location.hash;
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't know how much resources you are saving, but this line could be the first one and hash used on the if

}

var hash = window.location.hash;
if (hash[0] === '#') {
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 if hash=""?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

screenshot 2018-04-04 11 57 59

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But it can't be empty because we've escaped already if that's the case

if ( ! empty( $state_decoded->interim ) ) {
include WPA0_PLUGIN_DIR . 'templates/login-interim.php';
} else {
$redirect_to = ! empty( $state_decoded->redirect_to ) ? $state_decoded->redirect_to : null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this the same, or you need an explicit null later?

$redirect_to = $state_decoded->redirect_to

Copy link
Contributor Author

Choose a reason for hiding this comment

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

$state_decoded->redirect_to will throw an error if it does not exist, so I'm checking whether it's there or not with empty()

@@ -551,7 +561,7 @@ protected function get_state( $decoded = FALSE ) {

if ( empty( $this->state ) ) {
// Get and store base64 encoded state
$state_val = $this->query_vars( 'state' );
$state_val = $_REQUEST[ 'state' ];
Copy link
Contributor

Choose a reason for hiding this comment

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

Why, did the previous stop working?

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() is not explicit enough, makes for confusion around how these vars are handled. I want state from either POST or GET (aka $_REQUEST), I don't want anything else.

public function generateNonce() {
return function_exists( 'random_bytes' ) ? bin2hex( random_bytes( 32 ) ) : md5( uniqid( rand(), true ) );
public function generateNonce( $bytes = 32 ) {
$nonce_bytes = function_exists( 'random_bytes' ) ? random_bytes( $bytes ) : openssl_random_pseudo_bytes( $bytes );
Copy link
Contributor

Choose a reason for hiding this comment

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

will openssl_random_pseudo_bytes always exist?

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 plugin is tagged PHP 5.3, which is when this function was introduced:

https://secure.php.net/manual/en/function.openssl-random-pseudo-bytes.php

@@ -123,6 +123,10 @@ public function init() {

$this->check_signup_status();

if ( $this->a0_options->get( 'auto_login' ) ) {
WP_Auth0_Nonce_Handler::getInstance()->setCookie();
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this well indented?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Like, correct whitespace? Yes. This project mixes tabs and spaces, as well as how many spaces for each. I'll do an automated whitespace cleanup before I start the next release.

form.appendChild( createHiddenField( 'token', data.id_token ) );
form.appendChild( createHiddenField( 'state', data.state ) );
} else {
redirectWithoutQuery();
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this a valid path? When will it happen? Do you have an example? If you don't have one but still want to catch it, would be nice to log that query somewhere to debug and understand it I guess. If the authentication was initiated for "implicit flow" and the token is not there, there must be an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's valid as in it exists but without a hash we don't have much to do. The param triggers this JS to load and for the form to be excluded (param tells us that we need to process something from Auth0). If we don't have the information we need, then we need to cease processing, hence the redirect.

If we have the Auth0 param but no error or no state, that could be an intermittent problem, in which case showing the login form to try again seems reasonable. I'm not sure I want to catch and log every possible outcome here, I think it's best to get someone back to a place where they can try again.

@@ -150,9 +150,10 @@ public function init_auth0() {

// Catch any incoming errors and stop the login process
// See https://auth0.com/docs/libraries/error-messages
if ( $this->query_vars( 'error_description' ) ) {
if ( $this->query_vars( 'error' ) || $this->query_vars( '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.

I think the error_description might be missing sometimes (AFAIK is optional). In any case, because error is always present that condition should suffice.

Move the JS that POSTs auth data back to the WP site to it's own file. Better enqueuing for login JS and CSS.
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.

Aaaand done!

@joshcanhelp joshcanhelp merged commit d7ad3f3 into dev Apr 5, 2018
@joshcanhelp joshcanhelp deleted the fix-implicit-login branch April 5, 2018 19:06
@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

3 participants