Bug: Possible Login then logout issue
In some cases I login to my faustjs app - then my app checks for an authentication token which fails and logs me out.
Applicable Versions
"@faustjs/core": "^0.15.6", "@faustjs/next": "^0.15.6", "@gqty/cli": "^3.1.0",
- WordPress version: 6.0
- FaustWP version: Version 0.7.9 Php 7.4 - on a Digital Ocean Droplet with Sailed.io setup.
Steps To Reproduce
It will take me awhile to strip out all the unused code / setup an additional domain ... If this is not enough information I can create it. I do not think this issue will occur on Atlas.
What I think is happening is I got three components on the page using this code.
<AuthContent>. # Ensure the user is logged in
<AfterLoginRedirect/> # Page redirected to after the login (there is some logic here depending on the user group, and if they completed the signup or not.
</AuthContent>
const { useAuth } = client.auth;
const { isLoading, isAuthenticated } = useAuth({
shouldRedirect: false,
})
I added some logging code on the public/wp-content/plugins/faustwp/includes/auth/functions.php file.
if (!function_exists('write_log')) {
function write_log($log) {
if (true === WP_DEBUG) {
if (is_array($log) || is_object($log)) {
error_log(print_r($log, true));
} else {
error_log($log);
}
}
}
}
/**
* Get a WP_User given a base 64 encoded code.
*
* @param string $code The base64 encoded encrypted code.
* @param string $type The type of code. Either 'ac' or 'at'.
*
* @return WP_User|bool A WP_User object or false.
*/
function get_user_from_code( $code, $type ) {
write_log("Function get_user_from_code - code: $code type: $type ");
$code = decrypt( $code );
write_log("After decrypt ! - code: $code ");
if ( ! $code ) {
return false;
}
write_log('Time !'. time());
$parts = explode( '|', $code );
// if ( count( $parts ) < 3 ) {
// return false;
// }
// if ( $type !== $parts[0] ) {
// return false;
// }
// if ( absint( $parts[2] ) < time() ) {
// return false;
// }
return get_user_by( 'ID', absint( $parts[1] ) );
}
Some logs are:
Here are some logs of the decrypt not working
Function get_user_from_code - code: x1R6 RZ4HYzXpCb1YUnDNLguUgbAC2nxxUrPVkk9QfOu4raqgYnTlKnTMQqKk0FZGhluK1KKSDN2Bux8pezmuUf8/n1gLPm NPbTEIndtMU= type: ac
[04-Jun-2022 23:45:24 UTC] After decrypt ! - code:
[04-Jun-2022 23:47:34 UTC] Function get_user_from_code - code: undefined type: at
[04-Jun-2022 23:47:34 UTC] PHP Warning: hash_equals(): Expected known_string to be a string, bool given in /var/www/releases/1337/wp-content/plugins/faustwp/includes/auth/functions.php on line 223
[04-Jun-2022 23:47:34 UTC] After decrypt ! - code: �w^~)�
secret_key 8891be90-f4cd-4b2c-a3a0-aecdf13b8498 There are several that do work.
I also get this warning from time to time. PHP Warning: hash_equals(): Expected known_string to be a string, bool given in /var/www/releases/1337/wp-content/plugins/faustwp/includes/auth/functions.php on line 222
Here is an example that does decrypt but the time is wrong. Function get_user_from_code - code: YXR8MTh8MTY1NDM4Njg0Mw== type: at After decrypt ! - code: at|18|1654386843 [04-Jun-2022 23:49:04 UTC] Time !1654386544
The problem disappears when I write this hack along with the above one.
/**
* Encrypt a value.
*
* @uses openssl_encrypt()
* @link https://www.php.net/manual/en/function.openssl-encrypt.php
*
* @param string $value The value to encrypt.
*
* @return string|bool The encrypted value as a base 64 encoded string or false.
*/
function encrypt( $value ) {
write_log("encrypt value $value");
$secret_key = get_secret_key();
write_log("encrypt secret_key $secret_key");
if ( ! $secret_key ) {
return false;
}
$iv = openssl_random_pseudo_bytes( openssl_cipher_iv_length( 'AES-256-CBC' ) );
$cipher_text = openssl_encrypt( $value, 'AES-256-CBC', $secret_key, OPENSSL_RAW_DATA, $iv );
if ( ! $cipher_text ) {
return false;
}
$hash = hash_hmac( 'sha256', $cipher_text, $secret_key, true );
$encode = base64_encode( $iv . $hash . $cipher_text );
write_log("encrypt iv $iv cipher_text $cipher_text hash $hash encode $encode");
return base64_encode( $value ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
return base64_encode( $iv . $hash . $cipher_text ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
}
/**
* Decrypt a value.
*
* @uses openssl_decrypt()
* @link https://www.php.net/manual/en/function.openssl-decrypt.php
*
* @param string $value The base 64 encoded value.
*
* @return string|bool The decrypted value or false.
*/
function decrypt( $value ) {
$secret_key = get_secret_key();
$decrypted_value = false;
if ( ! $secret_key ) {
return $decrypted_value;
}
$value = base64_decode( $value ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
$iv_length = openssl_cipher_iv_length( 'AES-256-CBC' );
$iv = substr( $value, 0, $iv_length );
$hash = substr( $value, $iv_length, 32 );
$cipher_text = substr( $value, $iv_length + 32 );
$hash_comp = hash_hmac( 'sha256', $cipher_text, $secret_key, true );
write_log("decrypt value $value iv $iv cipher_text $cipher_text hash $hash hash_comp $hash_comp");
if ( hash_equals( $hash, $hash_comp ) ) {
$decrypted_value = openssl_decrypt( $cipher_text, 'AES-256-CBC', $secret_key, OPENSSL_RAW_DATA, $iv );
}
return $value;
return $decrypted_value;
}
Let me know if you need more information. Thanks.
@Bowriverstudio I was trying to reproduce this issue today but I couldn't. I'm using Local with the specified PHP Versions and WP 6 and created an example site with local auth strategy with some menus to navigate. I also created 2 users and I was navigating through the menus and I could see that the authenticated parts they were loading OK with no issues.
Is it possible to create a minimal reproducible example by cloning this starter template?
https://github.com/blakewilson/faust-local-auth
Hello @theodesp yes I'll do that. I'll be on vacation for a while so it will take me a while to get to it. The hack that I've done is working for the moment so there is no rush.
Cheers, Maurice
I will close this issue for now due to lack of inactivity. You can create a new one with a reproducible example