JSON:API can't handle exceptions thrown by the authentication provider

Created on 1 December 2019, over 4 years ago
Updated 18 January 2023, over 1 year ago

Regarding the issue #2840205: Error messages/codes should be more helpful & match spec. β†’ .

SimpleOauthAuthenticationProvider now throws a HttpException if the user $request is not valid (which can happen when the access token is expired i.e.).

    // Update the request with the OAuth information.
    try {
      $auth_request = $this->resourceServer->validateAuthenticatedRequest($request);
    }
    catch (OAuthServerException $exception) {
      // Procedural code here is hard to avoid.
      watchdog_exception('simple_oauth', $exception);

      throw new HttpException(
        $exception->getHttpStatusCode(),
        $exception->getHint(),
        $exception
      );
    }

Or when the account is blocked:

    // Revoke the access token for the blocked user.
    if ($account->isBlocked() && $account->isAuthenticated()) {
      $token->revoke();
      $token->save();
      $exception = OAuthServerException::accessDenied(
        t(
          '%name is blocked or has not been activated yet.',
          ['%name' => $account->getAccountName()]
        )
      );
      watchdog_exception('simple_oauth', $exception);
      throw new HttpException(
        $exception->getHttpStatusCode(),
        $exception->getHint(),
        $exception
      );
    }

The authentication_subscriber runs before than the route_listener (in charge of setting the request format and other route parameters), the exception ends the propagation of the kernel.request event and prevents the route listener to be executed.

An Exception subscriber trying to handle the error would look into the request for a specific content type or parameter, but it can't get these parameters because the route listener hasn't run.

In JSON:API, when the token is expired, an exception is thrown and the EarlyFormatSetter::filter is never able to set the $request format to 'api_json'. JSON:API tries to handle jsonapi response errors in DefaultExceptionSubscriber::onException by looking at the $request format and at the route parameter _is_jsonapi, but neither of them have been set, because the route_listener never ran.

  protected function isJsonApiExceptionEvent(GetResponseForExceptionEvent $exception_event) {
    $request = $exception_event->getRequest();
    $parameters = $request->attributes->all();
    return $request->getRequestFormat() === 'api_json' || (bool) Routes::getResourceTypeNameFromParameters($parameters);
  }

The result is that the exception is handled by the core DefaultExceptionHtmlSubscriber, which makes a subrequest to /system/4xx and returns the error to the client with html content instead of jsonapi.

The fix for this would be to return NULL, as other authentication providers do, and let the Controller decide how to handle the request for anonymous user.

Not sure how to differentiate between the possible error causes in the authentication provider, which was the original reason why the exception was introduced, instead of just returning NULL. Somehow the Authentication Provider should leave the error code in the event/request/session for the controller to respond accordingly.

πŸ› Bug report
Status

Active

Version

5.0

Component

Code

Created by

πŸ‡ΊπŸ‡ΈUnited States sam711

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

No activities found.

Production build 0.69.0 2024