I have written my own authentication controller to perform user authentication in my Slim application. Although it works, I am unsure if this is the way Slim is intended to work.
My authentication controller $auth
has methods like $auth->login($user, $password)
and $auth->logout()
that change the state of the session and methods that report status, such as $auth->userIsLoggedIn()
. Also, given a request, it can determine if a user has access to the requested route.
Currently, I am using a single instance of $auth
in my Slim application in two different ways: as a singleton registered to $app->auth
, and as route middleware that is applied to all routes. So, the Slim application is bootstrapped like this:
// Create singleton instance of MyAuthWrapper
$app->auth = new MyAuthenticationWrapper( array() );
// Attach the same instance as middleware to all routes
$app->add( $app->auth );
I am using the singleton instance from within my routes, for example, in the login route:
$app->post( '/login', function() use ($app)
{
// ...
$user = $app->auth->authenticate( $app->request()->post('username'), $app->request()->post('password') );
// ...
}
And I am using the middleware version in all routes by attaching a method to the slim.before.dispatch
hook that verifies that the user is authenticated and redirects to the login page otherwise. In order to do that, the authentication wrapper extends \Slim\Middleware and thus implements the call
method, like this (simplified):
class MyAuthenticationWrapper extends \Slim\Middleware
{
// ... Implementation of methods such as authenticate(), isLoggedIn(), logout(), etc.
public function call()
{
$app = $this->app;
$isAuthorized = function () use ($app) {
$hasIdentity = $this->userIsLoggedIn(); // Assume this to work
$isAllowed = $this->userHasAccessToRequestedRoute(); // Assume this to work
if ($hasIdentity && !$isAllowed)
{
throw new Exception("You have no access to this route");
}
if (!$hasIdentity && !$isAllowed)
{
return $app->redirect( $loginPath );
}
};
$app->hook('slim.before.dispatch', $isAuthorized);
$this->next->call();
}
}
Using a singleton is a slight code smell to me, but then adding the singleton instance as middleware with $app->add( $app->auth )
feels plain dirty. And finally using the middleware to register a closure to the dispatch hook makes me wonder if this whole strategy isn't too convoluted for a framework called Slim. But I can't figure out if there is an easier or more elegant way to accomplish what I want.
The question: Am I on the right track, or am I missing something about how Slim works that would allow me to accomplish this in a less complex way?