Created
April 29, 2014 19:21
-
-
Save evillemez/11409525 to your computer and use it in GitHub Desktop.
Prototype interfaces for a simpler Authentication framework.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace AC\Authentication; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\RequestMatcherInterface; | |
use Symfony\Component\HttpFoundation\Response; | |
use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
/** | |
* Authenticates an incoming request by matching it to registered Authenticators. | |
*/ | |
interface FirewallInterface | |
{ | |
/** | |
* Given a request, return an AuthContextInterface, or a Response. The firewall calls | |
* registered Authenticators that match the request, until one of them handles the request. | |
* If Authentication failed, or could not be performed, throws an exception. | |
* | |
* @param Request $request [description] | |
* @return AuthContextInterface|Response [description] | |
* @throws AuthFailedException | |
*/ | |
public function authenticate(Request $request); | |
/** | |
* Register an instance of an authenticator, identifiable by a unique tag. There could | |
* be more than one instance of the same Authenticator class, but configured differently | |
* for different parts of a site. | |
* | |
* @param string $name [description] | |
* @param AuthenticatorInterface $handler [description] | |
*/ | |
public function registerAuthenticator($name, AuthenticatorInterface $handler); | |
/** | |
* Match a request to a specific Authenticator, with extra optional parameters. | |
* | |
* @param RequestMatcherInterface $matcher A matcher for incoming Requests | |
* @param string $name The name of the Authenticator that should handle the matched Request | |
* @param array|null $options Any optional parameters that apply only to this matched Request | |
*/ | |
public function matchRequest(RequestMatcherInterface $matcher, $name, array $options = []); | |
/** | |
* Get the dispatcher instance used by the Firewall. The firewall emits events that can | |
* be subscribed to during the authentication flow. | |
* | |
* @return EventDispatcherInterface | |
*/ | |
public function getDispatcher(); | |
} | |
/** | |
* Describes events emitted by the Firewall during the authentication process. | |
*/ | |
final class FirewallEvents | |
{ | |
/** | |
* Fires before Authenticators are called to authenticate the request. | |
*/ | |
const REQUEST = 'firewall.request'; | |
/** | |
* Fired when an AuthContext is either newly created, or successfully refreshed. | |
*/ | |
const AUTHENTICATED = 'firewall.authenticated'; | |
/** | |
* Fired when a new AuthContext has been created. | |
*/ | |
const NEW_AUTH = 'firwall.newly_authenticated'; | |
/** | |
* Fires when an AuthContext has been refreshed. | |
*/ | |
const REFRESHED_AUTH = 'firwall.authentication_refreshed'; | |
/** | |
* Fires when the Firewall returns a Response. | |
*/ | |
const RESPONSE = 'firewall.response'; | |
/** | |
* Fires when the Firewall catches an AuthException thrown by an Authenticator. | |
*/ | |
const EXCEPTION = 'firewall.exception'; | |
} | |
/** | |
* Implements a specific type of authentication logic for the Firewall. Authenticators | |
* create or refresh AuthContexts. | |
*/ | |
interface AuthenticatorInterface | |
{ | |
/** | |
* Allows the firewall to easily check for mismatches. | |
* | |
* @param AuthContextInterface $context [description] | |
* @return [type] [description] | |
*/ | |
public function supportsAuthContext(AuthContextInterface $context); | |
/** | |
* Given a request, the handler should create a new AuthContext, or return | |
* a response. | |
* | |
* @param Request $req The request to authenticate. | |
* @param array $ops Optional array of parameters. | |
* @return AuthContextInterface|Response | |
* @throws AbstractAuthException | |
*/ | |
public function createAuthContext(Request $req, $ops = []); | |
/** | |
* The the request contains a session, and there already is an AuthContext | |
* stored in the session, the handler must refresh it to ensure that it is | |
* still valid. | |
* | |
* @param Request $req [description] | |
* @param AuthContextInterface $context [description] | |
* @param array $ops [description] | |
* @return AuthContextInterface|Response [description] | |
* @throws AbstractAuthException | |
*/ | |
public function refreshAuthContext(Request $req, AuthContextInterface $context, array $ops = []); | |
} | |
/** | |
* Base interface for AuthContext's. An AuthContext describes the manner | |
* in which authentication occured. | |
* | |
* Specific handlers would provide their own extensions of the AuthenticationContext that | |
* are relevant to them. For example, an ApiKeyAuthHandler would likely have extra | |
* methods for retrieving the specific ApiKey used. | |
* | |
* Applications could extend the AuthContexts as far as having their User representations | |
* stored in them as well - or by registering listeners to derive the User during Auth events. | |
*/ | |
interface AuthContextInterface | |
{ | |
/** | |
* Some forms of Authentication are stateless, for example API keys. If stateless, the | |
* Firewall will not store AuthContext in the Session. | |
* | |
* @return boolean | |
*/ | |
public function isStateless(); | |
/** | |
* In Authentication cases that involve multiple requests, an AuthContext could be | |
* created, even though Authentication may not be complete. The firewall will not | |
* fire any authentication events unless this method returns true. | |
* | |
* @return boolean [description] | |
*/ | |
public function isAuthenticated(); | |
/** | |
* If the AuthContext is not stateless and will be stored in the session, the Firewall will | |
* call this method to ensure that sensitive data is not stored. For example, if the AuthContext | |
* contains user passwords, these should be erased for security reasons. | |
*/ | |
public function eraseSensitiveData(); | |
public function getSession(); | |
public function setSession(SessionInterface $session = null); | |
public function getRequest(); | |
public function setRequest(Request $request); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\Response; | |
use Symfony\Component\HttpFoundation\RequestMatcher; | |
use AC\Authentication\Firewall; | |
use AC\Authentication\FirewallEvents; | |
use AC\Authentication\ApiKey\ApiKeyAuthenticator; | |
use AC\Authentication\HttpBasic\HttpBasicAuthenticator; | |
$firewall = new Firewall(); | |
$firewall->registerAuthenticator('api_key', new ApiKeyAuthenticator(/*... deps ...*/)); | |
$firewall->matchRequest(new RequestMatcher('^/api/v1/resources'),'api_key'); | |
$firewall->matchRequest(new RequestMatcher('^/api/v1/reslations'), 'api_key'); | |
$firewall->registerAuthenticator('http_basic', new HttpBasicAuthenticator(/*...deps...*/)); | |
$firewall->matchRequest(new RequestMatcher('^/clients'),'http_basic'); | |
//one way of getting the current "user" | |
$firewall->getDispatcher()->addListener(FirewallEvents::AUTHENTICATED, function(FirwallEvent $e) { | |
$context = $e->getAuthContext(); | |
//derive user from $context (if it's not already stored there) | |
//set user service in whatever framework being used | |
}); | |
//handle auth errors | |
$firewall->getDispatcher()->addListener(FirewallEvents::EXCEPTION, function(FirewallEvent $e) { | |
$e->setResponse(new Response(401, 'Authentication Required.')); | |
}); | |
//authenticate the request | |
$result = $firewall->authenticate(Request::createFromGlobals()); | |
if ($result instanceof Response) { | |
$result->send(); | |
exit(); | |
} | |
//otherwise, we have an AuthContext, do your app stuff |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment