Silex koppelt een request aan deze url en stuurt de request naar de juist controller en daar wordt alles afgehandeld tot er uiteindelijk een JSON-string terug gestuurd wordt.
Nu zou ik in die controller eerst kunnen checken of de token valid is. Maar aangezien ik veel url's heb en later ook gebruik wil maken van "Roles en rights" wil ik dit automatisch via een firewall van de SecurityServiceProvider laten lopen.
Wat deze code doet, is alle urls van /members opvangen dus onze call van hierboven heeft ook een match met deze firewall. Momenteel staat de "authentication" ingesteld op http maar ik wil eigenlijk mijn GET parameter "token" gebruiken. Laat ons zeggen dat dit heel erg lijkt op wat ik wil bereiken maar ik weet niet hoe ik dit in deze firewall kan implementeren.
Ik heb ook al gekeken naar een "form" oplossing i.d.p.v. de http oplossing maar dat wil zeggen dat ik een url /authentication moet maken waar ik een controller aankoppelt die de authentication voor zich neemt en ik weet niet of dat wel de bedoeling is van het firewall concept.
Hoe beveilig ik mijn API via de firewall van Symfony in Silex?
ik heb inmiddels de FOSUserBundle en de FOSRestBundle opgezet en werkend gekregen. Ik heb ook de api key authentication geïmplementeerd en aangepast en alles werkt al maar nog niet zoals ik het wil.
<?php
namespace Jds\UserBundle\Security;
use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\HttpFoundation\Response;
class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface, AuthenticationFailureHandlerInterface
{
protected $userProvider;
protected $encoder;
public function __construct($userProvider, $encoder)
{
$this->userProvider = $userProvider;
$this->encoder = $encoder;
}
public function createToken(Request $request, $providerKey)
{
// look for an credentials query parameter
$credentials = array(
'username' => $request->query->get('username'),
'password' => $request->query->get('password')
);
// or if you want to use an "credentials" header, then do something like this:
// $credentials = $request->headers->get('credentials');
if (count(array_filter($credentials)) != 2) {
throw new BadCredentialsException('No API key found');
}
return new PreAuthenticatedToken(
'anon.',
$credentials,
$providerKey
);
}
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
{
$credentials = $token->getCredentials();
try {
$user = $this->userProvider->loadUserByUsername($credentials['username']);
}
catch(UsernameNotFoundException $e) {
throw new AuthenticationException(
sprintf('User "%s" does not exist.', $credentials['username'])
);
}
$encoder = $this->encoder->getEncoder($user);
$encoded_pass = $encoder->encodePassword($credentials['password'], $user->getSalt());
if($user->getPassword() == $encoded_pass) {
return new PreAuthenticatedToken(
$user,
$credentials,
$providerKey,
$user->getRoles()
);
} else {
throw new AuthenticationException(
sprintf('User "%s" does not exist.', $credentials['username'])
);
}
}
public function supportsToken(TokenInterface $token, $providerKey)
{
return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
//return new Response("Authentication Failed.", 403);*/
throw new AuthenticationException('Invalid username or password');
}
}
?>
Zoals jullie bij de URL's kunnen zien moet GET api/v1/activities.json voor iedereen toegankelijk zijn zonder credentials en dat heb ik in security.yml opgelost door de public_api firewall.
Dat is nog niet de oplossing die ik wens, ik wil indien er geen credentials aanwezig zijn enkel veld1 veld2 en veld 3 terugsturen en indien er wel valid credentials zijn ook nog veld4 en veld5 meesturen maar dat is momenteel op geen enkele manier mogelijk.
Volgens mij loopt het mis bij de onfail functie. Daar maak ik een exception aan wat perfect werk voor alle url's die wel sowieso een authentication nodig hebben. Dit omzeilen door een firewall voor get activities aan te maken werkt maar dan is er totaal geen authentication meer mogelijk omdat de credentials niet meer gelezen worden door de ApiKeyAuthenticator.