Skip to content

Commit

Permalink
New class for state handling; set cookie for implicit nonce
Browse files Browse the repository at this point in the history
  • Loading branch information
joshcanhelp committed May 7, 2018
1 parent 8b6b3d1 commit 0f63a3f
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 198 deletions.
5 changes: 0 additions & 5 deletions WP_Auth0.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

define( 'WPA0_AUTH0_LOGIN_FORM_ID', 'auth0-login-form' );
define( 'WPA0_CACHE_GROUP', 'wp_auth0' );
define( 'WPA0_STATE_COOKIE_NAME', 'auth0_state' );
define( 'WPA0_JWKS_CACHE_TRANSIENT_NAME', 'WP_Auth0_JWKS_cache' );

define( 'WPA0_LANG', 'wp-auth0' ); // deprecated; do not use for translations
Expand Down Expand Up @@ -127,10 +126,6 @@ public function init() {

$this->check_signup_status();

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

WP_Auth0_Email_Verification::init();
}

Expand Down
4 changes: 4 additions & 0 deletions assets/js/lock-init.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ jQuery(document).ready(function ($) {
// Set state cookie to verify during callback
Cookies.set( opts.stateCookieName, opts.settings.auth.params.state );

if ( opts.settings.auth.params.nonce ) {
Cookies.set( opts.nonceCookieName, opts.settings.auth.params.nonce );
}

// Set Lock to standard or Passwordless
var Lock = opts.usePasswordless
? new Auth0LockPasswordless( opts.clientId, opts.domain, opts.settings )
Expand Down
6 changes: 3 additions & 3 deletions lib/WP_Auth0_Lock10_Options.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public function get_state_obj( $redirect_to = null ) {

$stateObj = array(
'interim' => ( isset( $_GET['interim-login'] ) && $_GET['interim-login'] == 1 ),
'nonce' => WP_Auth0_Nonce_Handler::getInstance()->get()
'nonce' => WP_Auth0_State_Handler::getInstance()->getUniqid()
);

if ( !empty( $redirect_to ) ) {
Expand Down Expand Up @@ -154,7 +154,7 @@ public function get_sso_options() {

unset( $options["authParams"] );
$options["state"] = $this->get_state_obj( $redirect_to );
$options["nonce"] = WP_Auth0_Nonce_Handler::getInstance()->get();
$options["nonce"] = WP_Auth0_Nonce_Handler::getInstance()->getUniqid();

return $options;
}
Expand Down Expand Up @@ -182,7 +182,7 @@ public function get_lock_options() {
$extraOptions["auth"]["responseType"] = 'id_token';
$extraOptions["auth"]["redirectUrl"] = $this->get_implicit_callback_url();
$extraOptions["autoParseHash"] = false;
$extraOptions["auth"]["params"]["nonce"] = WP_Auth0_Nonce_Handler::getInstance()->get();
$extraOptions["auth"]["params"]["nonce"] = WP_Auth0_Nonce_Handler::getInstance()->getUniqid();
} else {
$extraOptions["auth"]["responseType"] = 'code';
$extraOptions["auth"]["redirectUrl"] = $this->get_code_callback_url();
Expand Down
2 changes: 1 addition & 1 deletion lib/WP_Auth0_Lock_Options.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public function modal_button_name() {
public function get_state_obj( $redirect_to = null ) {
$stateObj = array(
'interim' => ( isset( $_GET['interim-login'] ) && $_GET['interim-login'] == 1 ),
'nonce' => WP_Auth0_Nonce_Handler::getInstance()->get()
'nonce' => WP_Auth0_Nonce_Handler::getInstance()->getUniqid()
);
if ( !empty( $redirect_to ) ) {
$stateObj["redirect_to"] = addslashes( $redirect_to );
Expand Down
73 changes: 17 additions & 56 deletions lib/WP_Auth0_LoginManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,6 @@ class WP_Auth0_LoginManager {
*/
protected $users_repo;

/**
* State value returned from successful Auth0 login.
*
* @var string
*
* @see WP_Auth0_Lock10_Options::get_state_obj()
*/
protected $state;

/**
* Decoded version of $this>state.
*
* @var object
*/
protected $state_decoded;

/**
* WP_Auth0_LoginManager constructor.
*
Expand Down Expand Up @@ -127,7 +111,12 @@ public function login_auto() {
$auth_url = 'https://' . $this->a0_options->get( 'domain' ) . '/authorize';
$auth_url = add_query_arg( array_map( 'rawurlencode', $auth_params ), $auth_url );

setcookie( WPA0_STATE_COOKIE_NAME, $auth_params['state'], time() + WP_Auth0_Nonce_Handler::COOKIE_EXPIRES, '/' );
WP_Auth0_State_Handler::getInstance()->setStateCookie( $auth_params['state'] );

if ( isset( $auth_params['nonce'] ) ) {
WP_Auth0_Nonce_Handler::getInstance()->setCookie();
}

wp_redirect( $auth_url );
exit;
}
Expand All @@ -154,7 +143,7 @@ public function init_auth0() {

// Check for valid state nonce, set in WP_Auth0_Lock10_Options::get_state_obj().
// See https://auth0.com/docs/protocols/oauth2/oauth-state for more info.
if ( ! $this->validate_state() ) {
if ( ! WP_Auth0_State_Handler::getInstance()->validate() ) {
$this->die_on_login( __( 'Invalid state', 'wp-auth0' ) );
}

Expand Down Expand Up @@ -268,7 +257,7 @@ public function redirect_login() {
$userinfo = json_decode( $userinfo_resp_body );

if ( $this->login_user( $userinfo, $data->id_token, $data->access_token ) ) {
$state_decoded = $this->get_state( true );
$state_decoded = $this->get_state();
if ( ! empty( $state_decoded->interim ) ) {
include WPA0_PLUGIN_DIR . 'templates/login-interim.php';
} else {
Expand Down Expand Up @@ -336,7 +325,7 @@ public function implicit_login() {
if ( $this->login_user( $decoded_token, $token, null ) ) {

// Validated above in $this->init_auth0().
$state_decoded = $this->get_state( true );
$state_decoded = $this->get_state();

if ( ! empty( $state_decoded->interim ) ) {
include WPA0_PLUGIN_DIR . 'templates/login-interim.php';
Expand Down Expand Up @@ -577,8 +566,8 @@ public function auth0_singlelogout_footer() {

/**
* End the PHP session.
*
* TODO: Deprecate
*
* TODO: Deprecate
*/
public function end_session() {
if ( session_id() ) {
Expand Down Expand Up @@ -612,7 +601,7 @@ public static function get_authorize_params( $connection = null, $redirect_to =
$options = WP_Auth0_Options::Instance();
$lock_options = new WP_Auth0_Lock10_Options();
$is_implicit = (bool) $options->get( 'auth0_implicit_workflow', FALSE );
$nonce = WP_Auth0_Nonce_Handler::getInstance()->get();
$nonce = WP_Auth0_Nonce_Handler::getInstance()->getUniqid();

$params[ 'client_id' ] = $options->get( 'client_id' );
$params[ 'scope' ] = self::get_userinfo_scope( 'authorize_url' );
Expand Down Expand Up @@ -671,41 +660,13 @@ protected function query_vars( $key ) {
/**
* Get the state value returned from Auth0 during login processing.
*
* @param bool $decoded - pass `true` to return decoded state, leave blank for raw string.
*
* @return string|object|null
*/
protected function get_state( $decoded = false ) {

if ( empty( $this->state ) ) {
// Get and store base64 encoded state.
$state_val = isset( $_REQUEST['state'] ) ? $_REQUEST['state'] : '';
$state_val = urldecode( $state_val );
$this->state = $state_val;

// Decode and store the state.
$state_val = base64_decode( $state_val );
$this->state_decoded = json_decode( $state_val );
}

if ( $decoded ) {
return is_object( $this->state_decoded ) ? $this->state_decoded : null;
} else {
return $this->state;
}
}

/**
* Check the state send back from Auth0 with the one stored in the user's browser.
*
* @return bool
*/
protected function validate_state() {
$valid = isset( $_COOKIE[ WPA0_STATE_COOKIE_NAME ] )
? $_COOKIE[ WPA0_STATE_COOKIE_NAME ] === $this->get_state()
: false;
setcookie( WPA0_STATE_COOKIE_NAME, '', 0, '/' );
return $valid;
protected function get_state() {
$state_val = $this->query_vars( 'state' );
$state_val = base64_decode( $state_val );
$state_val = json_decode( $state_val );
return $state_val;
}

/**
Expand Down
173 changes: 41 additions & 132 deletions lib/WP_Auth0_Nonce_Handler.php
Original file line number Diff line number Diff line change
@@ -1,134 +1,43 @@
<?php

final class WP_Auth0_Nonce_Handler {

/**
* Cookie name used to store nonce
*
* @var string
*/
const COOKIE_NAME = 'auth0_nonce';

/**
*
*/
const COOKIE_EXPIRES = HOUR_IN_SECONDS;

/**
* Singleton class instance
*
* @var WP_Auth0_Nonce_Handler|null
*/
private static $_instance = null;

/**
* Nonce stored in a cookie
*
* @var string
*/
private $_uniqid;

/**
* WP_Auth0_Nonce_Handler constructor
* Private to prevent new instances of this class
*/
private function __construct() {
$this->init();
}

/**
* Private to prevent cloning
*/
private function __clone() {}

/**
* Private to prevent serializing
*/
private function __sleep() {}

/**
* Private to prevent unserializing
*/
private function __wakeup() {}

/**
* Start-up process to make sure we have a nonce stored
*/
private function init() {
if ( isset( $_COOKIE[ self::COOKIE_NAME ] ) ) {
// Have a nonce cookie, don't want to generate a new one
$this->_uniqid = $_COOKIE[ self::COOKIE_NAME ];
} else {
// No nonce cookie, need to create one
$this->_uniqid = $this->generateNonce();
}
}

/**
* Get the internal instance of the singleton
*
* @return WP_Auth0_Nonce_Handler
*/
public static final function getInstance() {
if ( null === self::$_instance ) {
self::$_instance = new WP_Auth0_Nonce_Handler();
}
return self::$_instance;
}

/**
* Return the unique ID used for nonce validation
*
* @return string
*/
public function get() {
return $this->_uniqid;
}

/**
* Check if the stored nonce matches a specific value
*
* @param string $nonce - the nonce to validate against the stored value
*
* @return bool
*/
public function validate( $nonce ) {
$valid = isset( $_COOKIE[ self::COOKIE_NAME ] ) ? $_COOKIE[ self::COOKIE_NAME ] === $nonce : FALSE;
$this->reset();
return $valid;
}

/**
* Set the nonce cookie value
*
* @return bool
*/
public function setCookie() {
$_COOKIE[ self::COOKIE_NAME ] = $this->_uniqid;
return setcookie( self::COOKIE_NAME, $this->_uniqid, time() + self::COOKIE_EXPIRES, '/' );
}

/**
* Reset the nonce cookie value
*
* @return bool
*/
public function reset() {
return setcookie( self::COOKIE_NAME, '', 0 );
}

/**
* Generate a random ID
* If using on PHP 7, it will be cryptographically secure
*
* @see https://secure.php.net/manual/en/function.random-bytes.php
*
* @param int $bytes - number of bytes to generate
*
* @return string
*/
public function generateNonce( $bytes = 32 ) {
$nonce_bytes = function_exists( 'random_bytes' ) ? random_bytes( $bytes ) : openssl_random_pseudo_bytes( $bytes );
return bin2hex( $nonce_bytes );
}
}
final class WP_Auth0_Nonce_Handler extends WP_Auth0_Random_Storage {

/**
* Cookie name used to store nonce
*
* @var string
*/
const UNIQID_COOKIE_NAME = 'auth0_nonce_uniqid';

/**
* Singleton class instance
*
* @var WP_Auth0_Nonce_Handler|null
*/
private static $_instance = null;

/**
* Get the internal instance of the singleton
*
* @return WP_Auth0_Nonce_Handler
*/
public static final function getInstance() {
if ( null === static::$_instance ) {
static::$_instance = new static;
}
return static::$_instance;
}

/**
* Check if the stored nonce matches a specific value
*
* @param string $nonce - the nonce to validate against the stored value
*
* @return bool
*/
public function validate( $nonce ) {
$valid = isset( $_COOKIE[ self::UNIQID_COOKIE_NAME ] ) ? $_COOKIE[ self::UNIQID_COOKIE_NAME ] === $nonce : FALSE;
$this->reset();
return $valid;
}
}
Loading

0 comments on commit 0f63a3f

Please sign in to comment.