Neka
Neka

Reputation: 1664

Security throwns 500 exception with Token was not found in TokenStorage instead of 403 or 401

I make authorization by ApiKey and I want to get 401 Unauthorized if no authorization data presented, and 403 Forbidden if authorization data is invalid. But I got 500 Internal Server Error in both situations.

security.yml:

security:

    providers:
        api_key_user_provider:
            entity:
                class: RestBundle:RestUser
                property: apikey

    firewalls:
        rest_api_area:
            pattern: ^/api
            stateless: true
            rest_auth:
                header: x-apikey
            provider: api_key_user_provider

    access_control:
        - { path: ^/api, roles: ROLE_REST_USER }

RestUserListener.php:

class RestUserListener implements ListenerInterface
{
    protected $tokenStorage;
    protected $authenticationManager;
    private $header;

    function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager, $header)
    {
        $this->tokenStorage = $tokenStorage;
        $this->authenticationManager = $authenticationManager;
        $this->header = $header;
    }

    public function handle(GetResponseEvent $event)
    {
        $request = $event->getRequest();

        $apikey = $request->headers->get($this->header);
        if (!$apikey) return;

        $token = new RestUserToken();
        $token->setUser($apikey);

        $authToken = $this->authenticationManager->authenticate($token);
        $this->tokenStorage->setToken($authToken);
        return;
    }
}

RestUserAuthenticationProvider.php:

class RestUserAuthenticationProvider implements AuthenticationProviderInterface
{
    private $userProvider;

    public function __construct(UserProviderInterface $userProvider)
    {
        $this->userProvider = $userProvider;
    }

    public function authenticate(TokenInterface $token)
    {
        $user = $this->userProvider->loadUserByUsername($token->getUsername());

        if ($user)
        {
            $authenticatedToken = new RestUserToken($user->getRoles());
            $authenticatedToken->setUser($user);

            return $authenticatedToken;
        }

        throw new AuthenticationException("Apikey not found.");
    }

    public function supports(TokenInterface $token)
    {
        return $token instanceof RestUserToken;
    }
}

RestUserToken as simple as AbstractToken and has no additional logic.

api_key_user_provider is the standard entity provider identified by apikey property of RestUser

RestUserFactory also has no additional magic inside, just like in the official documentation

Upvotes: 4

Views: 3768

Answers (1)

Radu Murzea
Radu Murzea

Reputation: 10900

The RestUserListener::handle() method should handle the case of returning HTTP 401 or HTTP 403.

Simply having return; is not going to make this happen.

In a similar application I wrote, I did this:

use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;

//
...
//

public function handle(GetResponseEvent $event)
{
    $request = $event->getRequest();

    if ( /* something invalid here, so error */ ) {
        $context = $request->getHost();

        throw new UnauthorizedHttpException(
            "Basic realm=\"$context\"",
            'Please authenticate correctly or any other message here'
        );
    }
}

Throwing UnauthorizedHttpException will result in a HTTP 401 (you'll understand if you'll look at the source code of the exception).

For HTTP 403, you can use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException instead.

Upvotes: 2

Related Questions