Check if the user is authenticated on not secured route - silex

I'm trying to build a simple login using symofny/security package in Silex, but I have a small problem with authentication checking.
The structure:
/
/login
/admin
/login_check
/logout
In order to get to the /admin route user needs to be authenticated, if he's not, he gets redirected to /login form, which then, as recommended, is asking admin/login_check for authentication (that's all provided by symofny's security firewalls).
The firewalls configuration:
'security.firewalls' => array(
'login' => array(
'pattern' => '^/login$',
),
'admin' => array(
'pattern' => '^/admin',
'http' => true,
'form' => array(
'login_path' => '/login',
'check_path' => '/admin/login'
),
'logout' => array(
'logout_path' => '/admin/logout',
'invalidate_session' => true
),
'users' => ...
),
)
Everything works fine, but the user can enter the /login route even-though he's already authenticated, which is not that bad, but I'd like to avoid it. I tired to check the user authentication status, but I guess it does not work as I'm checking it on /login controller, which is not in "secured" area of the website.
Here's the code:
public function index(Request $request, Application $app)
{
if ($app['security.authorization_checker']->isGranted('ROLE_ADMIN')) {
return $app->redirect($app['url_generator']->generate('admin'));
}
return $app['twig']->render('login/index.twig', array(
'error' => $app['security.last_error']($request),
'last_username' => $app['session']->get('_security.last_username'),
));
}
That throws an error: The token storage contains no authentication token. One possible reason may be that there is no firewall configured for this URL. So, the question, is there any way to do this using symfony/security natively (without any walk-arounds)? or is it somehow possible to create the /login route inside secured area with possibility to access it even-though the user is not logged in (e.g. make an exception for GET request) which would solve the problem?
UPDATE
I've also added the firewall configuration for /login page with an option anonymous: true, which resulted in not throwing an error anymore, but yet, when I'm logged in on the /admin route, method isGranted('ROLE_ADMIN') results in true, whereas on /login route it results in false (I'm still logged in there).

You can easily understand the behavior of the security component by dumping the current token.
public function index(Request $request, Application $app)
{
var_dump($application['security.token_storage']->getToken());
}
When you don't set anonymous option for login page (by default it is false):
null
When you set anonymous option for login page to true:
object(Symfony\Component\Security\Core\Authentication\Token\AnonymousToken)
private 'secret' => string 'login' (length=5)
private 'user' (Symfony\Component\Security\Core\Authentication\Token\AbstractToken) => string 'anon.' (length=5)
private 'roles' (Symfony\Component\Security\Core\Authentication\Token\AbstractToken) =>
array (size=0)
empty
private 'authenticated' (Symfony\Component\Security\Core\Authentication\Token\AbstractToken) => boolean true
private 'attributes' (Symfony\Component\Security\Core\Authentication\Token\AbstractToken) =>
array (size=0)
empty
Following example describes why you are getting error in your initial code example.
How to share security token?
To share security token between multiple firewalls you have to set the same Firewall Context.
SecurityServiceProvider registers context listener for protected firewall you have declared with pattern security.context_listener.<name>. In your example it is registered as security.context_listener.admin. Thus context you want to use is named admin.
Also a firewall context key is stored in session, so every firewall using it must set its stateless option to false.
What is more, to invoke authentication on login page the anonymous option must be set to true
'security.firewalls' => array(
'login' => array(
'context' => 'admin',
'stateless' => false,
'anonymous' => true,
'pattern' => '^/login$',
),
'admin' => array(
'context' => 'admin',
'stateless' => false,
'pattern' => '^/admin',
'http' => true,
'form' => array(
'login_path' => '/login',
'check_path' => '/admin/login'
),
'logout' => array(
'logout_path' => '/admin/logout',
'invalidate_session' => true
),
'users' => ...
),
)
After the change, if you are logged in on admin panel and login page you will get an instance of Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken as a token and
Symfony\Component\Security\Core\Authentication\Token\AnonymousToken if you are not logged in.
Because of that, you can safely check permission using $app['security.authorization_checker']->isGranted('ROLE_ADMIN') and get the same results on both firewalls.

Related

omniauth-saml nil values in info hash

I'm working with devise and omniauth-saml, following the instructions from https://github.com/PracticallyGreen/omniauth-saml and the omniauth Facebook example https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview
I have a simplesamlphp server running, using the metadata provided by omniauth, and everything seems to be connecting properly, but the response from the simplesaml server has nil values for email, first_name, last_name and name. The weird thing is that it is returning the email and name values, just not in a useful part of the response (see below - super#man.com is the email, Clark Kent is the name)
The weird thing is that I get these nil values with both omniauth-saml and devise-saml-authenticatable gems, but I think my simplesamlphp server is configured properly.
The response from omniauth looks like this:
#<OmniAuth::AuthHash credentials=#<OmniAuth::AuthHash> extra=#<OmniAuth::AuthHash raw_info=#<OneLogin::RubySaml::Attributes:0x007fc695850148 #attributes={"urn:oid:0.9.2342.19200300.100.1.1"=>["super#man.com"], "urn:oid:2.16.840.1.113730.3.1.241"=>["Clark Kent"], "fingerprint"=>"FINGERPRINT REMOVED"}>> info=#<OmniAuth::AuthHash::InfoHash email=nil first_name=nil last_name=nil name=nil> provider="saml" uid="_ca76f49ccb6ccce7827111ae1ff0563f534f0a4d1a">
The simplesamlphp config looks like this:
$metadata['http://localhost:3000/saml/users/auth/saml/metadata'] = array(
'AssertionConsumerService' => 'http://localhost:3000/saml/users/auth/saml/callback',
'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:email',
'simplesaml.nameidattribute' => 'email',
);
I am sure that there are good values for the user in the database, and when I test authentication on the IDP itself, everything works as expected.
I solved this. The problem was that I was trying to use values that didn't use the ldap attribute names.
I changed my sp metadata as follows (after modifying my model to use first_name and last_name instead of just name)
$metadata['http://localhost:3000/saml/users/auth/saml/metadata'] = array(
'AssertionConsumerService' => 'http://localhost:3000/saml/users/auth/saml/callback',
'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:email',
'authproc' => array(
95 => array(
'class' => 'core:AttributeMap',
'givenName' => 'first_name',
'sn' => 'last_name',
'mail' => 'email',
),
96 => array(
'class' => 'core:AttributeLimit',
'email', 'first_name', 'last_name'
),
),
'simplesaml.nameidattribute' => 'email',
);

Silex : The token storage contains no authentication token

When trying to check whether the user is authenticated or not in layout
{% if is_granted('IS_AUTHENTICATED_FULLY') %}
<p>Username: {{ app.user.username }}</p>
{% endif %}
I am getting an error as
Twig_Error_Runtime in Template.php line 304:
An exception has been thrown during the rendering of a template ("The token storage contains no authentication token. One possible reason may be that there is no firewall configured for this URL.") in "layout.html" at line 39.
This is the configuration of the security firewall. I need to only allow logged in users to access the website.
$app->register(new SecurityServiceProvider(), array(
'security.firewalls' => array(
'dev' => array(
'pattern' => '^/(_(profiler|wdt)|css|images|js)/',
'security' => false
),
'login' => array(
'pattern' => '^/login$',
),
'secured' => array(
'pattern' => '^.*$',
'form' => array('login_path' => '/login', 'check_path' => '/login_check'),
'logout' => array('logout_path' => '/logout'),
'users' => $app->share(function() use ($app) {
// Specific class App\User\UserProvider is described below
return new App\User\UserProvider($app['db']);
}),
),
'unsecured' => array(
'anonymous' => true,
)
),
'security.access_rules' => array(
// You can rename ROLE_USER as you wish
array('^/.+$', 'ROLE_USER'),
array('^/login$', 'SS'), // This url is available as anonymous user
)
));
Any ideas to fix this is welcome.
Thank you
Since the error message says that the error happens in layout.html, I'm guessing it is used on every page even the ones like /login that is not behind a firewall. The error is caused by calling is_granted when not behind a firewall.
So there are a few options:
Use a separate layout for login page that does not call is_granted
Check if there is an existing security token before calling is_granted
Option 1 should be obvious so not going into more detail with that.
With option 2, you can do something like this to check for existing security token:
{% if app.security.token is not null and is_granted('IS_AUTHENTICATED_FULLY') %}

BjyAuthorize: Parent Role id "user" does not exist

I'm having a Parent Role id "user" does not exist error
What I'm trying to get BjyAuthorize to work. In my config, I have
'bjyauthorize' => array(
// Using the authentication identity provider, which basically reads the roles from the auth service's identity
'identity_provider' => 'BjyAuthorize\Provider\Identity\AuthenticationIdentityProvider',
'resource_providers' => [
'BjyAuthorizeProviderResourceConfig' => [
'user' => [],
],
],
'role_providers' => array(
// using an object repository (entity repository) to load all roles into our ACL
'BjyAuthorize\Provider\Role\ObjectRepositoryProvider' => array(
'object_manager' => 'doctrine.entity_manager.orm_default',
'role_entity_class' => 'SorgcaUser\Entity\Role',
),
'BjyAuthorizeProviderResourceConfig' => array(
'allow' => array(
array('user', 'admin', 'manage'),
),
),
),
),
In my controller I would simply call $this->isAllowed('admin', 'manage');
Now, calling isAllowed function will throw the error saying my Parent Role id does not exist
But, in my user_role table, I have
I'm not really sure how to begin debugging. Please advice.

hybridauth's social_hub/login.php doesn't work

I install the hybridauth on windows IIS, and setup the hybridauth\config.php as below:
array(
"base_url" => "http:///hybridauth/",
"providers" => array (
.....
"Google" => array (
"enabled" => true,
"keys" => array ( "id" => "<myappkey>", "secret" => "<myappsecret>" ),
"scope" => "email"
),
)
But when I click "Sign-in with Google" in http:///examples/social_hub/login.php, it just redirect me to http:///hybridauth/?hauth.start=Google&hauth.time=1401608000 which show the files under "localhost - /hybridauth/"
Anyone know how to fix it?
Thank you!
1) i am not sure if your scope is properly set up (you should have probably set it to "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email"
2) did you set up the return address in google api correct?

SessionManager Timeout/Logout

I'm currently using the examples found here http://zf2.readthedocs.org/en/latest/modules/zend.session.manager.html
In my autoload/global.php file I have the following:
'session' => array(
'config' => array(
'class' => 'Zend\Session\Config\SessionConfig',
'options' => array(
'name' => 'otwebsoft',
'save_path' => __DIR__ . '/../../data/session',
'use_cookies' => true,
'cookie_lifetime' => 3600,
'cookie_httponly' => true,
'cookie_secure' => false,
'remember_me_seconds' => 1800
)
),
'storage' => 'Zend\Session\Storage\SessionArrayStorage',
'validators' => array(
array(
'Zend\Session\Validator\RemoteAddr',
'Zend\Session\Validator\HttpUserAgent'
)
)
)
I like how that the session expires after X amount of time and if the user was logged in, upon a page refresh the user is then automagically logged out and they must login again. However, I would like it so that if the user continues to surf the site, after X amount of time the session isn't killed off but perhaps just regenerated or something so that they are not forced to log back in again.
I am just not certain how or if this is possible to do both scenarios. If this is not possible, I could just raise the X time for session lifetime and live with that.
Does anyone have any ideas I could try?
Set cookie lifetime to 0:
'cookie_lifetime' => 0
it will tell the browser to kill the cookie only after the browser is closed.
And define the server session storage lifetime:
'gc_maxlifetime' => 3600
So after 1 hour of inactivity the session storage will be cleaned up.

Resources