A
A
Alexey Skobkin2014-09-18 18:47:59
symfony
Alexey Skobkin, 2014-09-18 18:47:59

How to properly arrange custom authentication in Symfony2?

I’ll make a reservation right away that the official documentation with an example of WSSE authentication did not really clarify the essence of the issue for me.
It is necessary to make authentication through the service API method, which looks like OpenID, but is not. Authentication mechanics:
1. Checking the user's authentication and, if he is not authenticated, then give him a message asking him to go to the authentication page. This is where my controller fires, displaying the region selection, and then redirecting the user to the desired API method.
2. On the page, he will have to select one option: region. When clicking on the button of the region he needs, the user gets to call a certain Action of the controller (or is it more correct to do it inside the authentication mechanism, but then how and where to redirect the user?), where the request is made to the API of the external service of the desired region (the region is remembered) to obtain a unique link authentication, after which the user is redirected to this link, where he will have to confirm the transfer of data to the site: an access token to the API of this very external service, a username in the external service, and an ID there.It is not clear here whether it will be correct to do this in the controller, or should this also be shifted to custom authentication? If you need to transfer, then how? After clicking on the region button from the user, nothing more is required before returning with the data to the site.
3. After confirmation, the user is redirected back to the site (in the first request, the external API is given the path where to redirect the user), to the page that receives data - token, username, ID.
4. The authentication system in the Listener creates a token, writes the incoming data to it, and sends the token for verification:

protected function attemptAuthentication(Request $request)
    {
        if ($request->get($this->options['status_parameter'], null, true) == 'ok') {
            // Наполнение токена данными
            return $this->authenticationManager->authenticate($token);
        }
    }

Here, fortunately, everything is clear. Unless, I would like to clarify when the attemptAtuthentication () method is called from the Listener. If only when you go to the address that is specified in the firewall as the check address is given, then I understood everything correctly.
5. A custom authentication provider comes into play. In the authenticate() method, another method is called, which makes a request to an external service to check the validity of the token (after all, a fake username can be returned with a redirect) and, if the token is valid, a user is searched for with the username specified in the token via the userProvider:
$user = $this->userProvider->loadUserByUsername($token->getUsername());

If the user does not exist, you need to create it and set the username and ID parameter on the external service (not the internal id, which is the primary key, but an additional field that stores the external id).
if (!$user) {
    $user = new User();
    $user->setUsername($token->getUser());
    //...
    // Setting role
    //...
}

As a result, we get either a new user or one found in the database. The new token is given a user and an authentication status:
public function authenticate(TokenInterface $token)
{
    // Тут вышеописанная рутина
    $authenticatedToken->setUser($user);
    $authenticatedToken->setAuthenticated(true);
    return $authenticatedToken;
}

It is not clear here how it is possible to initiate the creation of a user in the authenticate () method of the authentication provider, because this is not a controller, and here you just can’t get the entity manager and send a new user for saving
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();

Perhaps I need to somehow pass the Doctrine service to the provider's constructor?
6. After that, in theory, somewhere you need to put the current token in the security context, and the user needs to be put somewhere so that in the future it will be transparently available via $this->getUser() in controllers. It is not clear where exactly this would be done correctly. Or will the authentication system take care of this itself?
7. Well, somehow after this process, you again need to issue your custom response in JSON stating that authentication has been passed.
PS There is now a token that seems to be implemented correctly. At least with him everything is simple and clear. There is also a Listener inherited from AbstractAuthenticationListener. The abstract class has 'login_path' in its parameters. I understand that somewhere there I need to display the region selection for the user, but since this is handled by the Security bundle, and not by my controllers, I still do not understand how to implement this. 'check_path' is, as I understand it, the page where the user should land after a redirect from a third-party service (in the case of a regular login form, the form data should arrive here, right?).
PPS Read the official documentation . There we are talking about WSSE, which has a different principle of operation. And this article- too. Thanks to them, I partially figured it out, but all of the above are gaps after reading.
I apologize for such a voluminous "question". I suspect that something else may be revealed that I did not understand, but if I figure out the mechanism described above, it will be much easier to figure it out further.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
P
PaulJE, 2014-09-23
@PaulJE

Actually, you need to make an implementation through DI, because the provider itself is described in your services? Well, inject the entity manager into it.
Get something like this:

private $_em;
public function setEntityManager(EntityManager $em) {
    $this->_em = $em;
}

public function authenticate(TokenInterface $token)
{
    // Тут вышеописанная рутина
    $this->_em->persist($user);
    $this->_em->flush();

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question