Closed EgilSandfeld closed 2 years ago
Thanks so much for the feedback and questions here! I'll take a look as soon as I'm able.
Looking more into it, the response content from Auth0.com/userinfo yields the following:
{
"sub":"google-oauth2|1098...8889",
"given_name":"AWE",
"family_name":"Tester",
"nickname":"theawetesting",
"name":"AWE Tester",
"picture":"https://lh3.googleusercontent.com/a/AATXAJw...sY9t=s96-c",
"locale":"en",
"updated_at":"2022-03-30T07:07:09.835Z",
"email":"....@gmail.com",
"email_verified":true,
"https://custom-claim/has_wp_account":true
}
So the https://custom-claim/has_wp_account is set to true here, so far so good, but on the other existing WP users this property is not even present in the response?!
Anyway, I believe I made a workaround to solve my issue, by adding this to the determine_current_user filter:
// Could not find a user with the incoming Auth0 ID.
if ( ! $wp_user ) {
$wp_user = get_user_by_email($decoded_token['email']);
// Could not find a user by the incoming email either.
if ( ! $wp_user ) {
echo " No wp_user found by sub: " . $decoded_token['sub'] . " or found by email: " . $decoded_token['email'];
return null;
}
//Found the user by email, so set the meta data auth0_id value using $decoded_token['sub']
//echo " Found by email: " . $decoded_token['email'] . " and adding sub: " . $decoded_token['sub'];
update_user_meta( $wp_user->ID, $meta_key, $decoded_token['sub']);
}
I would say this is still quite safe to do, as it still requires both a known auth0_id + known WP user email. Let me know if this is bad practice.
Okay, my question is shifting slightly towards the WP-Auth0 setup now. While the above works for adding the auth0_id to an existing WP user, my setup isn't sufficient for new users being created via an app.
I have two apps in Auth0:
1) Regular Web Application, which uses Token Endpoint Auth Method: POST (users should be able to login/signup via the website) 2) Native C# App, which uses Token Endpoint Auth Method: NONE (users should be able to login/signup via the app) Both are connected to the same DBs and authentication ways, but the native apps maybe can't access the WP-API I made for the Regular Web variant.
I think this is due to the WP Auth0 plugin only being connected with the 1) Regular Web Application or that the Native App isn't connected properly with the WP-API during user creation?
How can a WP-User be created using both a native app and regular web app?
@EgilSandfeld - Apologies for the delay in response here.
For your first comment about linking the incoming user in WordPress ... I have not worked in the plugin for a while but, last I was in there, it would link the user by default if an email is found but only if the incoming user has an email_verified
claim set to true
(which your example user does). The reason for this is that a user could register themselves in Auth0 under any email address they want and, if it's not verified, they would be linked to an account in WordPress without any checks. Now they would be able to log into the WordPress account using their Auth0 credentials without any check on whether they own that email address or not.
For your next comment ... the native app should be able to make an account on Auth0, that's fine. The Rule code provided in the post only checks for the existence of an account, it does not create an account. If your native app sees that claim is false
then it needs to send the user to the WordPress site to register (which will use the same Auth0 user).
Hope that helps a bit and apologies again for the long delay!
Thank you for the help and push in the right direction. I have it working now, where it's able to create a WP user from the Auth0 data. I basically took most of your code and adapted it to create the WP user:
`declare(strict_types=1);
namespace DRE\WordPress\RestApiAuth0;
header('Content-Type: application/json');
function create_wpuser($data) {
// Only checked, not saved or output anywhere.
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput
$request_uri = wp_unslash( $_SERVER['REQUEST_URI'] ?? '' );
// Validated below with TokenVerifier.
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput
$auth_header_raw = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? '';
$auth_header_raw = wp_unslash( $auth_header_raw );
// Check if this is a WP REST API request.
if ( 1 !== preg_match( '/^\/' . rest_get_url_prefix() . '(.*)/', $request_uri ) ) {
return $data;
}
// Check for a token in the Authorization header.
$auth_header_parts = explode( ' ', $auth_header_raw );
if ( 'Bearer' !== $auth_header_parts[0] || empty( $auth_header_parts[1] ) ) {
return $data;
}
// From this point on, we're going to treat the request as OAuth2 protected.
// If we cannot validate the token for some reason, the request is processed without auth.
if (empty($_GET['scope'])) {
die( 'No `scope` URL parameter' );
}
if (empty($_GET['id_token'])) {
die( 'No `id_token` URL parameter' );
}
if (empty($_GET['token_alg']) || ! in_array($_GET['token_alg'], [ 'HS256', 'RS256' ])) {
die( 'Missing or invalid `token_alg` URL parameter' );
}
$token_issuer = 'https://'.AUTH0_DOMAIN.'/';
$client_id = AUTH0_CLIENT_ID;
$id_token = rawurldecode($_GET['id_token']);
$signature_verifier = null;
if ('RS256' === $_GET['token_alg']) {
$jwks_fetcher = new \WP_Auth0_JwksFetcher();
$jwks = $jwks_fetcher->getKeys($token_issuer.'.well-known/jwks.json');
$signature_verifier = new \WP_Auth0_AsymmetricVerifier($jwks_fetcher);
} else if ('HS256' === $_GET['token_alg']) {
$signature_verifier = new WP_Auth0_AsymmetricVerifier(getenv('AUTH0_CLIENT_SECRET'));
}
$token_verifier = new \WP_Auth0_IdTokenVerifier(
$token_issuer,
$client_id,
$signature_verifier
);
try {
$decoded_token = $token_verifier->verify($id_token);
} catch (\Exception $e) {
echo 'Caught: Exception - '.$e->getMessage();
}
// We don't have a user to associate this call to.
if ( ! $decoded_token['sub'] ) {
return null;
}
global $wpdb;
$meta_key = $wpdb->prefix . 'auth0_id';
$username = $decoded_token['https://custom-claim/username'];
$user_id = wp_insert_user( array(
'user_login' => $username,
'user_pass' => '',
'user_email' => $decoded_token['email'],
'first_name' => $decoded_token['email'] . ' ' . $decoded_token['nickname'],
'display_name' => $username,
'role' => 'subscriber'
));
if ( is_wp_error( $user_id ) ) {
// something went wrong
echo $user_id->get_error_message();
return null;
}
$wp_user = get_user_by_email($decoded_token['email']);
if ( ! $wp_user ) {
echo " User not created: " . $decoded_token['email'];
return null;
}
//Found the user by email, so set the meta data auth0_id value using $decoded_token['sub']
update_user_meta( $wp_user->ID, $meta_key, $decoded_token['sub']);
// Pull the scopes out of the access token and adjust the user accordingly.
$access_token_scopes = explode( ' ', $_GET['scope'] );
foreach ( $wp_user->allcaps as $cap => $value ) {
if ( ! in_array( $cap, $access_token_scopes, true ) ) {
$wp_user->allcaps[ $cap ] = 0;
}
}
// Set the current user as this modified user.
//global $current_user;
//$current_user = $wp_user;
return $wp_user->ID;
}`
add_action( 'rest_api_init', function () { register_rest_route( 'dre/v1', '/create_user/', array( 'methods' => 'GET', 'callback' => __NAMESPACE__ . '\\create_wpuser', 'permission_callback' => '__return_true' ) ); } );
I've left the WP user password empty, which seems to be working. Since there's IdentityToken brought along, a password seems redundant to set, right?
Also, I'm not sure whether to uncomment the last bit:
// Set the current user as this modified user. //global $current_user; //$current_user = $wp_user;
If there's anything that should be changed for the general case, a PR would be appreciated! I
As for the commented code ... I don't know if I can say whether that's necessary or not without understanding exactly what you're doing here. Those two lines set the session to whatever $wp_user
is set to, effectively logging them in as that user. Since you could set that to literally anything, it's a critical thing to get right!
Don't think any PR is necessary for this scenario. Thanks for taking the time to help me 🤘
I'm a bit at a loss here and hoping you can lead me in the right direction. After having a successful Auth0 WordPress connection up and running, I've used your article to verify users' subscription from the WP REST. I've taken the code from wp-rest-api-auth.php and pasted it into a PHP snippet on my WP admin. Minor changes and additions here:
` $wp_user = reset( get_users( array( 'meta_key' => $meta_key, 'meta_value' => $decoded_token['sub'], 'number' => 1 )
) );
This part fails with a valid $decoded_token['sub'] and meta key, and it doesn't return any users:
"No wp_user found with sub: google-oauth2|1098..........8889 and meta_key: wp_DREauth0_id {\"code\":\"rest_forbidden\",\"message\":\"Sorry, you are not allowed to do that.\",\"data\":{\"status\":401}}"
The real issue here is probably not that code part, but the fact that new users do not have the auth0_id added to their user metadata. Looking at the WP users, it's clear that at some point they did get auth0_id added, but not anymore:
I might previously have added these auth0_ids manually (I can't honestly remember), so it might be that this never worked in the first place. I can confirm that all users have the user_id present in Auth0 User Management. Example:
What should I do to (re)establish auth0_id presence in WP users meta data?