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?
Authentication middleware This middleware checks for a valid identity using the hasIdentity() method of AuthenticationService . If no identity is present, we redirect the redirect configuration value.
You can run code before and after your Slim application to manipulate the Request and Response objects as you see fit. This is called middleware.
Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs. At its core, Slim is a dispatcher that receives an HTTP request, invokes an appropriate callback routine, and returns an HTTP response. That's it.
You're absolutely on the right track using Middleware to register a hook for authentication. That's the approach I take and the approach I implemented in my own library, Slim Auth.
Using a Singleton can definitely be a code smell, but isn't always. If you feel you need to refactor MyAuthenticationWrapper, that's entirely up to you. The way you're using Middleware and Hooks with your custom class is, IMHO, 100% on target.
Side note: One of my mottos is "Make it work, then refactor." It looks like you're on track there too, so kudos.
Finally, authentication and authorization are complex topics requiring complex solutions. Complex does not mean convoluted, hard-to-maintain spaghetti, but getting it right can result in more code than I'd hoped to write (or more dependencies than I'd hoped to pull in via Composer).
UPDATE
If $app->auth
is Middleware, then yes, you're off track a bit. Your instinct to create Middleware to register a hook is dead on, but Middleware is Middleware and shouldn't be used outside of that context. Ideally you'd create (or better yet find a package on Packagist) an auth class you can use both in your routes and in your Middleware. Pseudo-code would look something like:
$auth = new Auth(); // This is *not* middleware
$app->auth = $auth;
// Login route looks the same
// Middleware
class MyAuthenticationWrapper extends \Slim\Middleware
{
public function call()
{
$app = $this->app;
$auth = $app->auth;
// Register your hook using appropriate methods from Auth class ...
$this->next->call();
}
}
Here's an example Middleware from Slim Auth. I've put together a sample implementation you can look at to see how I put it all together.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With