Closed javaguirre closed 10 years ago
I solved it in security.yml creating an excluding regex for the pattern, using the example above:
security:
...
firewalls:
secured_area:
pattern: ^/(?!login\/check)
I like the approach better also because this is a security matter, so not having to add the url inside the ApiKeyAuthentication class seems a good deal.
Hi @javaguirre!
Your solution sounds good, but I'm not totally sure it should work - but obviously, you have it working, so I'm trying to understand :). If you authenticate at /login_check
, that URL must be under your firewall, because when you set the token, you're setting it for whatever firewall you're in. But, that doesn't really matter :).
Because I think your original report may be valid. The createToken
function is called here: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Http/Firewall/SimpleFormAuthenticationListener.php#L119
And as you can see, the $token
we return is passed directly to the authenticate
method, which does require a Token object (null is not valid). So, when we return null
in the example, I think this is a mistake. Instead, we need to return some token in all cases. Unfortunately, this causes the authenticateToken
method to be called, where I think we need to return an authenticatedToken, or throw an exception (which would trigger them being asked to login).
So basically, I think you're right, but I'm not sure immediately what the proper solution is. Your solution doesn't make logical sense to me, but if you want to explain it further, it could be legitimate :).
Thanks!
I'll work on a solution, thank you for your answer. :-)
I ran up against this today, and kind of hacked my way around it by - in the case where I don't want to authenticate a particular URL - setting an empty 'api key' on the token returned by createToken
. Then having authenticateToken
throw an exception if the 'api key' is empty. Then having onAuthenticationFailure
ignore any exceptions where the 'api key' is empty. This works for my use case, but wouldn't if, say an empty api key was really an error for your application.
It would certainly be nice to have a 'canonical' way of doing this, because as noted the current advice in the docs under 'Only Authenticating for Certain URLs' doesn't work!
I believe a proposed fix for this is at symfony/symfony#11414.
Using this:
public function createToken(Request $request, $providerKey) { // set the only URL where we should look for auth information // and only return the token if we're at that URL $targetUrl = '/login/check'; if (!$this->httpUtils->checkRequestPath($request, $targetUrl)) { return; }
// ...
}
For me returns : "A Token was not found in the SecurityContext."
How i can solve this?
put on authenticateToken method:
return new PreAuthenticatedToken(
'anon.',
'',
$providerKey
);
????
@cirovargas @peterrehm has a workaround here: https://github.com/symfony/symfony/issues/11490#issuecomment-50265924
I have wrote this code http://symfony.com/doc/current/cookbook/security/api_key_authentication.html#cookbook-security-api-key-config
And now when I ran something like this: app/login/check?apiKey=mihai I receive:cNotFoundHttpException: No route found for "GET /app/login/check".
My config is: * Security / $app->register( new Silex\Provider\SecurityServiceProvider(), array( 'security.firewalls' => array( /'members-area' => array( 'pattern' => '^/app', 'anonymous' => true, 'form' => array( 'login_path' => '/app/login', 'check_path' => '/app/login/check', 'failure_path' => '/app/login', 'default_target_path' => '/app', //'always_use_default_target_path' => true, 'username_parameter' => 'username', 'password_parameter' => 'password', 'use_referer' => true ), 'logout' => array( 'logout_path' => '/app/logout', 'target' => '/app', 'invalidate_session' => true, ), 'users' => $app['user.provider'] ),*/ 'secured_area' => array( 'pattern' => '^/app', 'stateless' => false, 'anonymous' => true, 'form' => array( 'authenticator' => $app['apiKey.authenticator'] ), 'logout' => array( 'logout_path' => '/app/logout', 'target' => '/app', 'invalidate_session' => true, ), 'users' => $app['apiKey.provider'],
),
),
'security.providers' => array(
'api_key_user_provider' => array(
'id' => $app['apiKey.provider'],
),
)
)
);
Could someone help me? thanks
Hi there?
Just create a route for /app/login/check. It doesn't need to do anything - the controller won't be called, but it has to exist.
Cheers!
Hi @weaverryan , And should I do something in that route? After I have created: $app->match('/app/login/check', function() use ($app) {
}); The controller must return a response (null given). Did you forget to add a return statement somewhere in your controller?
If you submit your form, this should result in a POST request to that URL, and then the security system will intercept it (so it won't even hit your controller). If you just surf to the URL, you'll get an error - but people will only submit the login form to get here (make sure you have method=POST on your login form)
@weaverryan This part is working with the form login. But what I am trying to do is to login with http://localhost/myenms/web/app/login/check?apikey=mihai and I do not manage to do it. I have done this steps in my project: http://symfony.com/doc/current/cookbook/security/api_key_authentication.html#cookbook-security-api-key-config
And my config is:
$app->register( new Silex\Provider\SecurityServiceProvider(), array( 'security.firewalls' => array( 'members-area' => array( 'pattern' => '^/app', 'anonymous' => true, 'form' => array( 'login_path' => '/app/login', 'check_path' => '/app/login/check', 'failure_path' => '/app/login', 'default_target_path' => '/app', //'always_use_default_target_path' => true, 'username_parameter' => 'username', 'password_parameter' => 'password', 'use_referer' => true ), 'logout' => array( 'logout_path' => '/app/logout', 'target' => '/app', 'invalidate_session' => true, ), 'users' => $app['user.provider'] ), 'secured_area' => array( 'pattern' => '^/app', 'stateless' => false, 'anonymous' => true, 'form' => array( 'authenticator' => $app['apiKey.authenticator'] ), 'logout' => array( 'logout_path' => '/app/logout', 'target' => '/app', 'invalidate_session' => true, ), 'users' => $app['apiKey.provider'],
),
),
'security.providers' => array(
'api_key_user_provider' => array(
'id' => $app['apiKey.provider'],
),
)
)
);
@weaverryan
In the final I have manage to do it. I will post here how I did probably would help someone.
$app->match('/app/login/api/check', function() use ($app) {
$auth = new \Application\Security\ApiKeyAuthenticator($app['security.http_utils']);
$token = $auth->createToken($app['request'], 'mihai');
$result = $auth->authenticateToken($token, $app['apiKey.provider'], "mihai");
$app['security']->setToken($result);
return $app->redirect(
$app['baseUrl'] . '/app'
);
});
I think this part is not valid, It couldn't work.
http://symfony.com/doc/current/cookbook/security/api_key_authentication.html#only-authenticating-for-certain-urls
If you just do this:
The Token is null and then authenticate won't work because needs an instance of Symfony\Component\Security\Core\Authentication\Token\TokenInterface. The exact error is:
It happens here:
https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php#L77-L80
I am trying to sort things out. :-)
Thank you in advance!