3

I have a server sided API developed with Symfony2 and now I try to authenticate.

  1. The Client / The Mobile Device App must authentificate with an API Key
  2. The User who uses the app must authentificate with Email + Password and gets an access_token

Therefore I use this firewall and apikey authenticator

firewalls:
    login:
        pattern:  ^/login$
        security: false

    secured_area:
        pattern: ^/
        stateless: true
        simple_preauth:
            authenticator: apikey_authenticator

Api Key Authenticator

namespace Rental\APIBundle\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\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;


class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface    {
    protected $userProvider;

    public function __construct(ApiKeyUserProvider $userProvider)
    {
        $this->userProvider = $userProvider;
    }

    public function createToken(Request $request, $providerKey)
    {

        //$apiKey = $request->query->get('apikey');
        // use test value
        $apiKey = "234234234";

        if (!$apiKey) {
            throw new BadCredentialsException('No API key found');
        }

        return new PreAuthenticatedToken(
            'anon.',
            $apiKey,
            $providerKey
        );
    }

    public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
    {
        $apiKey = $token->getCredentials();
        $username = $this->userProvider->getUsernameForApiKey($apiKey);

        if (!$username) {
            throw new AuthenticationException(
                sprintf('API Key "%s" does not exist.', $apiKey)
            );
        }

        $user = $this->userProvider->loadUserByUsername($username);

        return new PreAuthenticatedToken(
            $user,
            $apiKey,
            $providerKey,
            $user->getRoles()
        );
    }

    public function supportsToken(TokenInterface $token, $providerKey)
    {
        return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
    }
}

Up to this point, no problem. Now this class uses methods of the following class

namespace Rental\APIBundle\Security;

use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\User;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;

class ApiKeyUserProvider implements UserProviderInterface {
    public function getUsernameForApiKey($apiKey)
    {
        // Look up the username based on the token in the database
        // use test value
        $username = "Alex";

        return $username;
    }

    public function loadUserByUsername($username)
    {
        // return User by Username
    }

    public function refreshUser(UserInterface $user)
    {
        // code
    }

    public function supportsClass($class)
    {
        return 'Symfony\Component\Security\Core\User\User' === $class;
    }
}

My Problems in detail are:

  1. The Method loadUserByUsername needs to find the User entity by searching the Username. But from this Class I do not have access to database. I found examples using a static method User::find() but there is not such a method and the Entity - the model of MVC- does also not have access to database. How do I get the User out of the database ?
  2. I want to authentificate first the APP itself for general API access and second the User for personal information and limited editing rights. When the User logged in via Email and Password an the data was saved eg in UsernamePasswortToken how does the next call from the same Client can access data with the access_token. The session does not have effect on these AJAX HTTP Requests.
alex
  • 829
  • 11
  • 16

1 Answers1

2

1. You should use Dependency Injection for it and inject entity manager in your provider.

 your_api_key_user_provider:
            class:     Rental\APIBundle\Security\ApiKeyUserProvider
            arguments: ["@doctrine.orm.entity_manager"]
 apikey_authenticator:
            class:     Rental\APIBundle\Security\ApiKeyAuthenticator
            arguments: [""@your_api_key_user_provider"]  

After that add it in provider:

    use Doctrine\ORM\EntityManager;

    class ApiKeyUserProvider implements UserProviderInterface {

        protected $em;

        public function __construct(EntityManager $em){
            $this->em = $entityManager;
        }
       //... Now you have access to database

    }

2. Ajax can send cookies and php can work with these requests like with normal and use sessions. Make sure if your request is sending cookies(see Why is jquery's .ajax() method not sending my session cookie?)

Community
  • 1
  • 1
Artem Zhuravlev
  • 778
  • 6
  • 12