Hi @joshcanhelp — thanks for this plugin, it fills a real gap in the WordPress ecosystem.
While integrating it I ran into a few issues ranging from a straightforward PHP warning to some security-relevant behaviour worth discussing. I've grouped them below by category.
Bug fixes (unambiguous)
1. Undefined array key warning on every REST request
On line 43, $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] is accessed without a null-coalescing fallback, unlike the three keys above it:
$auth_header = $_SERVER['Authorization'] ??
$_SERVER['authorization'] ??
$_SERVER['HTTP_AUTHORIZATION'] ??
$_SERVER['REDIRECT_HTTP_AUTHORIZATION']; // ← missing ?? null
This produces a PHP Warning: Undefined array key on every REST request that doesn't carry an Authorization header (i.e. the vast majority of them). REDIRECT_* keys only exist on some Apache + mod_rewrite configurations. Fix: add ?? null at the end of the chain.
2. Undefined array key warnings on JWT claims
$decoded_token['sub'], $decoded_token['gty'], and $decoded_token['scope'] are accessed directly without null coalescing. A valid JWT that is missing any of these optional claims will produce additional PHP warnings. Fix: use empty() / ?? null guards on each.
3. get_users() is unbounded
The call to get_users() does not include 'number' => 1 or 'count_total' => false. While the sub claim should be unique, the query is not bounded at the SQL level, which can be costly on large user tables.
Security / architecture discussion
I'd like to open a conversation on the following before proposing a PR, since they involve deliberate design decisions.
4. Token invalid → return null silently ignores a failed authentication (RFC 6750 §3.1)
Currently, when a Bearer token is present but fails verification, the plugin returns null. Depending on WordPress Core internals and other plugins, this may silently fall back to cookie-based authentication for the same request.
RFC 6750 §3.1 states that a server receiving an invalid token MUST respond with HTTP 401 and a WWW-Authenticate: Bearer error="invalid_token" header — not silently degrade to another auth mechanism. The fix would be to hook rest_authentication_errors to surface a proper WP_Error with status: 401 when a token fails validation.
5. Only HS256 (symmetric) signing is supported
Auth0 signs API access tokens with RS256 (asymmetric, JWKS-published) by default. The current implementation requires AUTH0_API_SIGNING_SECRET to be stored in wp-config.php, which increases exposure if that file is ever compromised. Supporting RS256 via WP_Auth0_AsymmetricVerifier and auto-detecting the algorithm from the JWT header alg claim would remove the need to store a shared secret at all for most use cases.
6. Capability restriction mutates $current_user global directly
The scope-based capability restriction modifies $wp_user->allcaps and then overwrites the $current_user global. This is fragile: any subsequent call to wp_set_current_user() by Core or another plugin will discard the mutation. A user_has_cap filter with a captured closure would achieve the same result non-destructively and without touching a global.
Proposed next steps
I'm happy to open a focused PR for items 1–3 (clear bugs, no design decisions involved) right away. For items 4–6 I wanted to get your thoughts first before writing the code, in case you have constraints or prior reasons for the current approach.
Let me know if this is useful — and thanks again for the plugin.
Hi @joshcanhelp — thanks for this plugin, it fills a real gap in the WordPress ecosystem.
While integrating it I ran into a few issues ranging from a straightforward PHP warning to some security-relevant behaviour worth discussing. I've grouped them below by category.
Bug fixes (unambiguous)
1. Undefined array key warning on every REST request
On line 43,
$_SERVER['REDIRECT_HTTP_AUTHORIZATION']is accessed without a null-coalescing fallback, unlike the three keys above it:This produces a
PHP Warning: Undefined array keyon every REST request that doesn't carry anAuthorizationheader (i.e. the vast majority of them).REDIRECT_*keys only exist on some Apache +mod_rewriteconfigurations. Fix: add?? nullat the end of the chain.2. Undefined array key warnings on JWT claims
$decoded_token['sub'],$decoded_token['gty'], and$decoded_token['scope']are accessed directly without null coalescing. A valid JWT that is missing any of these optional claims will produce additional PHP warnings. Fix: useempty()/?? nullguards on each.3.
get_users()is unboundedThe call to
get_users()does not include'number' => 1or'count_total' => false. While the sub claim should be unique, the query is not bounded at the SQL level, which can be costly on large user tables.Security / architecture discussion
I'd like to open a conversation on the following before proposing a PR, since they involve deliberate design decisions.
4. Token invalid →
return nullsilently ignores a failed authentication (RFC 6750 §3.1)Currently, when a Bearer token is present but fails verification, the plugin returns
null. Depending on WordPress Core internals and other plugins, this may silently fall back to cookie-based authentication for the same request.RFC 6750 §3.1 states that a server receiving an invalid token MUST respond with HTTP 401 and a
WWW-Authenticate: Bearer error="invalid_token"header — not silently degrade to another auth mechanism. The fix would be to hookrest_authentication_errorsto surface a properWP_Errorwithstatus: 401when a token fails validation.5. Only HS256 (symmetric) signing is supported
Auth0 signs API access tokens with RS256 (asymmetric, JWKS-published) by default. The current implementation requires
AUTH0_API_SIGNING_SECRETto be stored inwp-config.php, which increases exposure if that file is ever compromised. Supporting RS256 viaWP_Auth0_AsymmetricVerifierand auto-detecting the algorithm from the JWT headeralgclaim would remove the need to store a shared secret at all for most use cases.6. Capability restriction mutates
$current_userglobal directlyThe scope-based capability restriction modifies
$wp_user->allcapsand then overwrites the$current_userglobal. This is fragile: any subsequent call towp_set_current_user()by Core or another plugin will discard the mutation. Auser_has_capfilter with a captured closure would achieve the same result non-destructively and without touching a global.Proposed next steps
I'm happy to open a focused PR for items 1–3 (clear bugs, no design decisions involved) right away. For items 4–6 I wanted to get your thoughts first before writing the code, in case you have constraints or prior reasons for the current approach.
Let me know if this is useful — and thanks again for the plugin.