9

When having a backend for admin users, it is interesting to have a login form, and at the same time having a normal login form for normal users in the public area of our website.

Is that possible using FOSUserBundle? How can it be done "the Symfony2" way?

David Morales
  • 17,816
  • 12
  • 77
  • 105

2 Answers2

25

First we need to configure some special routes for the admin area:

admin_login:
    pattern:  /admin/login
    defaults: { _controller: FOSUserBundle:Security:login }

admin_login_check:
    pattern:  /admin/login_check
    defaults: { _controller: FOSUserBundle:Security:check }

admin_logout:
    pattern:  /admin/logout
    defaults: { _controller: FOSUserBundle:Security:logout }

Next configure a special firewall for the admin area using these routes, and define them to be anonymously accessed:

firewalls:
  ...
  admin:
    pattern:            /admin/(.*)
    form_login:
      provider:       fos_userbundle
      login_path:     admin_login
      check_path:     admin_login_check
      default_target_path: yourproject_admin_default_index
    logout:
      path:           admin_logout
      target:         admin_login
    anonymous:        true
    context:          application

  main:
    pattern: ^/
    form_login:
      provider:      fos_userbundle
      csrf_provider: form.csrf_provider
    context:         application
    ...

access_control:
  ...
  - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
  - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
  - { path: ^/admin/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY }
  - { path: ^/admin/, role: ROLE_ADMIN }

Ok! We have just separated our login system in two parts: admin and main.

Let's override the SecurityController. For that we will need to create a custom bundle which parent is FOSUserBundle (check the doc for that). In this new bundle, create the controller:

<?php

namespace YourProject\UserBundle\Controller;

use FOS\UserBundle\Controller\SecurityController as BaseController;

/**
 * {@inheritDoc}
 */
class SecurityController extends BaseController
{
    /**
     * {@inheritDoc}
     */
    public function renderLogin(array $data)
    {
        $requestAttributes = $this->container->get('request')->attributes;

        if ('admin_login' === $requestAttributes->get('_route')) {
            $template = sprintf('AdminBundle:Security:login.html.twig');
        } else {
            $template = sprintf('FOSUserBundle:Security:login.html.twig');
        }

        return $this->container->get('templating')->renderResponse($template, $data);
    }
}

That's it! Now you can write your AdminBundle:Security:login.html.twig :)

NOTE: Don't forget to use the admin routes in your admin area! (in the login form action, the logout link, etc)

David Morales
  • 17,816
  • 12
  • 77
  • 105
  • I followed all instructions you mentioned above but its not working for me. When i am invoking /admin them i am being redirected to /login instead of /admin/login – Neeraj Sep 12 '13 at 10:07
  • Can you please send invite for chat to discuss it in detail? – Neeraj Sep 12 '13 at 10:08
  • Now i am being redirected to /admin/login but getting error message "The page isn't redirecting properly" .... Any suggestion – Neeraj Sep 12 '13 at 10:19
  • Is it the cleanest way to do it ? I don't understand why there's not a feature like this in the Bundle, isn't it pretty common to need that ?! Plus, it's a bit annoying to do that for each feature (resetting password for example).. – Bonswouar Mar 25 '14 at 13:57
  • you dont need `^/admin/login_check$` in access_control, also use of `sprintf` in your `renderLogin` is unnecessary. – gondo Apr 25 '14 at 08:07
  • If you get this error "You must configure the check path to be handled by the firewall using form_login in your security firewall configuration." try using the real path as check_path, not the route name: check_path: /admin/login_check – glerendegui Nov 14 '15 at 22:39
  • `$requestAttributes->get('_route')` kept giving me the value `fos_user_security_login` here. Not sure if it is because I am doing something wrong or if something changed. I could get the target URL by doing `$this->get('session')->get( '_security.main.target_path')` – Arkounay Jul 11 '16 at 06:36
  • have edited ur response to work correctly please @David Morales – Thamer Feb 13 '18 at 01:01
1

Regarding to the approved answer, I made some adjustments in my Symfony 3.2.8 project in order to work correctly.

Instead of $requestAttributes = $this->container->get('request')->attributes; in the Security Controller, I used $requestAttributes = $this->container->get('request_stack')->getCurrentRequest();.