Skip to content

determine_current_user hook is unscoped — validates/rejects Bearer tokens intended for other plugins' REST routes #329

Closed

Description

@lhero-org

Plugin version: 4.7.2

Summary

IndieAuth\Authorize::load() hooks determine_current_user unscoped:

\add_filter( 'determine_current_user', array( $this, 'determine_current_user' ), 15 );
\add_filter( 'rest_authentication_errors', array( $this, 'rest_authentication_errors' ) );

This fires on every WordPress request, not just requests to IndieAuth's own routes. determine_current_user() calls get_provided_token(), which reads any Authorization: Bearer ... header present (via $_SERVER['HTTP_AUTHORIZATION'], REDIRECT_HTTP_AUTHORIZATION, or a getallheaders() fallback) and runs it through verify_access_token() — regardless of which plugin or endpoint the token was actually issued for.

If that token isn't a real IndieAuth token, verify_access_token() returns an invalid_token OAuth_Response, which gets stored on $this->error and then surfaced by rest_authentication_errors() as the request's authentication error — before the actual target route's own permission callback or logic ever runs. This produces a 401/error response for a request that has nothing to do with IndieAuth.

Concrete case we hit

Enable Mastodon Apps' Site Health test for its own Authorization-header diagnostic sends a synthetic, intentionally-invalid value (Bearer ema-health-check) to a dedicated diagnostic REST route, specifically to check whether the header physically arrives at WordPress. With IndieAuth active, this synthetic Bearer value gets validated (and rejected) by IndieAuth's unscoped hook before EMA's own diagnostic route logic gets a clean look at the header — producing a false negative ("Authorization headers do not reach WordPress") on a host where header transport is actually working fine. Full root-cause writeup: akirk/enable-mastodon-apps#324

More generally, this affects any plugin that issues or expects its own Bearer tokens on its own REST routes: IndieAuth's check runs first, unconditionally, and can reject a token that was never meant to be validated against IndieAuth's token store at all.

Precedent

Enable Mastodon Apps had the identical unscoped-hook shape on its own OAuth class and fixed it by scoping determine_current_user to only run on its own routes (akirk/enable-mastodon-apps#325, merged) — checking the request's REST route against its own prefix before attempting token validation, and returning the passed-through $user_id unchanged otherwise.

Suggested fix

Scope determine_current_user() (and correspondingly rest_authentication_errors()) so token validation is only attempted when the current request is actually targeting an IndieAuth-owned route. The codebase already has this pattern elsewhere — Authorize::return_oauth_error() checks:

if ( 0 !== strpos( $request->get_route(), '/indieauth/1.0/' ) ) {
    return $response;
}

Applying an equivalent guard to determine_current_user() before calling get_provided_token()/verify_access_token() would prevent IndieAuth from intercepting Bearer tokens meant for other plugins' routes, while leaving its own protected endpoints unaffected.

Workaround in place

We're currently running a small site-specific compatibility plugin that temporarily hides the Authorization header from $_SERVER and IndieAuth's getallheaders() fallback, but only for the one EMA diagnostic route, then restores it immediately after. Happy to remove that once this is scoped upstream — flagging here in case it's useful as a live reproduction case.

Activity

  1. dshanske commented on Sep 6, 2026

    @dshanske
    Member

    @lhero-org What exactly is an IndieAuth scoped route? IndieAuth is used to provide access to any authenticated route. Mastodon only has specific routes it uses. This solution wouldn't work here.

  2. dshanske commented on Sep 6, 2026

    @dshanske
    Member

    It seems like there are a few things that might work here. @pfefferle would welcome your thoughts.

    1. Specifically exclude known conflicting plugin routes. Add a filter that allows other plugins to register the endpoints with this one.
    2. Specifically require supporting endpoints to register indieauth support, e.g. Micropub, Microsub, etc.
    3. Being as IndieAuth also would be supported for the WP routes unless otherwise noted, have that as an enable/disable filter.

    I think scoping IndieAuth to only work with itself makes it useless the same way scoping any OAuth type plugin to do that would.

  3. pfefferle commented on Sep 7, 2026

    @pfefferle
    Member

    I think we had something similar in the past? Isn't it possible to pass the info through, so that another plugin can try to authenticate? Isn't that a filter queue?

  4. kasparsd commented on Sep 8, 2026

    @kasparsd

    I was about to report the same issue. The OAuth2 integration I'm using for an MCP integration is reporting authentication failure on custom REST API routes where the client is sending a Bearer token that is invalid according to indieauth checks.

    I wonder if we could make indieauth skip "invalid" Bearer tokens and let the REST API return an error, if the authorization callback for those routes fail.

  5. pfefferle commented on Sep 8, 2026

    @pfefferle
    Member

    To answer my own question: yes, it is a filter queue, and #245 and #263 (both by @akirk) already made the plugin pass the user through so another plugin can authenticate. What was never handled is the case where nobody recognizes the token. IndieAuth still stores an invalid_token error and returns it from rest_authentication_errors, so the request fails before the route gets a say. @dshanske actually asked about that case in #245, we just never followed up.

    That is basically what @kasparsd suggested. I opened #330: unknown tokens are passed through and the route decides. If the route rejects the request with a 401 anyway, the plugin swaps in the invalid_token error, so Micropub clients still get the RFC 6750 response. Public routes (like the EMA health check) and routes that return their own error are not touched. No gate, IndieAuth tokens keep working on every route.

    One thing it does not solve: Micropub returns a 403 forbidden when no user is logged in, so an invalid token there now gets the Micropub error instead of ours. I think that should be fixed in Micropub, it should be a 401 anyway. I will open a follow-up there.

  6. dshanske commented on Sep 8, 2026

    @dshanske
    Member

    2023 me was on top of things

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions