Two Login in Symfony 6 - security

I had problems with tow login in Symfony 6. When I access to /admin/login I get the error "Class App\Controller\AuthenticationUtils does not exist"
here is my security.yaml
security:
encoders:
App\Entity\User:
algorithm: auto
App\Entity\AdminUser:
algorithm: auto
enable_authenticator_manager: true
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
App\Entity\User:
algorithm: auto
App\Entity\AdminUser:
algorithm: auto
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: email
admin_user_provider:
entity:
class: App\Entity\AdminUser
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
admin:
pattern: ^/admin
lazy: true
provider: admin_user_provider
custom_authenticator: App\Security\AdminLoginFormAuthenticator
logout:
path: admin_logout
# where to redirect after logout
# TODO target: app_any_route
main:
lazy: true
provider: app_user_provider
custom_authenticator: App\Security\LoginFormAuthenticator
logout:
path: app_logout
# where to redirect after logout
# TODO target: app_any_route
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#the-firewall
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
# TODO - { path: ^/admin, roles: ROLE_ADMIN }
# TODO - { path: ^/profile, roles: ROLE_USER }
And it is my AdminSecurityController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class AdminSecurityController extends AbstractController
{
/**
* #Route("/admin/security", name="admin_security")
*/
public function index(): Response
{
return $this->render('admin_security/index.html.twig', [
'controller_name' => 'AdminSecurityController',
]);
}
/**
* #Route("/admin/login", name="admin_login")
*/
public function login(AuthenticationUtils $authenticationUtils): Response
{
// get the login error if there is one
// $error = $authenticationUtils->getLastAuthenticationError();
// // last username entered by the user
// $lastUsername = $authenticationUtils->getLastUsername();
return $this->render('admin_login/index.html.twig'); //, ['last_username' => $lastUsername, 'error' => $error]
}
/**
* #Route("/admin/logout", name="admin_logout")
*/
public function logout()
{
throw new \Exception('This method can be blank - it will be intercepted by the logout key on your firewall');
}
}
I attach some images with information about the error
I have no idea about the problem, hope you can help me!!
Thank you !!

You're missing your import of the
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
at the top of your controller

Related

Symfony 4.4 Security / Session is saved but not used

Full config files are available bellow.
My website is using 2 guards at differents entries :
lexik_jwt_authentication.jwt_token_authenticator to access the ^/api routes
App\Security\LoginAuthenticator to access ^/secured routes
The JWT authetification is working well, allowing users to call a API plateform routes such as localhost/api/types
The problem is about using the LoginAuthenticator session. This is the basic symfony configuration (auto-generated files with php bin/console make:auth)
When login with correct user/password, the guard save a session to app/var/sessions/dev/sess_ras4up86e1c8a1bs9khr5t7scg
When the session is saved and set, the LoginAuthenticator trigger onAuthenticationSuccess function redirecting the user to route name `test.
But the testController is not working, redirecting the user to login page (302 HTTP code). It also throwing the following error in var/log/dev/dev.log :
[2021-07-08 13:07:40] request.INFO: Matched route "app_login". {"route":"app_login","route_parameters":{"_route":"app_login","_controller":"App\\Controller\\SecurityController::login"},"request_uri":"https://krang.local/login","method":"POST"} []
[2021-07-08 13:07:40] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"login","authenticators":1} []
[2021-07-08 13:07:40] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"login","authenticator":"App\\Security\\LoginAuthenticator"} []
[2021-07-08 13:07:40] security.DEBUG: Calling getCredentials() on guard authenticator. {"firewall_key":"login","authenticator":"App\\Security\\LoginAuthenticator"} []
[2021-07-08 13:07:40] security.DEBUG: Passing guard token information to the GuardAuthenticationProvider {"firewall_key":"login","authenticator":"App\\Security\\LoginAuthenticator"} []
[2021-07-08 13:07:40] doctrine.DEBUG: SELECT t0.id AS id_1, t0.username AS username_2, t0.password AS password_3, t0.is_active AS is_active_4, t0.roles AS roles_5, t0.customer_id AS customer_id_6 FROM users t0 WHERE t0.username = ? LIMIT 1 ["matthieu"] []
[2021-07-08 13:07:40] security.INFO: Guard authentication successful! {"token":"[object] (Symfony\\Component\\Security\\Guard\\Token\\PostAuthenticationGuardToken: PostAuthenticationGuardToken(user=\"matthieu\", authenticated=true, roles=\"ROLE_USER, ROLE_ADMIN\"))","authenticator":"App\\Security\\LoginAuthenticator"} []
[2021-07-08 13:07:40] security.DEBUG: Guard authenticator set success response. {"response":"[object] (Symfony\\Component\\HttpFoundation\\RedirectResponse: HTTP/1.0 302 Found\r\nCache-Control: no-cache, private\r\nDate: Thu, 08 Jul 2021 11:07:40 GMT\r\nLocation: /secured/backmarketProducts\r\n\r\n<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"UTF-8\" />\n <meta http-equiv=\"refresh\" content=\"0;url='/secured/backmarketProducts'\" />\n\n <title>Redirecting to /secured/backmarketProducts</title>\n </head>\n <body>\n Redirecting to /secured/backmarketProducts.\n </body>\n</html>)","authenticator":"App\\Security\\LoginAuthenticator"} []
[2021-07-08 13:07:40] security.DEBUG: Remember me skipped: it is not configured for the firewall. {"authenticator":"App\\Security\\LoginAuthenticator"} []
[2021-07-08 13:07:40] security.DEBUG: The "App\Security\LoginAuthenticator" authenticator set the response. Any later authenticator will not be called {"authenticator":"App\\Security\\LoginAuthenticator"} []
[2021-07-08 13:07:40] request.INFO: Matched route "test". {"route":"test","route_parameters":{"_route":"test","_controller":"App\\Controller\\BackMarketController::productsList"},"request_uri":"https://krang.local/secured/backmarketProducts","method":"GET"} []
[2021-07-08 13:07:40] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"secured","authenticators":1} []
[2021-07-08 13:07:40] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"secured","authenticator":"App\\Security\\LoginAuthenticator"} []
[2021-07-08 13:07:40] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"secured","authenticator":"App\\Security\\LoginAuthenticator"} []
[2021-07-08 13:07:40] security.INFO: An AuthenticationException was thrown; redirecting to authentication entry point. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationCredentialsNotFoundException(code: 0): A Token was not found in the TokenStorage. at /home/matthieu/krang/webservice/vendor/symfony/security-http/Firewall/AccessListener.php:69)"} []
[2021-07-08 13:07:40] security.DEBUG: Calling Authentication entry point. [] []
[2021-07-08 13:07:40] request.INFO: Matched route "app_login". {"route":"app_login","route_parameters":{"_route":"app_login","_controller":"App\\Controller\\SecurityController::login"},"request_uri":"https://krang.local/login","method":"GET"} []
[2021-07-08 13:07:40] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"login","authenticators":1} []
[2021-07-08 13:07:40] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"login","authenticator":"App\\Security\\LoginAuthenticator"} []
[2021-07-08 13:07:40] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"login","authenticator":"App\\Security\\LoginAuthenticator"} []
[2021-07-08 13:07:40] security.INFO: Populated the TokenStorage with an anonymous Token. [] []
[2021-07-08 13:07:40] request.INFO: Matched route "_wdt". {"route":"_wdt","route_parameters":{"_route":"_wdt","_controller":"web_profiler.controller.profiler::toolbarAction","token":"ad25b7"},"request_uri":"https://krang.local/_wdt/ad25b7","method":"GET"} []
We can see this error :
[2021-07-08 13:07:40] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"secured","authenticator":"App\\Security\\LoginAuthenticator"} []
[2021-07-08 13:07:40] security.INFO: An AuthenticationException was thrown; redirecting to authentication entry point. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationCredentialsNotFoundException(code: 0): A Token was not found in the TokenStorage. at /home/matthieu/krang/webservice/vendor/symfony/security-http/Firewall/AccessListener.php:69)"} []
Guard authenticator does not support the request
What does this mean ? I give you some useful code to checkout :
security.yaml : (full file)
security:
encoders:
App\Entity\User:
algorithm: auto
role_hierarchy:
ROLE_USER : "ROLE_USER"
ROLE_ADMIN : "ROLE_ADMIN"
ROLE_SUPERADMIN : "ROLE_SUPERADMIN"
providers:
entity_provider:
entity:
class: App\Entity\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/login
stateless: true
anonymous: true
json_login:
check_path: /login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
guard:
authenticators:
- App\Security\LoginAuthenticator
logout:
path: app_logout
# where to redirect after logout
# target: app_any_route
docs:
pattern: ^/docs
stateless: true
anonymous: true
secured:
pattern: ^/secured
stateless: true
provider: entity_provider
guard:
authenticators:
- App\Security\LoginAuthenticator
api:
pattern: ^/api
stateless: true
anonymous: false
provider: entity_provider
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
- { path: ^/docs, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/secured, roles: IS_AUTHENTICATED_FULLY }
SecurityController: (unchanged from default)
class SecurityController extends AbstractController
{
/**
* #Route("/login", name="app_login")
*/
public function login(AuthenticationUtils $authenticationUtils): Response
{
if ($this->getUser()) {
die();
}
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
}
/**
* #Route("/logout", name="app_logout")
*/
public function logout()
{
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
}
LoginAuthenticator: (src/Security/LoginAuthenticator.php)
class LoginAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'app_login';
private $entityManager;
private $urlGenerator;
private $csrfTokenManager;
private $passwordEncoder;
public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
{
$this->entityManager = $entityManager;
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
$this->passwordEncoder = $passwordEncoder;
}
public function supports(Request $request)
{
return self::LOGIN_ROUTE === $request->attributes->get('_route')
&& $request->isMethod('POST');
}
public function getCredentials(Request $request)
{
$credentials = [
'username' => $request->request->get('username'),
'password' => $request->request->get('password'),
'csrf_token' => $request->request->get('_csrf_token'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['username']
);
return $credentials;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
$user = $this->entityManager->getRepository(User::class)->findOneBy(['username' => $credentials['username']]);
if (!$user) {
throw new UsernameNotFoundException('Username could not be found.');
}
return $user;
}
public function checkCredentials($credentials, UserInterface $user)
{
return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
}
/**
* Used to upgrade (rehash) the user's password automatically over time.
*/
public function getPassword($credentials): ?string
{
return $credentials['password'];
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
return new RedirectResponse($this->urlGenerator->generate('test'));
}
protected function getLoginUrl()
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
}
BackMarketController (just a function)
function called on authentification success
/**
* #return JsonResponse
* #Route("/secured/backmarketProducts", name="test")
*/
public function productsList(Request $request)
{
return $this->render('back_market/backmarketProducts.html.twig');
}
About the webserver :
Apache2
Debian 8
using HTTPS
Symfony 4.4
I can give you more informations of course, just ask for it.
What i tried by looking on other similar subjects :
Symfony 4 login Guard authenticator errors (save the session information elsewhere from default)
update project components
Symfony 4 login form : authenticating successfully, but authentication immediately lost after redirect (add EquatableInterface to User entity)
Kill onAuthenticationSuccess BEFORE REDIRECTION show on the profiler that a session is created but cant follow redirection (look at the error above)
UPDATE 1 (3 days later)
I found a way to allow jwt token and securityauthentificator to work together.
There is the security.yaml :
security:
encoders:
App\Entity\User:
algorithm: auto
role_hierarchy:
ROLE_USER : "ROLE_USER"
ROLE_ADMIN : "ROLE_ADMIN"
ROLE_SUPERADMIN : "ROLE_SUPERADMIN"
providers:
entity_provider:
entity:
class: App\Entity\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
guard:
entry_point: lexik_jwt_authentication.jwt_token_authenticator
authenticators:
- App\Security\SecurityAuthenticator
- lexik_jwt_authentication.jwt_token_authenticator
logout:
path: app_logout
json_login:
check_path: /login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
login:
pattern: ^/login
stateless: true
anonymous: true
json_login:
check_path: /login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
docs:
pattern: ^/docs
stateless: true
anonymous: true
api:
pattern: ^/api
stateless: true
anonymous: false
provider: entity_provider
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
secured:
pattern: ^/secured
anonymous: false
guard:
authenticators:
- App\Security\SecurityAuthenticator
logout:
path: app_logout
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
- { path: ^/docs, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/secured, roles: IS_AUTHENTICATED_FULLY }
I think that block solve the issue, the symfony security compenent got both guards declared on main firewall, using jwt by default.
To use the basic symfony auth system (securityAuthentificator) i just added a secured firewall providing SecurityAuthenticator
Is there a good way to work with security ? Does this trick seems legit ? Im not sure im providing a good solution.
Nobody seems able to help me. In any case I solved the problem on my own.
There is my full functionnal security.yaml :
This config is working, making symfony able to use API PLATEFORM with JWT tokens and to log user on a simple back office application.
security:
encoders:
App\Entity\User:
algorithm: auto
role_hierarchy:
ROLE_USER : "ROLE_USER"
ROLE_ADMIN : "ROLE_ADMIN"
ROLE_SUPERADMIN : "ROLE_SUPERADMIN"
providers:
entity_provider:
entity:
class: App\Entity\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
guard:
entry_point: lexik_jwt_authentication.jwt_token_authenticator
authenticators:
- App\Security\SecurityAuthenticator
- lexik_jwt_authentication.jwt_token_authenticator
logout:
path: app_logout
json_login:
check_path: /login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
login:
pattern: ^/login
stateless: true
anonymous: true
json_login:
check_path: /login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
docs:
pattern: ^/docs
stateless: true
anonymous: true
api:
pattern: ^/api
stateless: true
anonymous: false
provider: entity_provider
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
secured:
pattern: ^/secured
anonymous: false
guard:
authenticators:
- App\Security\SecurityAuthenticator
logout:
path: app_logout
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
- { path: ^/docs, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/secured, roles: IS_AUTHENTICATED_FULLY }

FOSUserbundle + Additional HTTP auth without user getting ROLE_USER

I have a "little" problem with the Symfony2 security system. This is what I need to achieve:
/ needs a HTTP auth BEFORE any page can be seen. I want to protect the whole page with a constant user / pass pair. After the user has entered the right pair, he should be a guest (and not ROLE_USER) und be able to login via the FOSUserBundle form
/api needs a separate login via HTTP auth, independent from FOSUserBundle and the other HTTP auth
I already managed to provide a separate login for the API. This is my complete security.yml:
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
Symfony\Component\Security\Core\User\User: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
in_memory_http:
memory:
users:
User1: { password: PW1, roles: ROLE_HTTP }
in_memory_api:
memory:
users:
User2: { password: PW2, roles: ROLE_API }
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
api:
pattern: ^/api
http_basic:
provider: in_memory_api
realm: "API login"
http:
pattern: ^/
provider: in_memory_http
http_basic:
realm: "Hello"
context: primary_auth
main:
pattern: ^/
form_login:
provider: fos_userbundle
login_path: fos_user_security_login
csrf_provider: form.csrf_provider
check_path: fos_user_security_check
logout:
path: fos_user_security_logout
target: home
anonymous: true
context: primary_auth
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
security: false
secured_area:
anonymous: ~
access_control:
- { path: ^/api, roles: ROLE_API }
- { path: ^/user/login.html, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/logout.html, roles: IS_AUTHENTICATED_REMEMBERED }
This works nearly as expected, but unfortunately not completely...
The /api part works like I wanted it to work. Nothing to do here, I hope.
But when I navigate to /, enter User1/PW1 and send the credentials, I get access to the page, just like expected. The only problem is that the User1 gets logged in! But I want User1 not to be handled like a normal user. He should just be required to access the normal login form and the rest of / except of /api. I can't even log out this user. If I navigate to /user/login.html while User1 is logged in (due to the required http auth) and enter valid user data of a real fosuserbundle user, I get: "You must configure the check path to be handled by the firewall using form_login in your security firewall configuration."
If I want to log out, I get: "You must activate the logout in your security firewall configuration."
What I want is kind of a two step authentication.
First HTTP Auth, then the FOSUserBundle form.
Can somebody help me? :) The documentation is not very good at this point...
So.....
After several hours of pure pain I gave up on trying it with the Symfony2 Security Component. I also did not manage to realize what I want with Apache (Tried SetEnvIf and FilesMatch).
So I wrote a request listener that does what I want. If anybody has the same problem, here is my solution!
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpFoundation\Response;
class RequestListener
{
/**
* Returns true iff the specified $password belongs to the $user and the $user has access to the specified $area
*/
private function hasAccess($user, $password, $area) {
$users = array("User1" => array("password" => "PW1",
"areas" => array("portal")),
"User2" => array("password" => "PW2",
"areas" => array("API", "portal"))
);
return $user
&& array_key_exists($user, $users)
&& $users[$user]["password"] == $password
&& in_array($area, $users[$user]["areas"]);
}
/**
* Extracts the area out of the $path
*/
public function getArea($path) {
if (substr($path, 0, 4) == "/api") {
return "API";
} else {
return "portal";
}
}
/**
* Service handler
*/
public function onKernelRequest(GetResponseEvent $event) {
$request = $event->getRequest();
# $path cointains the path to the virtual resource the user requested!
# If the user calls app_dev.php/api/users, then $path is "/api/users"
# If the user calls app.php/api/users, then $path is "/api/users"
# If the user calls /api/users, then $path is "/api/users"
# If the user calls /app_dev.php, then $path is "/"
# If the user calls /, then $path is "/"
# and so on
#
# ==> $path abstracts the front controller away
$path = $request->getPathInfo();
$area = $this->getArea($path);
# $user, $password are null if no AUTH data is sent
$user = $request->server->get("PHP_AUTH_USER");
$password = $request->server->get("PHP_AUTH_PW");
# If the user has no access, he must log in as another user
if (!$this->hasAccess($user, $password, $area)) {
# In case the response already exists (in most cases not) we use it and modify it
$response = $event->hasResponse() ? $event->getResponse() : new Response();
$response->setStatusCode(Response::HTTP_UNAUTHORIZED); # Code 401
$response->headers->set("WWW-Authenticate", "Basic realm=\"".$area."\"");
$response->setContent("Please provide valid data");
$response->send();
die(); # To make sure the page is not shown!
}
}
}
Now everything seems to work...

Error while unserializing user data in symfony authentication

my first question here. I have been sitting on it for few hours and couldn't find solution:
The error pops out when the user is successfuly found in DB and i guess symfony tries to serialize it's data into session.
Notice: unserialize(): Error at offset 37 of 49 bytes in G:\cebuland\vendor\symfony\symfony\src\Symfony\Component\Security\Core\Authentication\Token\AbstractToken.php line 163
Causes login process faliure.
User's Entity:
/**
* #ORM\Entity
* #ORM\Table(name="User")
*/
class User implements AdvancedUserInterface, \Serializable{
...
/**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="user")
* #var $role Doctrine\Common\Collections\ArrayCollection
*/
private $role;
...
public function getRoles() {
return $this->role->toArray();
}
...
public function serialize() {
serialize(array(
$this->id,
$this->name,
$this->password,
$this->created,
$this->last_activity,
$this->ghost,
$this->role
));
}
public function unserialize($serialized) {
list(
$this->id,
$this->name,
$this->password,
$this->created,
$this->last_activity,
$this->ghost,
$this->role
) = unserialize($serialized);
}
Role's Entity is quite similiar.
And the security.yml configuration file:
security:
providers:
users:
entity: {class: ApplicationMainBundle:User, property: name}
encoders:
Application\MainBundle\Entity\User:
#plain just for testing
algorithm: plaintext
firewalls:
secured_area:
logout:
path: /logout
pattern: ^/
anonymous: ~
form_login:
login_path: /login
check_path: /login_check
access_control:
- { path: ^/admin/, roles: ROLE_ADMINISTRATOR}
Couldn't find a clue anywhere on the internet.
Your User::serialize method does not return serialized string, but it should Serializable::serialize

Symfony2 Security log user manually

I want to log user manually in Symfony2. (I use fosuserbundle).
The authentication will be triggered in custom route like this /login/auto
Here is my controller code which match with /login/auto
public function loginAction(){
$em = $this->container->get('doctrine')->getManager();
$users = $em->getRepository('MybundleMainBundle:User');
$user = $users->findOneByEmail("user#user.com");
$securityContext = $this->get('security.context');
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$securityContext->setToken($token);
$this->get('session')->set('_security_'.'main', serialize($token));
return new RedirectResponse($this->generateUrl('home'));
}
But after the redirection, I'm redirected automatically to /login and not /home so the authentification failed
Here is my security file config :
security:
providers:
fos_userbundle:
id: fos_user.user_provider.username
encoders:
FOS\UserBundle\Model\UserInterface: sha512
firewalls:
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
always_use_default_target_path: true
logout: true
anonymous: true
switch_user: true
remember_me:
key: %secret%
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
access_control:
- { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, role: ROLE_USER }
role_hierarchy:
ROLE_USER: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
Thanks for your help
Looks very similar to mine. Maybe you should not write into the session. Or the token needs the (hashed) password. Try it, here's a working code ;)
public function demologinAction(Request $request)
{
$dm = $this->get('doctrine.odm.mongodb.document_manager');
$repo = $dm->getRepository('AcmeUserBundle:User');
$user = $repo->findOneByUsername('demo');
if (!$user) {
throw $this->createNotFoundException('No demouser found!');
}
$token = new UsernamePasswordToken($user, $user->getPassword(), 'main', $user->getRoles());
$context = $this->get('security.context');
$context->setToken($token);
$router = $this->get('router');
$url = $router->generate('dashboard_show');
return $this->redirect($url);
}
Why are you trying to log a user in manually?
I may be wrong, but if the reason is so that you can run some postLogin code, it would be easier to use the built in login functionality, but setup a listener on the login action as a service. Then add your login code in there.
Sevice definition would be:
user.login:
class: You\Bundle\EventListener\EventListener
arguments: [#doctrine.orm.entity_manager, #service_container]
tags:
- { name: kernel.event_listener, event: security.interactive_login, method: onLogin }
And your event listener might be:
public function onLogin(InteractiveLoginEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
$user->setLastLoggedInAt(new \DateTime());
$user->setLoginCount($user->getLoginCount() + 1);
$this->manager->flush();
}

Symfony2 security token cleared after redirect

I have built a login form, with a custom provider which successfully sets the auth token.
I can see the "logged in as" in the profiler, while on the redirect interception page. The form then redirects to my success page.
For some reason, my success page, doesn't recognise that I'm logged in and loops back to the login page.
security:
encoders:
EP\Common\Entity\User: sha1
encode_as_base64: false
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
main:
entity: { class EP\Common\Entity\User, property: username }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login_form:
pattern: ^/cms/(login|logout)$
security: false
cms:
pattern: ^/cms/
security: true
provider: main
form_login:
check_path: /cms/login_check
login_path: /cms/login
logout:
path: /cms/logout
target: /cms/login
access_control:
- { path: ^/cms/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: http }
- { path: /cms, roles: ROLE_USER }
I've discovered why this is.
I'd added these methods to my User class (notice the [0] on unserialize - doctrine was looking for the ID of the user based on the first character of the ID):
/**
* (PHP 5 >= 5.1.0)<br/>
* String representation of object
* #link http://php.net/manual/en/serializable.serialize.php
* #return string the string representation of the object or null
*/
public function serialize()
{
return serialize($this->getId());
}
/**
* (PHP 5 >= 5.1.0)<br/>
* Constructs the object
* #link http://php.net/manual/en/serializable.unserialize.php
* #param string $serialized <p>
* The string representation of the object.
* </p>
* #return mixed the original value unserialized.
*/
public function unserialize($serialized)
{
$this->_uuid = unserialize($serialized)[0];
}

Resources