Best Practices for Implementing Aura.Auth with Aura.Project_Kernel?

62 views
Skip to first unread message

nonzer0value

unread,
Jan 23, 2015, 12:31:34 PM1/23/15
to aur...@googlegroups.com
Hi,

I'm very new to Aura and so far I really like it, however I'm having trouble grokking how to make use of Auth.Auth with the Project_Framework

I'm building a Web API and a CLI tool for managing some aspects of that API. For the Web API, I will not be using sessions. I *think* I grok how to set up the Adapters that I need and how to wire the services up in the Config mode class DI container. What I'm missing is, once I've got all the dependencies wired together, how does the auth actually happen, i.e. where do I call LoginService::login()? In a Config method?

I need to authenticate on some actions, but not others, so I have some idea of restricted vs public actions. I have a nice set of Callable Action strategies set up to handle routes by the dispatcher. I was thinking I could subclass the abstract Action as 'PrivateAction' and then add some logic in there to accept an auth service wrapper class. The wrapper class would encapsulate Aura.Auth, and accept bits of it via dependency injection. Then in PrivateAction::__invoke() I could add a call to this wrapper and have it perform auth at that point.

I like this and I don't. What I like about it is, whether authentication is required, and the response if authentication fails, is all handled in the action object and that can be very convenient. However it feels like a poor separation of concerns.

Another thought I had was to wire up all the auth service objects in Common::define(), and add an authenticateRoute() method that class which gets called in between the modifyWebRouter() and modifyWebDispatcher() calls. I build a map of routes that require auth, and inject that into the wrapper. The wrappers authenticate() method would check the map to determine if the route it is asked to authenticate actually needs it and take appropriate action. I think that approach might be cleaner, and might preserve SoC between the things the Action needs to do and between the Authentication process. In other words, it keeps the Action code clean, and isolates the authentication code. The down side of that isolation is that the map is maintained separately, and devs may forget to wire up authentication, etc., but I honestly don't see that as too much of an issue in the long term.

Am I on the right track with one of these, or is there a better way to do it?

Thanks in advance for your thoughts and assistance! :)

~nzv

Paul M. Jones

unread,
Jan 25, 2015, 2:31:26 AM1/25/15
to aur...@googlegroups.com
Hi,

Yours is a complicated question, and because of that I can't address every possible aspect. Overall it might be easier if we could see some condensed code examples of your work. However, I think I can provide some direction.



> What I'm missing is ... how does the auth actually happen, i.e. where do I call LoginService::login()? In a Config method?

I opine that those services should be seen as part of your domain layer (in ADR terms) or your model layer (in MVC terms). As such, there should be an ADR Action, or an MVC Controller method, that calls the service. Same goes for the Logout service.

The Resume service, though, that one's a little more complicated. Depending on your needs, it might very well be part of a Config::modify() method to get the session resumed at the earliest possible time. However, I suggest that you place it in your Domain (or Model) layer as well, probably in an Application Service that your Action (or Controller) talks to, just before you test for authentication and authorization within that particular application service.

> I need to authenticate on some actions, but not others, so I have some idea of restricted vs public actions. I have a nice set of Callable Action strategies set up to handle routes by the dispatcher. I was thinking I could subclass the abstract Action as 'PrivateAction' and then add some logic in there to accept an auth service wrapper class. The wrapper class would encapsulate Aura.Auth, and accept bits of it via dependency injection. Then in PrivateAction::__invoke() I could add a call to this wrapper and have it perform auth at that point.
>
> I like this and I don't. What I like about it is, whether authentication is required, and the response if authentication fails, is all handled in the action object and that can be very convenient. However it feels like a poor separation of concerns.

I totally get you on that. I agree that it feels like a poor separation. Part of that is because we usually think of a Controller action method as handling some amount of business logic. This is why I wrote up the Action-Domain-Responder paper, as an alternative that more strictly defines where those separations should be (although you could apply the same ideas with MVC, as long as you had enough rigor to do it consistently).

<http://pmjones.github.io/adr/>

Thus my suggestion that authentication and authorization belong in the Domain (or Model) layer, to get the Action (or Controller) out of the business of handling that kind of work. The Action ends up being pretty dumb, only passing along input to the underlying Domain elements, and passing back output from the Domain to the Responder. Neither the Action nor the Responder really cares if you are authenticated or not, only the Domain does, so that's where authentication logic should go.


> Am I on the right track with one of these, or is there a better way to do it?

There's probably better ways to do it, even than I have mentioned here. Please let me know if my opinions and advice made sense or not. Good luck!



--
Paul M. Jones
pmjo...@gmail.com
http://paul-m-jones.com

Modernizing Legacy Applications in PHP
http://mlaphp.com



nonzer0value

unread,
Feb 5, 2015, 9:40:53 AM2/5/15
to aur...@googlegroups.com
Paul,

I know it's been over a week but I wanted to thank you for your response, and follow up to give you an idea of how I approached this problem.

I think the kind of auth I'm thinking about is "coarse-grained", and that it needs to happen as early as possible, before a request gets too deep into the system. So, I decided that such authentication needs to happen before dispatch, and that medium to fine-grained auth can happen in the Action or the Domain Layer.

I decided to implement a middleware "backbone" on which I can hang service objects that can act on a request independently of the Action itself. There are other use-cases for this. Say you wanted to send some data to a Graphite service somewhere else in your network, or to some other logging service. Or, if you have some global input sanitization filters that you want to run on all incoming request data. Or you want to log/cache a response before sending it.

I decided the first step was to subclass your WebKernel object.

<?php

namespace Nzv\Web;

use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;

use Aura\Web_Kernel\WebKernel;

use Nzv\Web\MiddlewareInterface as Middleware;

class Kernel extends WebKernel implements LoggerAwareInterface
{
 
use LoggerAwareTrait;

 
protected $middleware = null;

 
public function __invoke()
 
{
    $this
->runMiddleware(Middleware::PRE_ROUTE);
    $this
->router->__invoke();

    $this
->runMiddleware(Middleware::PRE_DISPATCH);
    $this
->dispatcher->__invoke();

    $this
->runMiddleware(Middleware::PRE_RESPONSE);
    $this
->response_sender->__invoke();
 
}

 
public function setMiddleware(Middleware $middleware)
 
{
    $this
->middleware = $middleware;
 
}

 
protected function runMiddleware($phase)
 
{
   
if (!$this->middleware)
     
return;

    $this
->middleware->__invoke($phase);
 
}
}

Then I changed the Kernel class name in index.php, altered the Common config to set the middleware service up and inject it into the new Kernel yadda yadda and *BOOM* - a Middleware service that's fun for the whole family! :-D 
 
I'm not going to go into details on the MiddleInterface here - suffice it to say that objects implementing MiddlewareInterface maintain collections of observers namespaced by $phase that it notifies when invoked for a particular phase.

This provides me with the scaffold I need to hang services (such as coarse-grained auth) that I might need to run "between" phases. I register middleware service objects for a particular phase during the "modify" stage of config. Currently I just have the one service - my Web\Auth\Service. Nutshell overview:

public class \Nzv\Web\Auth\Service
{
 
public function __construct(Request $request, Response $response, Auth $auth, LoginService $loginService)
 
{
   
/* args are assigned to member variables w/ same names, e.g. $this->request = $request, etc.) */
 
}

 
public function update()
 
{
    // pass the request to the login service, which uses an
   
// implementation of Aura\Auth\Adapter\AdapterInterface
   
// to provide signed request authentication

    $this->loginService->login($this->auth, ['request' => $request]);
 
}
}

The auth adapter used by the login service will verify a signature present in the request, and then the Aura\Auth\Auth object is injected into my Web\Action classes by the DI container. That way each Action has easy access to the information for the account that was authenticated, and can use it directly.

N.b., my next step with the Middleware thing is to turn it into an EventEmitter, bind events to it via $di->setter, and change the __invoke calls to $this->middleware->emit() calls.

I agree with you that the Action should be a thin, dumb object. Yeah, I know this isn't ideal - I've been living in transaction script hell for some time now. My experience has shown that with transaction scripts, you tend to have too much access to the data layer, which results in a lot of data access code in scripts that is the opposite of DRY, leading to systems with high viscosity due to all the required shotgun surgery.

~nzv

Hari K T

unread,
Feb 5, 2015, 10:01:14 AM2/5/15
to aur...@googlegroups.com
Hey , 

I intentionally left not replying to the thread earlier :-) .

But I smile looking at your middleware which I have been loving to be part of aura . And yes signals ( https://github.com/auraphp/Aura.Signal ) which you call event emitter which you are looking forward.

Good work on it, is the code open-sourced to have a look at?

Thanks!

--
You received this message because you are subscribed to the Google Groups "The Aura Project for PHP" group.
To unsubscribe from this group and stop receiving emails from it, send an email to auraphp+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

nonzer0value

unread,
Feb 5, 2015, 11:09:25 AM2/5/15
to aur...@googlegroups.com
Hari,

Sorry, it's not :-(. What I've given here is merely representative of the actual implementation, just to get my idea across.

Thanks for the tip about Aura.Signal, I'll take a look at it. I was going to use the Sabre EventEmitter because my ORM has a dependency on it, and I didn't want to bring in an extra lib for the same thing. It's also brain-dead simple to drop into any project.

Incidentally I haven't looked at Aura's DB and SQL Mapper libs yet. Haven't had the time, but I want to.

~nzv
Reply all
Reply to author
Forward
0 new messages