Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change symfony2 login_check URL

Tags:

php

symfony

I'm implementing a Symfony 2 solution according to a set specification. The specification states that the login form at GET /login must resumbit to POST /login.

How can I change the URL for the /login_check? Can I create a route that calls the security controller directly, like the following? I don't see any controller in the Security bundle.

oft_auth_login:
    pattern: /login
    defaults: { _controller: MagicSecurityBundle:Default:login_check }
    methods: [POST]

According to the documentation for security.yml,

check_path (type: string, default: /login_check) This is the route or path that your login form must submit to. The firewall will intercept any requests (POST requests only, by default) to this URL and process the submitted login credentials.

So I have to create a valid route, but I can't figure out where it's supposed to point to (firewall action? security action?) or what it's supposed to do.

I changed it to POST /login, but it said it couldn't find it...

Now I have a security.yml file that looks like this:

firewalls:
    dev:
        pattern:  ^/(_(profiler|wdt)|css|images|js)/
        security: false

    login:
        pattern:  ^/login$
        security: false

    secured_area:
        pattern:    ^/
        anonymous: ~
        form_login:
            login_path: oft_auth_login
            check_path: oft_auth_login_check
            username_parameter: user
            password_parameter: passwd

And a routing.yml that looks like this:

oft_auth_login:
    pattern: /login
    defaults: { _controller: OftAuthBundle:Default:login }
    methods: [GET]

oft_auth_login_check:
    pattern: /login
    methods: [POST]

I can load the form fine (GET /login), and the form method is POST and the action is /login, but when I submit it, I get the following error (404, route not found):

Unable to find the controller for path "/login". Maybe you forgot to add the matching route in your routing configuration? 404 Not Found - NotFoundHttpException

What code, firewall methods, etc. would I have to call to authenticate a user in a custom check_login method, or any other way to accomplish what I'm trying to do, which is to access and post the login form to the same URL (/login)?

like image 794
Nostradamnit Avatar asked Nov 10 '22 07:11

Nostradamnit


1 Answers

In your security.yml, you should see something like:

firewalls:
    dev:
        pattern:  ^/(_(profiler|wdt)|css|images|js)/
        security: false

    login:
        pattern:  ^/login$
        security: false

    main:
        pattern:    ^/
        form_login:
            check_path: /login_check
            login_path: /login
            always_use_default_target_path: true
            default_target_path: /secured

In your routing.yml, you should see something like:

## Security Routes
default:
    pattern:   /
    defaults:  { _controller: SecurityBundle:Security:login }

login:
    pattern:   /login
    defaults:  { _controller: SecurityBundle:Security:login }

login_check:
    pattern:   /login_check

logout:
    pattern: /logout

Change check_path: /login_check in security.yml to check_path: /new_login_check

Change pattern: /login_check in routing.yml to pattern: /new_login_check

That will change the URL for the login check route.

Your login form probably looks like:

<form action="{{ path('login_check') }}" method="post" class="form-horizontal">

The {{ path('login_check') }} twig snippet outputs the URL to the route identified by login_check. This is not the same as a URL. Example routing configuration (using YAML):

route_identifier:
    pattern: /relative/url/path
    defaults: {_controller: ExampleBundle:Example:test}

Using the path('route_identifier') function will route to the URL /relative/URL/path, which calls the method testAction in the ExampleController class in the ExampleBundle.

You should always use Symfony's routing where possible in your templates, so you avoid conflicts and can easily change routes.

Somewhat orthogonal to changing the login check route, but rereading your question, it says that you want to use the /login path for different actions depending on GET/POST. This requires an extra step, as Symfony will choose the first matching route available to use.

After following the above, your routing.yml might contain something like:

## Security Routes
default:
    pattern:   /
    defaults:  { _controller: SecurityBundle:Security:login }

login:
    pattern:   /login
    defaults:  { _controller: SecurityBundle:Security:login }

login_check:
    pattern:   /login

logout:
    pattern: /logout

As such, it'll only ever match the login route, never the login_check route. You'll need to specify a method as well:

## Security Routes
default:
    pattern:   /
    defaults:  { _controller: SecurityBundle:Security:login }

login:
    pattern:   /login
    defaults:  { _controller: SecurityBundle:Security:login }
    methods:   [GET]

login_check:
    pattern:   /login
    methods:   [POST]

logout:
    pattern: /logout

See the Symfony documentation on Routing for more information.

Another issue you will run into is that the login_check route must match the firewall the user is logging into, but the login route must be accessible to non-authenticated users!

From Symfony's documentation:

Next, make sure that your check_path URL (e.g. /login_check) is behind the firewall you're using for your form login (in this example, the single firewall matches all URLs, including /login_check). If /login_check doesn't match any firewall, you'll receive a Unable to find the controller for path "/login_check" exception.

This is a bit tricky when using the same URL for both actions, but it is possible. The biggest hurdle is that you can't match firewalls by an http method. The first thing you need to do, then, is make sure that both login routes only match your main firewall, by removing any firewalls that specifically match the login routes.

In the example above, that means removing this section from the security.yml:

    login:
        pattern:  ^/login$
        security: false

That'll meet the first requirement of login_check matching the firewall you're using, but unauthenticated users won't be able to reach the login route any more! That causes a redirect loop. Fortunately, there's a way around that. In your security.yml file, below the section firewalls, you should see a section called access_control. (If not, make one.) You'll need to add the following line to the top of this section. (Again, Symfony matching is conservative, quitting once it finds the first match.)

access_control:
        - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [GET] }

That will explicitly allow anonymous users to access the /login path, but only via GET.

like image 98
Kal Zekdor Avatar answered Nov 12 '22 19:11

Kal Zekdor