I have a question to the security features of Symfony2. I want to protect a special area of my application under the /my prefix.
My configuration looks like follows:
security.config:
providers:
my:
entity: { class: MyUserBundle:User, property: username }
firewalls:
public:
pattern: /my/login.*
security: false
my:
pattern: /my.*
form-login:
check_path: /my/login_check
login_path: /my/login
logout: true
access_control:
- { path: /my/login.*, roles: IS_AUTHENTICATED_ANONYMOUSLY }
When I try to access the login area, everything works fine, submitting the form leads to an error page, because there is no registered controller for the _security_check route, like its described in the guide:
_security_login:
pattern: /my/login
defaults: { _controller: MyUserBundle:Auth:login }
_security_check:
pattern: /my/login_check
I think normally the SecurityBundle hacks into this process so that no controller is needed. The configuration of Symfony2 is allways very complex.
I think I missed something, hope you can help.
Thanks in advance!
I solve the problem with the help of the symfony users mailing group.
You have to define one firewall (that describes all routes) and determine secure zone using access_control part of settings.
Related
I have two firewalls for auth and api. But i want to combine them and be able to check if there is some user in controller.
firewalls:
user:
pattern: ^/api/auth
stateless: true
anonymous: true
json_login:
check_path: /api/auth/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
require_previous_session: false
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
access_control:
- { path: ^/api/auth, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
What I want to do is to combine firewalls to one and allow any user everythere, but still be available to identify user if JWT passwed.
Is it possible?
In my opinion,to achieve this you will need only the firewall matching ^/api pattern and continue using guard authenticator. Then, inside your authenticator, check for the exact route requested and chose whether to continue with normal guard authenticator flow or use a custom logic to implement json login. But it is a little bit dirty even if guard permits you to implement your own logic. Keeping separated firewalls sounds better.
I think it is better to keep separate firewalls, as mentioned already, but in case you absolutely need to keep your application open to everybody and check if the user is authenticated in your controller, you can refer to symfony's documentation and use something like:
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
I'm trying to build a custom firewall for my Symfony3 website. I've been following the documentation, and was able to get it to work for the main firewall. My desired functionality is the ability for a user to login with their username and password, using Symfony's native classes. Here is my SecurityController:
namespace AppBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class SecurityController extends Controller
{
/**
* #Route("/m/login", name="model_login")
*/
public function loginAction(Request $request)
{
$authenticationUtils = $this->get("security.authentication_utils");
$error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('Model/login.html.twig', [
"error" => $error,
"lastUsername" => $lastUsername
]);
}
}
As you can see, it is exactly the same as the sample code, except with the routing settings changed. My login form renders fine. I get no errors when I submit the form, and I have my form POSTing to this exact controller. My view works perfectly as I am able to authenticate when my security settings are under "main." Here is my security.yml:
security:
providers:
in_memory:
memory: ~
doctrine_provider:
entity:
class: AppBundle:Model
property: username
encoders:
AppBundle\Entity\Model:
algorithm: bcrypt
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
model_area:
anonymous: ~
provider: doctrine_provider
pattern: ^/m/
form_login:
login_path: model_login
check_path: model_login
access_control:
- { path: ^/m/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/m/, roles: ROLE_MODEL }
My providers and encoders function properly, as exemplified by my success when authenticating under the "main" firewall. However, when I attempt to put my settings under "model_area," submitting my form just redirects me to the login form, with no authentication. I only added the pattern: ^/m/ and - { path: ^/m/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } so I do not block access to my login form. Everything else remained the same (even the route names!).
I have a route /m/model_dashboard, which throws an error, saying "full authentication is required to access this resource." So clearly my access control settings are working properly, however, it is not redirecting to my login form when I attempt to access a protected resource.
Is there something I'm missing? I'm extremely confused as to why the authentication would work under main but not under my custom firewall, which had the same exact settings.
The problem was my main firewall. I did not know that firewalls are determined similarly to routes, top to bottom. Every request was being filed under the "main" firewall (which has no form_login), which was why my login code wasn't working. I removed the main firewall and it is working beautifully. Here is what my updated security.yml looks like now:
security:
providers:
in_memory:
memory: ~
doctrine_provider:
entity:
class: AppBundle:Model
property: username
encoders:
AppBundle\Entity\Model:
algorithm: bcrypt
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
model_area:
anonymous: ~
provider: doctrine_provider
pattern: ^/m/
form_login:
login_path: model_login
check_path: model_login
access_control:
- { path: ^/m/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/m/, roles: ROLE_MODEL }
I am developing a backend service for my own project with Symfony2. What i would like to do is simple user registration. Whenever a user needs to be created there will be a POST call to
/v1.0/users (with POST method)
I would like to create a new user. For all of the other url should be authenticated except this one. So I created UserProvider and UserAuthenticator as described in here : http://symfony.com/doc/current/cookbook/security/api_key_authentication.html
I created a secured area and it works fine, but i want to disable this firewal for the url above with post method. I couldnt figure it out how. Here is my security.yml file
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api_user_secured_area:
pattern: ^/v1.0/users
stateless: true
simple_preauth:
authenticator: user_token_authenticator
provider: user_token_provider
access_control:
...
user_register:
path: /v1.0/users
roles: IS_AUTHENTICATED_ANONYMOUSLY ?? FOR POST ONLY ??
PS: I dont want to use annotation for security (like #Security in the controller)
Access control can be filtered to a given METHOD using the Methods property, please see here for more filters/options regarding access control:
http://symfony.com/doc/current/cookbook/security/access_control.html
Here is the option integrated into your code:
access_control:
user_register:
path: /v1.0/users
methods: [ POST ]
roles: IS_AUTHENTICATED_ANONYMOUSLY
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)?
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.
Here is my configuration to only use the /login route with Symfony 3.1:
File security.yml:
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/login$
security: false
methods: [GET]
main:
pattern: ^/
form_login:
login_path: login #this use the route name and not the pattern
check_path: login_check # same here : route name
logout:
path: logout
access_control:
- { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY, methods: [GET] }
- { path: ^/, roles: ROLE_USER}
File routing.yml:
login_check:
path: /login
methods: [POST]
logout:
path: /logout
Controller:
/**
* #Route("/login", name="login")
* #Method({"GET"})
*/
public function loginAction(Request $request)
{
And this is working for me:
/login with GET gives me the controller.
/login with POST to submit the form.
In the Book for symfony2 on pages 156-157 There is a nice tutorial for how to make your own login system using forms.
When I follow these steps, I am faced with a "No route found for "POST /login_check"" error message.
security.yml:
secured_area:
pattern: ^/secured/
form_login:
login_path: /login
check_path: /secured/login_check
logout:
path: /secured/logout
target: /login
routing.yml:
login:
pattern: /login
defaults: { _controller: MySecurityBundle:Security:login }
login_check:
pattern: /secured/login_check
Can anyonw tell me why mine doesn't work and sensioLabs apparently does?
The only difference I can see is that I wiped out the Acme bundle and created a fresh bundle with security, copy-pasting all files as per tutorial (I thought ACME was a demo bundle and not necessary for functions such as security?).
I have no default route set for the login_check route, since tutorial says on page 156 "You will not need to implement a controller for the /login_check URL as the firewall will
automatically catch and process any form submitted to this URL."
You don't need to specify controller but you have to specify routing path, in your case it is
pattern: /secured/login_check
but you are submitting form to /login_check, edit either yor form action to include /secure or remove it from routing.yml