I've got a issue creating a login form to authenticate users. I followed exactly the example in this page: http://silex.sensiolabs.org/doc/providers/security.html#defining-more-than-one-firewall, but I've got a redirect loop when I try to access my site.
I'd like to secure my entire website, so I wrote this lines:
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'login' => array(
'pattern' => '^/login$',
),
'secured' => array(
'pattern' => '^.*$',
'form' => array('login_path' => '/login', 'check_path' => '/login_check'),
'logout' => array('logout_path' => '/logout'),
'users' => array(
// password is foo
'user1' => array('ROLE_USER', '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='),
),
),
),
));
$app->mount('/login', include '../src/login.php');
Then I created a login.php file:
$controllers->get('/', function(Silex\Application $app, Request $request) {
return $app->render('login.html.twig', array(
'error' => $app['security.last_error']($request),
'last_username' => $app['session']->get('_security.last_username'),
));
});
When I try to go to my homepage http://localhost I've got a redirect loop message by the browser.
Where am I wrong?
Thanks all!
Ok I spotted the issue: it's on the mount behaviour with /login path. I had to change those lines in:
$app->mount('/', include '../src/login.php');
And in the login.php:
$controllers->get('/login', ...
Related
i need to setup my silex firewall like:
www.mysite.com/* => access to all users
www.mysite.com/admin/* => access to only logged in users
i use this set up but it does not work as expected:
$app->register(new SecurityServiceProvider(), array(
'security.firewalls' => array(
'secure' => [
'pattern' => '^/.*$',
'anonymous' => true,
'form' => array(
'login_path' => '/admin/login',
'check_path' => '/admin/auth'
),
'logout' => array(
'logout_path' => '/admin/logout'
),
'users' => $app->share(function() use ($app) {
return new AuthenticationSuccessHandler($app['db']);
}),
]
),
'security.access_rules' => array(
array('^/admin$', 'ROLE_ADMIN')
)
));
Any help?
Many Thanks!! ;-)
'users' => $app->share(function() use ($app) {
return new AuthenticationSuccessHandler($app['db']);
}),
The above function needs to return an object which implements
Symfony\Component\Security\Core\User\UserProviderInterface
Check here for custom user provider documentation
It may also be appropriate to move login_path outside the secured area. Another way of configuring would be:
$app['security.firewalls'] = array(
'secure' => array(
'pattern' => '^/admin/',
'form' => array('login_path' => '/login', 'check_path' => '/admin/auth'),
'users' => $app->share(function () use ($app) {
return new MyUserProvider($app['db']);
}),
),
),
);
$app['security.access_rules'] = array(
array('^/admin', 'ROLE_ADMIN')
);
Make sure you register doctrine dbal.
I got problem: I want to allow both users and anonymous view website, and allow only users to take certain actions ( which i got covered). The thing is that certain paths ( /account etc) should be accessible only to logged users.
I tried really hard to configure my secure.php but, either anonymous can access /account or I can't access logged user anywhere except /account/...
tried both :
$app['security.firewalls'] = array(
'secured' => array(
'pattern' => '/account',
'form' => array('login_path' => '/login', 'check_path' => '/account/login_check'),
'logout' => array('logout_path' => '/account/logout', 'invalidate_session' => true),
'users' => $app->share(function () use ($app) {
return new UserProvider($app['db']);
}),
),
'unsecured' => array(
'pattern'=> '/',
'anonymous' => true,
)
);
and
$app['security.firewalls'] = array(
'secured' => array(
'pattern' => '/account',
'anonymous'=> true,
'form' => array('login_path' => '/login', 'check_path' => '/account/login_check'),
'logout' => array('logout_path' => '/account/logout', 'invalidate_session' => true),
'users' => $app->share(function () use ($app) {
return new UserProvider($app['db']);
}),
),
);
You need to do that in the authorization step, so you have to configure the security.access_rules key.
You can do this with a single firewall by enabling anonymous and authenticated users in it and then, with access rules, restrict access to /accounts URIs to only allow authenticated users:
<?php
$app['security.firewalls'] = array(
'secured' => array(
'pattern' => '^.*$',
'anonymous' => true,
'form' => array('login_path' => '/login', 'check_path' => '/account/login_check'),
'logout' => array('logout_path' => '/account/logout', 'invalidate_session' => true),
'users' => $app->share(function () use ($app) {
return new UserProvider($app['db']);
}),
);
// By using authorization the access to the /account/* is protected to
// users with the ROLE_USER (you can be more creative here if you want)
// and with the second rule the whole site is allowed to non authenticated
// users (remember the /login path must not be protected!)
$app['security.access_rules'] = array(
// this could be also array('^/account', 'ROLE_USER')
array('^/account', 'IS_AUTHENTICATED_FULLY'),
array('^.*$', 'IS_AUTHENTICATED_ANONYMOUSLY')
);
See Symfony doc for more information on authorization. Also if you want to know more about access control without roles check this out
The Easiest way is to set a session in the page headers.
if(!isset($_SESSION["logged_in"])){
header("Location: http://www.example.com/");
}
This is quite primitive - Have you thought about using a MVC framework? Would save you a of a lot of time.
Why not create a Controller?
When user access unauthorized url in my application, CakePHP execute too many redirects.
I don't know why.
I try set the parameters unauthorizedRedirect and redirectUrl, but doesn't work.
AppController.php
public $components = array(
'DebugKit.Toolbar',
'Session',
'Acl',
'Auth' => array(
'unauthorizedRedirect ' => false,
'loginAction' => array('controller' => 'users', 'action' => 'login'),
'authenticate' => array(
'Form' => array(
'userModel' => 'User',
'fields' => array('username' => 'nickname', 'password' => 'password_hash')
),
),
'authorize' => array(
'Actions' => array('actionPath' => 'controllers/')
)
// 'authError' => 'This error shows up with the user tries to access a part of the website that is protected',
)
);
Change this
"actionPath" => "controllers/"
into this
"actionPath" => "Controllers/"
I'm quite sure that you are on a case sensitive OS.
Another thing to setup it's the "loginRedirect" and the "logoutRedirect" statements: at the moment, if you login into the users/login action you will be redirected to the same action again and again. For a testing purpose I'd recommend you to set both of them to the root just adding this to your code:
'loginRedirect' => '/',
'logoutRedirect' => '/'
firstly check that is users/login action can display content to unauthorized user ? Use $this->Auth->allow(array('login', 'logout') in user controller. If you use Acl and Action authorize, check that anonymus has permission to see this user/login page.
I use PHP and Silex to build a web app and I implemented basic authentication via SecurityServiceProvider like this:
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'private' => array(
'remember_me' => array(
'key' => $config['secret_key'],
'lifetime' => $config['remember_me_duration'],
),
'pattern' => '^/admin',
'form' => array('login_path' => '/login', 'check_path' => '/admin/login_check'),
'logout' => array('logout_path' => '/admin/logout'),
'users' => $app->share(function () use ($app) {
// ...
}),
),
'public' => array(
'pattern' => '^/$',
'anonymous' => true,
),
'login' => array(
'pattern' => '^/login$',
'anonymous' => true,
),
),
));
As you can see the /admin paths are secured, there I can use $app['security']->getToken()->getUser(); to get the actual user that is authenticated and display something like Logged in as $username, but if I do this on the /login or / routes the user will always be anon. even if I'm authenticated.
How can I get the authenticated user, if there is one, otherwise the anon. string, on the / and /login routes in order to display a message like: Logged in as $username ?
I also tried to use is_granted('IS_AUTHENTICATED_FULLY' function in the twig templates to check if the user is authenticated, but on /login and / it returns false (even if I'm authenticated) since the anonymous user takes precedence over the authenticated ones, so no success here.
In order to help other people, I solved the issue as described below.
Silex doesn't use access_control, but access_rules (dammit).
As Pazi (+1ed) suggested I combined everything under a single firewall and used access_rules:
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'main' => array(
'remember_me' => array(
'key' => $config['secret_key'],
'lifetime' => $config['remember_me_duration'],
),
'pattern' => '^/',
'anonymous' => true,
'form' => array(
'login_path' => '/login',
'check_path' => '/admin/login_check',
),
'logout' => array('logout_path' => '/admin/logout'),
'users' => $app->share(function () use ($app) {
// ...
}),
),
),
'security.access_rules' => array(array('^/admin/files', 'ROLE_ADMIN'),
),
));
Everything must be under the same firewall and you have to use access control.
#Paul
I'd prefer to add the comment but I can't do it yet, so as an answer than:
It looks like taking out
'pattern' => '^/admin'
string from the firewall pattern you have opened anonymous access to the /admin/login_check, and this is not right.
My suggestion is to take it back and remove
'anonymous' => 'true'
line from the firewall. Should work the same but would be more secured.
I'm currently building a web application with Silex and just started implementing the SecurityServiceProvider.
I added the following snippet to my bootstrapping code:
$app->register(new Silex\Provider\SecurityServiceProvider(), array(
'security.firewalls' => array(
'login' => array(
'pattern' => '^/login$',
'security' => false,
),
'secured' => array(
'pattern' => '^.*$',
'anonymous' => true,
'form' => array('login_path' => '/login', 'check_path' => '/login/check'),
'logout' => array('logout_path', '/logout'),
'users' => $app['custom.user_provider'],
'switch_user' => array('parameter' => '_switch_user', 'role' => 'ROLE_IMPERSONATE'),
),
),
'security.encoder.digest' => $app->share(function ($app) {
return new MySQLPasswordEncoder(false);
}),
));
$app['security.role_hierarchy'] = array(
'ROLE_ADMIN' => array('ROLE_STAFF', 'ROLE_BAN_MGR', 'ROLE_IMPERSONATE'),
'ROLE_STAFF' => array('ROLE_USER'),
);
$app['security.access_rules'] = array(
array('^/admin/bans/.*$', 'ROLE_BAN_MGR'),
array('^/admin/.*$', 'ROLE_STAFF'),
array('^/account/.*$', 'ROLE_USER'),
array('^.*$', ''),
);
I want to be able to use the security context (i.e. is_granted(...) in templates) on any page, therefore I use 'pattern' => '^.*$', 'anonymous' => true.
To match the requirement for login_path to be outside the secured area, I added the login firewall.
Now, the problem is, that the security context is not available on the /login page, hence is_granted(...) throws an exception:
AuthenticationCredentialsNotFoundException: The security context contains no authentication token. One possible reason may be that there is no firewall configured for this URL.
I tried adding 'security' => true, 'anonymous' => true to the login firewall, but this leads to inifinite redirections (because /login is inside a secured area).
Question: How can I make the security context available on the login page (which, by definition, may not be secured)?
remove the first firewall
'login' => array(
'pattern' => '^/login$',
'security' => false,
),
, it is unecessary
and change the last access rule to this
array('^/login','IS_AUTHENTICATED_ANONYMOUSLY'),
more details here , the symfony doc is where you should spend most of your time when you have a question regarding silex :
http://symfony.com/doc/current/book/security.html