I am trying to use multiple authenticators (for each user role). They were working fine until the last one, which redirects unauthenticated users to URL /dashboard/ to login, but requests to /dashboard are thrown AccessDeniedHttpException:
This is my security.yaml file, which have all the firewalls:
security:
encoders:
App\Entity\User:
algorithm: sha512
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/admin/
anonymous: lazy
provider: app_user_provider
guard:
authenticators:
- App\Security\AdminAuthenticator
logout:
path: admin_logout
expert:
pattern: ^/dashboard/
anonymous: lazy
provider: app_user_provider
guard:
authenticators:
- App\Security\ExpertAuthenticator
logout:
path: expert_logout
user:
anonymous: lazy
provider: app_user_provider
guard:
authenticators:
- App\Security\UserAuthenticator
logout:
path: user_logout
access_control:
- { path: ^/$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/ask, roles: ROLE_USER }
- { path: ^/dashboard/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/dashboard, roles: ROLE_EXPERT }
I think it is not necessary to paste here the authenticator since it is the Symfony 5 make:auth maker command default which I copied and pasted for all the three authenticators while changing only public const LOGIN_ROUTE = 'user_login'; and onAuthenticationSuccess() function's redirect response path.
I also used the default SecurityController:
namespace App\Controller\Expert;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
/**
* Class SecurityController
* #package App\Controller\Expert
* #Route("/dashboard")
*/
class SecurityController extends AbstractController
{
/**
* #Route("/login", name="expert_login")
*/
public function login(AuthenticationUtils $authenticationUtils): Response
{
if ($this->getUser()) {
return $this->redirectToRoute('expert_index');
}
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('expert/security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
}
/**
* #Route("/logout", name="expert_logout")
*/
public function logout()
{
$this->addFlash('success', 'Session closed');
return $this->redirectToRoute('expert_index');
}
}
Whenever I access to /admin, /admin/whatever or /ask, /ask/whatever I am redirected to its respective login form (/admin/login and /ask/dashboard).
I can access directly to /dashboard/login if I am not logged from another authenticator or if I am logged with ROLE_EXPERT. If not, I am getting the mentioned exception.
Well, do you see what is going on?
You should move - { path: ^/$, roles: IS_AUTHENTICATED_ANONYMOUSLY } as last access_control entry.
That's because Symfony parses it as you're writing it (think about it like it's a FIFO queue) and, as / could be accessed in anonymous way, associated token will be anonymous (it won't try to read from session or whatever).
Related
I am integrating lexik/jwtautheticationbundle version 1.3 with symfony 2.8 due to old application changes.
I have managed integrate and generate JWT authorization token but I wanted to use cookie and authentication_listener in lexit_jwt and I used but it has no any effect. If I use cookie, token should be saved in cookie but it is saved in session.
Can anyone suggest me why cookie enabled not working ?
Security.yml
security:
encoders:
AppBundle\Entity\User:
algorithm: bcrypt
providers:
db_provider:
entity:
class: AppBundle:User
property: username
firewalls:
login:
pattern: ^/api/login
stateless: true
anonymous: true
provider: db_provider
form_login:
check_path: /api/login_check
username_parameter: username
password_parameter: password
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
anonymous: true
provider: db_provider
lexik_jwt:
authentication_listener: storefront.listener.jwt_authentication
cookie:
enabled: true
name: IDENTITY
access_control:
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
services.yml
# Learn more about services, parameters and containers at
# https://symfony.com/doc/current/service_container.html
parameters:
#parameter_name: value
services:
#service_name:
# class: AppBundle\Directory\ClassName
# arguments: ['#another_service_name', 'plain_value', '%parameter_name%']
storefront.listener.jwt_authentication:
class: AppBundle\Listener\AuthenticationListener
arguments:
- "#security.token_storage"
- "#security.authentication.manager"
- []
AuthenicationListener.php
<?php
namespace AppBundle\Listener;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Firewall\JWTListener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
class AuthenticationListener extends JWTListener
{
public function handle(GetResponseEvent $event): void
{
if (!($requestToken = $this->getRequestToken($event->getRequest()))) {
return;
}
$token = new JWTUserToken();
$token->setRawToken($requestToken);
try {
$authToken = $this->authenticationManager->authenticate($token);
$this->tokenStorage->setToken($authToken);
return;
} catch (AuthenticationException $failed) {
if ($this->config['throw_exceptions']) {
throw $failed;
}
}
}
}
I thought when cookie is enabled, it will save the token in cookie in the browser, but it meant to just read token from cookie only. So i figured myself. Thank you anyway
I am trying to set up my security, so it redirect back to referer after login.
For example, i am opening /profile/{slug}
This is my ProfileController
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class ProfileController extends Controller
{
public function indexAction(Request $request, $slug)
{
$authChecker = $this->get('security.authorization_checker');
if(!$authChecker->isGranted('ROLE_USER')) {
return $this->redirect($this->generateUrl('fos_user_security_login'));
}
$userManager = $this->container->get('fos_user.user_manager');
$user = $userManager->findUserByUsername($slug);
if(!$user) {
throw $this->createNotFoundException('Пользователь не найден');
}
return $this->render('AppBundle::profile.html.twig', array('user' => $user));
}
}
If user is not logged in, controller redirects to login route.
After user logged in, it is being redirected to home page. I want to change it to referer.
Here is my security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_provider: security.csrf.token_manager # Use form.csrf_provider instead for Symfony <2.4
login_path: /account/login
check_path: /account/login-check
use_referer: true
logout:
path: /account/logout
anonymous: true
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
As you see, i have added use_referer: true there, but it is not working.
How should i do then?
I use UserBundle and HWIO for social network, but If user have not socials I create custom registration, when user have email and password for email I try authentication but have many error last error:
Error: User account is disabled.
I don’t know how to be tune service.yml and HWIO still work and standart authentication help please
And know not working enter with HWIO:
Unable to find the controller for path "/login/check-vkontakte". The route is wrongly configured.
with this work fine security:
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
id: fos_user.user_provider.username
my_custom_hwi_provider:
id: app.provider.user_provider
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
oauth:
resource_owners:
facebook: "/login/check-facebook"
vkontakte: "/login/check-vkontakte"
login_path: /login
failure_path: /login
oauth_user_provider:
#this is my custom user provider, created from FOSUBUserProvider - will manage the
#automatic user registration on your site, with data from the provider (facebook. google, etc.)
service: app.provider.user_provider
logout: true
anonymous: true
login:
pattern: ^/login$
security: false
remember_me:
key: "%secret%"
lifetime: 60 # 365 days in seconds
path: /
domain: ~ # Defaults to the current domain from $_SERVER
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
this my security
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
PillsBundle\Entity\User:
algorithm: sha1
encode_as_base64: false
iterations: 1
Symfony\Component\Security\Core\User\User: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
id: fos_user.user_provider.username
my_custom_hwi_provider:
id: app.provider.user_provider
chain_provider:
chain:
providers: [user_db, in_memory]
user_db:
entity: { class: UserBundle\Entity\User, property: email }
in_memory:
memory:
users:
admin_tyty: { password: adminpass_tyty, roles: [ 'ROLE_ADMIN' ] }
firewalls:
admin_secured_area:
pattern: /(.*)
anonymous: ~
form_login:
provider: chain_provider
login_path: /auth/login
check_path: /auth/login_check
always_use_default_target_path: true
default_target_path: /?r=db
logout:
path: /auth/logout
target: /
invalidate_session: false
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
form_login:
provider: fos_userbundle
#csrf_provider: form.csrf_provider
oauth:
resource_owners:
facebook: "/login/check-facebook"
vkontakte: "/login/check-vkontakte"
login_path: /login
failure_path: /login
oauth_user_provider:
#this is my custom user provider, created from FOSUBUserProvider - will manage the
#automatic user registration on your site, with data from the provider (facebook. google, etc.)
service: app.provider.user_provider
logout: true
anonymous: true
login:
pattern: ^/login$
security: false
remember_me:
key: "%secret%"
lifetime: 60 # 365 days in seconds
path: /
domain: ~ # Defaults to the current domain from $_SERVER
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
and my SecurityController Controller
/**
* #Route("/auth")
*/
class SecurityController extends Controller
{
/**
* #Route("/login", name="login_route")
* #Template()
*/
public function loginAction()
{
$request = $this->getRequest();
$session = $request->getSession();
$securityContext = $this->container->get('security.context');
if ( $securityContext->isGranted('IS_AUTHENTICATED_FULLY') ) {
return $this->redirect($this->generateUrl('get_all_posts'));
}
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} else {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
}
return array(
'_last' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
);
}
If you have override the registration controller then Just enable the user in FOSUserBundle > RegistrationController class
If not then have a look in to this doc.
http://symfony.com/doc/current/bundles/FOSUserBundle/overriding_controllers.html
RegistrationController extends BaseController
{
public function registerAction(Request $request)
{
/** #var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
$formFactory = $this->get('fos_user.registration.form.factory');
/** #var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
/** #var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
$dispatcher = $this->get('event_dispatcher');
$user = $userManager->createUser();
$user->setEnabled(true);
}
After login, I want to access to the same page. But I have a redirect loop of death
security.yml
login:
pattern: ^/admin/login
security: false
anonymous: true
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
secured_area:
pattern: ^/admin
form_login:
check_path: _login_check
login_path: _admin_login
default_target_path: _admin_dashboard
always_use_default_target_path: true
logout:
path: _admin_logout
target: _admin_login
anonymous: ~
access_control:
- { path: ^/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, roles: [ROLE_USER, ROLE_ADMIN] }
routing.yml :
_admin_secured:
resource: "#TestBackBundle/Controller/SecuredController.php"
type: annotation
_admin_home:
resource: "#TestBackBundle/Controller/DashboardController.php"
type: annotation
DashboardController.php :
...
/**
* #Route("/admin/dashboard")
*/
class DashboardController extends Controller
{
/**
* #Route("/", name="_admin_dashboard")
* #Template()
*/
public function indexAction()
{
return array();
}
}
and SecuredController.php :
...
/**
* #Route("/admin")
*/
class SecuredController extends Controller
{
/**
* #Route("/login", name="_admin_login")
* #Template()
*/
public function loginAction(Request $request)
{
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} else {
$error = $request->getSession()->get(SecurityContext::AUTHENTICATION_ERROR);
}
return array(
'last_username' => $request->getSession()->get(SecurityContext::LAST_USERNAME),
'error' => $error,
);
}
/**
* #Route("/login_check", name="_login_check")
*/
public function securityCheckAction()
{
// The security layer will intercept this request
}
/**
* #Route("/logout", name="_admin_logout")
*/
public function logoutAction()
{
// The security layer will intercept this request
}
}
What I did wrong ?
This worked for me, the main difference is that your secured area is ^/admin, and mine it is ^/
There is a lot of redundant code, but I was tired to figure to make it works, and when it worked I just don't want to touch it anymore
(that it is in my case, I wanted to protect ^/ but anonymously access to /login, you are protecting /admin but want to access /admin/login)
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
free:
pattern: ^/$
security: false
free2:
pattern: ^/login$
security: false
secured_area:
pattern: ^/
form_login:
login_path: login
check_path: login_check
always_use_default_target_path: true
default_target_path: /borsa/ofertes
logout:
path: /logout
target: /
login:
pattern: ^/
security: false
anonymous: ~
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY}
#- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
- { path: ^/admin, roles: ROLE_ADMIN }
Add below section to your firewall before secured_area
my_login:
pattern: ^/admin/login$
security: false
anonymous: true
You need to make the login page security off before defining the secured area
You also need to return a response not just array in your indexAction
/**
* #Route("/admin")
*/
class DashboardController extends Controller
{
/**
* #Route("/dashboard", name="_admin_dashboard")
* #Template()
*/
public function indexAction()
{
$response = new Response();
$response->setContent(json_encode(array());
return $response;
}
}
Check the following link which has same issue (Google Group Question)
I think you need to use the full path for login_check and login_path in your security.yml
check_path: ^/admin/login_check
login_path: ^/admin/login
I have found the issue : Should be added $ after login path in login firewall and access_control:
login:
pattern: ^/admin/login$
security: false
anonymous: true
access_control:
- { path: ^/admin/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, roles: [ROLE_USER, ROLE_ADMIN] }
I hope this will help other people !
I am trying to configure remember me feature of symfony using their default mechanism as described here; but couldn't make it work.
Cookie named REMEMBERME is created, but is set to deleted and its expire date is 1970. This is why I suppose remember me function is not working. However, when I use (always_remember_me: true) in security.yml, the code works nicely but it doesn't suite my purpose. Using (always_remember_me: true) even if user doesn't check REMEMBER ME checkbox in UI the cookie gets created.
Any help is highly appreciated
I am using the version 2.3.7
This is my complete security.yml file:
security:
firewalls:
main:
pattern: ^/
anonymous: ~
remember_me:
key: "%secret%"
lifetime: 31536000 # a year
domain: ~
path: /
remember_me_parameter: _remember_me
#always_remember_me: true
form_login:
login_path: _my_login
check_path: _my_login_check
always_use_default_target_path: true
default_target_path: /out/homepage
remember_me: true
logout:
path: _my_logout
target: _my_login
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: IS_AUTHENTICATED_REMEMBERED }
providers:
chain_provider:
chain:
providers: [in_memory, user_db]
in_memory:
memory:
users:
user: { password: user, roles: 'ROLE_USER' }
admin: { password: admin, roles: ['ROLE_ADMIN', 'ROLE_USER'] }
user_db:
entity: {class: ProjectName\StoreBundle\Entity\User, property: username}
encoders:
ProjectName\StoreBundle\Entity\User:
algorithm: sha1
iterations: 1
encode_as_base64: false
Symfony\Component\Security\Core\User\User: plaintext
LoginController.php
class LoginController extends Controller
{
/**
* #Route("/login", name="_my_login")
* #Template()
*/
public function loginAction()
{
$request = $this->getRequest();
$session = $request->getSession();
// get the login error if there is one
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(
SecurityContext::AUTHENTICATION_ERROR
);
} else {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
}
return array(
// last username entered by the user
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
);
}
/**
* #Route("/login_check", name="_my_login_check")
*/
public function loginCheckAction()
{
}
I added the value attribute inside the input tag. After removing it, it worked for now. :)
<input type="checkbox" id="remember_me" name="_remember_me" checked /> Remember me
I suppose the checkbox is named incorrectly. It should have the 'name' attribute set to '_remember_me' to make the magic happen. Can you post the content of the form template?