Jwt-auth: Testing failing to parse authorization header

Created on 21 Jun 2016  路  5Comments  路  Source: tymondesigns/jwt-auth

Hi there. First of all, thanks for the awesome work.

I'm writing unit tests for my API. When I use WithoutMiddleware, work's fine. But there is some endpoints that I need to parse the current user:

$user = JWTAuth::parseToken()->authenticate();

When I need this, I write my tests like this:

$user  = factory(User::class)->create(['active' => 1]);
$token = JWTAuth::fromUser($user);
$headers['authorization'] = 'Bearer ' . $token;
$server   = $this->transformHeadersToServerVars($headers);
$data     = factory(Product::class)->make(['title' => 'Lorem Ipsum'])->toArray();
$response = $this->call('POST', '/api/v1/itens', $data, [], [], $server);

But in the test above, I always get an JWTException. Researching more, I checked the Tymon\JWTAuth\JWTAuth class, parseAuthHeader method. The headers authorization simply don't exists. So, I changed the method to:

protected function parseAuthHeader($header = 'authorization', $method = 'bearer')
{
    // $header = $this->request->headers->get($header);
    $header = request()->headers->get($header);

    if (! starts_with(strtolower($header), $method)) {
        return false;
    }

    return trim(str_ireplace($method, '', $header));
}

This work's fine. A think the Request object passed into the __construct method is not the same. Don't know exactly if it is a Laravel problem or a package problem (or I'm doing something wrong).

Most helpful comment

In most tests, I just enabled the middleware and it worked.

But in some tests, I had to do this in some controllers:

if ((app()->environment() == 'testing') && array_key_exists('HTTP_AUTHORIZATION',  \Request::server())) {
    JWTAuth::setRequest(\Route::getCurrentRequest());
}

It's not the ideal but it's better to have this and a test that really test something.
Hope it helps.

All 5 comments

A think the Request object passed into the __construct method is not the same.

You're correct. During tests, Laravel constructs the Request object a bit differently. The problem is that, in production, Laravel is booted with an incoming request, but in testing it has to create a placeholder, while it waits for you run $this->call or a similar function. Thus, the Request object that gets injected during construct isn't exactly the one you want. (Which I believe is why jwt-auth no longer injects it in v1.0)

One option you have is to set the request manually:

$user = JWTAuth::setRequest($request)->parseToken()->authenticate();

(Of course, the middleware already uses setRequest like this, so it might just be simpler to enable the middleware for these specific tests.)

Oh, now I get it. I just enable the middlewares from test's like this and it worked correctly. Thanks!

@luisdalmolin, could you share your test code?

thanks

In most tests, I just enabled the middleware and it worked.

But in some tests, I had to do this in some controllers:

if ((app()->environment() == 'testing') && array_key_exists('HTTP_AUTHORIZATION',  \Request::server())) {
    JWTAuth::setRequest(\Route::getCurrentRequest());
}

It's not the ideal but it's better to have this and a test that really test something.
Hope it helps.

@luisdalmolin Thanks a lot for your reply :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gamelife1314 picture gamelife1314  路  3Comments

harveyslash picture harveyslash  路  3Comments

shah-newaz picture shah-newaz  路  3Comments

marciomansur picture marciomansur  路  3Comments

mihailo-misic picture mihailo-misic  路  3Comments