Closed zeced closed 8 years ago
How does your .htaccess
file look like?
Like this :
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
</IfModule>
And call to middleware is:
$app->add(new \Slim\Middleware\JwtAuthentication([
"relaxed" => ['192.168.98.13'], // only for testing
"secret" => getenv('TOKEN_SECRET'),
"logger" => $c->get('logger'),
"callback" => function ($request, $response, $arguments) use ($app) {
$app->jwt = $arguments["decoded"];
},
"rules" => [
new \Slim\Middleware\JwtAuthentication\RequestPathRule([
"path" => "/",
"passthrough" => ["/users/login", "/ping"]
]),
new \Slim\Middleware\JwtAuthentication\RequestMethodRule([
"passthrough" => ["OPTIONS"]
])
]
])
);
Thanks for support :-)
Slim 3 don't keep $app instance anymore, you should save your JWT in container.
@ronaldo-systemar I've seen that i'm currently using an abstract class to get the SLIM instance through controllers but i will certainly pass the token through a DI container to be more "SLIM compliant". My main problem is that without apache_request_headers() the response's code is always 401.
I am also having a hard time trying to get my decoded JWT token inside my routes and controllers with Slim 3.1. I understand the concept that i have to inject it through the DI, but, i don't know exactly how to do it. The JwtAuthentication->fetchToken method needs the Request object to fetch the token, so i tried to get the Request object inside my container injection. But, the only way I could do it was returning a Closure from the DI container, and then passing my Request object as an argument of this closure to fetch and decode the token.
For now, i did it like this:
$container['jwt_token'] = function (Container $c) { $jwtAuth = new JwtAuthentication([ 'secret' => 'my-jwt-secret' ]); return function (Request $request) use ($jwtAuth) { return $jwtAuth->decodeToken($jwtAuth->fetchToken($request)); }; };
Then, in my routes definition I do like this:
$app->get('/dump-jwt-token', function($request, $response, $args) { $closure = $this->get('jwt_token'); $token = $closure($request); $response->write($token); return $response; });
Ok, this works to get my JWT inside the routes. But, I don't feel that's the right way to do it. I would appreciate some help about the right (or better) way to do it. Also, how shoudl I do to get my token from inside any controller or AbstractController class?
I am grateful for any help.
Have you tried the example from README?
$app = new \Slim\App();
$app->add(new \Slim\Middleware\JwtAuthentication([
"secret" => "supersecretkeyyoushouldnotcommittogithub",
"callback" => function ($request, $response, $arguments) use ($app) {
/* Do something with decoded token in here, for example: */
$app->jwt = $arguments["decoded"];
}
]));
Hi tuupola! Thanks for your answer.
Yes, that was the first thing I've tried and it just not worked for me. But now, after your answer, I tried it again and I just discovered that my $app->jwt was not defined because the route I was trying to test, was listed to be a passthrough route in my JwtAuthentication settings initial object. So, when I removed the route from the passthrough array, it just worked perfectly. I noticed that happens because, when a route is passthrough route, the Jwt callback function is not called at all. I am sorry for this misunderstanding...
Okay, now I am able to retrieve my $app->jwt from inside any route declared with closures using the
function ($req,$res,$args) use ($app)statement. But, I am still unable to retrieve it from inside routes declared inside controllers. Because, when declaring routes with controllers, I can not pass the use ($app) statement.
So, I have a base AbstractController like this:
abstract class AbstractController { protected $app; public function __construct(Container $container) { $this->app = $container; } public function getApp() { return $this->app; } // This does not work public function getJwt1() { return $this->app->jwt; } // Also this does not work public function getJwt2() { return $this->app->get('jwt'); } // Also this does not work public function getJwt3() { return $this->jwt; } }
So now, the question is, how can I get the $app->jwt variable from inside a route defined with controllers?
Now I understand how simple it is to retrieve an $app variable inside of the controllers. That is done using the container, and not the $app itself. So I just declared a container service like this:
$container['jwt'] = function (Container $c) use ($app) { return $app->jwt; };
And then, I can get it from anywhere I have access to container instance (in these example from an abstract base controller):
abstract class AbstractController { protected $app; public function __construct(Container $container) { $this->app = $container; } public function getJwt() { return $this->app->get('jwt'); } }
Thanks for the help! And sorry for my confusion about it.
Great and thanks for the example! It will help people coming here from Google.
I have the same problem. I want to access JWT token body in a function which is called on a route.
Here is the route:
$app->put('/transcript', '\Myclass:Myfunction');
if we put the following code in dependencies.php
$container['jwt'] = function (Container $c) use ($app) {
return $app->jwt;
};
and include it like:
require_once('vendor/autoload.php');
require_once('classes.php');
require_once 'dependencies.php';
$app = new \Slim\App($container);
We can't access $app
in dependencies.php
. And if we include it after $app
like:
require_once('vendor/autoload.php');
require_once('classes.php');
$app = new \Slim\App($container);
require_once 'dependencies.php';
How could we pass $container
to The App instance?
I'm new to DI and services in general. Perhaps somebody could explain it to me.
If possible, a minimal working example would be great.
In callback store the token somewhere where you can fetch it later. Using $app
is just an example. You do not need to use it.
$app = new \Slim\App();
$app->add(new \Slim\Middleware\JwtAuthentication([
"secret" => "supersecretkeyyoushouldnotcommittogithub",
"callback" => function ($request, $response, $arguments) use ($app) {
print_r($arguments["decoded"]);
/* Here save the decoded token somewhere. */
}
]));
But this is more about Slim 3 general help and not directly related to JwtAuthentication middleware. Slim docs have some explanation on how the dependency injection works.
@kirtangajjar If you look a little up, @mateuslopes described a perfect example for your issue. Create a controller (base controller) that be extended by all your controllers, so, inject the container into it and voilà.
E.g:
Base.php
<?php
namespace Controllers;
class Base
{
protected $container;
public function __construct(\Slim\Container $container)
{
$this->container = $container;
}
}
Users.php
<?php
namespace Controllers;
class Users extends Base
{
/* Your controller actions */
}
Now, in your src/dependencies.php (if you have it, of course), do it:
$container = $app->getContainer();
// Controller container inject
$container['Base'] = function ($c) {
return new Base($c);
};
Everytime Base controller is called, Slim will look into DI container and find the definition above, so, a new instance of Base is created with container injected, saving it into protected $container. Now, you can easy access container inside controller like this, e.g: $this->container->get('settings')['app']['key'] or something like this $this->container['jwt'];
If you are lost with file structure definition, as I said early src/dependencies.php, look at this skeleton project: https://github.com/slimphp/Slim-Skeleton
--- EDIT --- I forget to mention, you must instantiate your middleware like this, e.g:
$app->add(new Slim\Middleware\JwtAuthentication([
"secret" => 'my_secret_key',
"callback" => function ($request, $response, $arguments) use ($container) {
$container['jwt'] = $arguments["decoded"];
}
]));
Good lucky and have a nice coding
Oh! Thanks @ronaldo-systemar for the example. That piece of code really helped.
I was facing same fate as @mateuslopes and was unable to get token header. Later did I realized i had put it in passthrough for debugging.
Now that I have removed it, it works fine.
Thanks everyone for helping out!
@kirtangajjar You are welcome =)
A lot of Slim 2 users are facing this same problem when upgrading to Slim 3, because $app instance was easy to get into old version inside controllers. Since Slim 3 changed approach, basically everything is handled into container or middleware.
I updated the README example for callback to following. Thanks @ronaldo-systemar for the tips.
$app = new \Slim\App();
$container = $app->getContainer();
$container["jwt"] = function ($container) {
return new StdClass;
};
$app->add(new \Slim\Middleware\JwtAuthentication([
"secret" => "supersecretkeyyoushouldnotcommittogithub",
"callback" => function ($request, $response, $arguments) use ($container) {
$container["jwt"] = $arguments["decoded"];
}
]));
Now you can access the decoded tokens in your routes with:
$app->get("/test", function ($request, $response, $arguments) {
print_r($this->jwt);
});
:+1: Hope helped.
Nice coding for all.
Did this thread get off topic a little? When using that rewrite rule combination, I send everything over to api.php. You'd think I would see $_SERVER['HTTP_AUTHORIZATION'], but instead I see $_SERVER['REDIRECT_HTTP_AUTHORIZATION']. I'm using PHP-FPM with the Apache 2.4 module mod_proxy_fcgi. I don't know if others have seen this or not, but Apache mangles the environment variable at some point in the htaccess rule processing apparently. I had to make a kludge to check for the present of 'REDIRECT_HTTP_AUTHORIZATION' and then set 'HTTP_AUTHORIZATION' on _SERVER manually, which then allowed the fetchToken() method in JwtAuthentication.php to see it properly.
For some discussion on the redirection rule processor and environment variables about this same thing.
Further commenting on the OP, apache_request_headers() seems to not always be available. It isn't for me when using PHP-FPM. I think it might only be when using mod_php. I'm not 100% sure on this, though....
@craigify That is an undocumented Apache feature. If you are affected by it you can set the environment to search the token from with environment
setting.
$app->add(new \Slim\Middleware\JwtAuthentication([
"secret" => "supersecretkeyyoushouldnotcommittogithub",
"environment" => "REDIRECT_HTTP_AUTHORIZATION"
]));
I actually thought this was documented in the README but seems I have forgotten it.
@craigify Actually when I think about it https://github.com/tuupola/slim-jwt-auth/commit/418bfa3edfee5d80e4d2ed01341956611f64cebd makes it more developer friendly. Now the middleware checks both HTTP_AUTHORIZATION
and REDIRECT_HTTP_AUTHORIZATION
by default.
The above examples are fine if you're injecting the container directly into your controllers. But what about if you're not injecting the container and doing DI (instead of service location)? How do I pass the token object to controllers?
Here's an example that doesn't work ... obviously because JWT is empty at the time it's passed to the controller.
$container['JWT'] = function() {
return new StdClass;
};
$container['MAPT\Controllers\GroupController'] = function($ci) {
return new MAPT\Controllers\GroupController(
$ci->GroupRepository,
$ci->JWT
);
};
On my Slim setup (v3) with php 5.6.14, token can't be found, i need to get it via apache_request_headers() and set $_SERVER['HTTP_AUTHORIZATION'] in index.php. I've tried different .htaccess directives like the one provided here without success.
Would it be possible to check if token can be found in 'apache_request_headers' ?