I try to authenticate user:
<?php
/**
* #Route("/testLogin", name="testLogin")
*/
public function testLoginAction()
{
$em = $this->getDoctrine()->getEntityManager();
$user = $em->getRepository('ApplicationDefaultBundle:User')->findOneBy(array('id' => 126));
$providerKey = 'main';
$token = new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());
$this->container->get('security.context')->setToken($token);
return $this->redirect($this->generateUrl('testCheck'));
}
/**
* #Route("/testCheck", name="testCheck")
*/
public function testCheckAction()
{
if (false === $this->get('security.context')->isGranted(
'IS_AUTHENTICATED_REMEMBERED'
)) {
return new Response('Not logged');
}
$user = $this->container->get('security.context')->getToken()->getUser();
return new Response($user->getUsername.' is logged');
}
But I get permanent 302 redirect to /login page.
security:
encoders:
Application\Bundle\DefaultBundle\Entity\User:
algorithm: sha512
iterations: 24
encode_as_base64: true
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SPECIALIST: ROLE_USER
ROLE_EMPLOYER: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
main:
entity: { class: Application\Bundle\DefaultBundle\Entity\User, property: username }
firewalls:
secured_area:
remember_me:
key: MySecretKeyBlablabla
lifetime: 36000000
path: /
domain: ~
pattern: ^/
form_login:
check_path: /login_check
login_path: /login
provider: main
logout:
path: /logout
target: /
anonymous: true
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
Code for authenticate I take from https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Controller/RegistrationController.php
Error in app/logs/dev.log:
redirecting to authentication entry point (No Authentication Provider found for token of class "Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken".) [] []
I can't access to site until I clean cookies.
Try code from this answer. In your case firewall name is secured_area:
// your controller action
public function myAction()
{
// Authenticating user
$token = new UsernamePasswordToken($user, null, 'secured_area', $user->getRoles());
$this->get('security.token_storage')->setToken($token);
//For Symfony <= 2.3
//$this->get('security.context')->setToken($token);
$this->get('session')->set('_security_secured_area', serialize($token));
}
Verify that you have configured encoders section in security.yml, as configuration there has changed not very long time ago and it affects your problem.
Related
I'm using HWIOAuthBundle with Symfony 4.1.
My custom user provider gets called and returns a valid user, however the user is not logged in after the redirect. The web profiler shows: logged in as anon; anonymous token; firewall main
I've simplified the provider class below for brevity and it's only considering twitter right now. The conditions around $source are so I can add more later.
I have used xdebug to make sure loadUserByOauthuserResponse() is being called and that users are both being created in the case of a new user, or the existing user is being returned when it exists.
Question: If loadUserbyOauthUserResponse() IS returning a valid user entity, then what could be preventing it from creating a valid session with this user?
<?php
namespace App\Security;
...
class OAuthProvider extends OAuthUserProvider
{
...
public function loadUserByOAuthUserResponse(UserResponseInterface $response): User
{
/** #var EntityManager $em */
$em = $this->container->get('doctrine.orm.entity_manager');
$repo = $em->getRepository('App:User');
$source = $response->getResourceOwner()->getName();
$email = null;
$data = [];
$newUser = false;
// Set email and socialUser.
if ($source === 'twitter') {
$data = $response->getData();
$email = $data['email'];
}
// Check if this user already exists in our app.
$user = $repo->findOneBy(['email' => $email]);
if ($user === null) {
$newUser = true;
$user = new User();
$user->setPassword($this->strand(32));
}
// Set session and user data based on source.
if ($source === 'twitter') {
$name = $data['name'];
if ($newUser) {
$user->setNickName($name);
$user->setEmail($email);
$em->persist($user);
$em->flush();
}
}
return $user;
}
}
hwi_oauth.yaml
hwi_oauth:
# list of names of the firewalls in which this bundle is active, this setting MUST be set
firewall_names: [main]
# https://github.com/hwi/HWIOAuthBundle/blob/master/Resources/doc/2-configuring_resource_owners.md
resource_owners:
twitter:
type: twitter
client_id: '%env(TWITTER_ID)%'
client_secret: '%env(TWITTER_SECRET)%'
options:
include_email: true
services.yaml:
app.oauth_aware.user_provider.service:
class: App\Security\OAuthProvider
arguments: ['#service_container']
security.yaml:
security:
# https://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
encoders:
App\Entity\User: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
app_users:
entity: { class: App\Entity\User, property: email }
hwi:
id: app.oauth_aware.user_provider.service
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
provider: app_users
anonymous: ~
remember_me:
secret: "%env(APP_SECRET)%"
lifetime: 2592000
path: /
guard:
authenticators:
- App\Security\LoginFormAuthenticator
entry_point: App\Security\LoginFormAuthenticator
logout:
path: /logout
target: /
oauth:
resource_owners:
twitter: "/login/check-twitter"
default_target_path: /
login_path: /
failure_path: /user-login
oauth_user_provider:
service: app.oauth_aware.user_provider.service
switch_user: ~
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/recover, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
I had the same problem until I found in logs "Token was deauthenticated after trying to refresh it."
And after that I found this solution.
Token was deauthenticated after trying to refresh it
I added isEqualTo and did not logged me out.
I'm working with symfony at backend (api). The authentication process is handled by FosUserBundle, LexikJWTAuthenticationBundle and LdapTools... all works fine.
The problem is when I'm about to get the Authenticated user in a controller or service.
The user is authenticated by the Authorization header, does not exist 401 Exception
$this->container->get('security.token_storage')->getToken()->getUser()//null
$preAuthToken = $this->container->get('security.token_storage')->getToken();
$tmp = $this->container->get('lexik_jwt_authentication.jwt_manager')->decode($preAuthToken);//i can get the username and roles
But the real proble is with the security system
if ($this->isGranted('ROLE_USER')) {
echo 'never gets here!!';
} else {
echo 'always';
}
The security system always fails because the user returned by getUser() is always null.
My question is: the LexikJWTAuthenticationBundle should not inject or replace the current user, token after a successfull authentication?
or should I do it programatically? I dont want to fall in bad practices..
thanks in advance!
security.yml info
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
LdapTools\Bundle\LdapToolsBundle\Security\User\LdapUser: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
ldap:
id: ldap_tools.security.user.ldap_user_provider
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
refresh:
pattern: ^/api/token/refresh
stateless: true
anonymous: true
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api_login:
pattern: ^/login
stateless: true
provider: fos_userbundle
anonymous: true
form_login:
check_path: /login
require_previous_session: false
username_parameter: username
password_parameter: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: AppBundle\Handler\AuthenticationFailureHandler
require_previous_session: false
guard:
authenticators:
- ldap_tools.security.ldap_guard_authenticator
logout: true
api:
pattern: ^/
stateless: true
lexik_jwt: ~
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, role: IS_AUTHENTICATED_FULLY }
Auth. failure handler(just in case)
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$token = $exception->getToken();
if (is_string($exception->getToken()->getUser())) {
$usuario = $this->container->get('fos_user.user_manager')->findUserByUsername($token->getUsername());
if ($usuario) {
$token = new UsernamePasswordToken($usuario, 'yes', 'public', $usuario->getRoles());
} else {
return $this->container->get('lexik_jwt_authentication.handler.authentication_failure')->onAuthenticationFailure($request, $exception);
}
}
return $this->handleAuthenticationFail($request, $token, $exception);
}
public function handleAuthenticationFail(Request $request, TokenInterface $token, AuthenticationException $exception)
{
$username = $token->getUsername();
$password = $request->get('password');
if ($this->ldapManager->authenticate($username, $password)) {
return $this->container->get('lexik_jwt_authentication.handler.authentication_success')->handleAuthenticationSuccess($token->getUser());
}
return $this->container->get('lexik_jwt_authentication.handler.authentication_failure')->onAuthenticationFailure($request, $exception);
}
I'm trying to integrate FOSUserBundle with HWIBundle, to add the social media connections to my app. The FOSUser is working perfectly, but I get a problem with the integration with HWIOAuthBundle.
Bundles are loaded in AppKernel, perfectly.
This is my config.yml
hwi_oauth:
connect:
account_connector: my_user_provider
firewall_name: name
fosub:
username_iterations: 30
properties:
facebook: facebook_id
google: google_id
resource_owners:
facebook:
type: facebook
client_id: "%facebook_app_id%"
client_secret: "%facebook_app_secret%"
scope: ""
google:
type: google
client_id: "%google_app_id%"
client_secret: "%google_app_secret%"
scope: "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
This is my security.yml:
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_USER
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
login_path: /login
check_path: /login_check
oauth:
resource_owners:
facebook: "/login/check-facebook"
google: "/login/check-google"
login_path: /login
failure_path: /login
oauth_user_provider:
service: my_user_provider
logout: true
anonymous: true
login:
pattern: ^/login$
security: false
remember_me:
key: "%secret%"
lifetime: 31536000 # 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: ^/backend/, role: ROLE_ADMIN }
This is my Bundle services.yml
parameters:
my_user_provider.class: Main\SiteBundle\Services\FOSUBUserProvider
services:
my_user_provider:
class: "%my_user_provider.class%"
arguments: [#fos_user.user_manager,{facebook: facebook_id, google: google_id}]
And this is the Service:
namespace Main\SiteBundle\Services;
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
use HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider as BaseClass;
use Symfony\Component\Security\Core\User\UserInterface;
class FOSUBUserProvider extends BaseClass
{
/**
* {#inheritDoc}
*/
public function connect(UserInterface $user, UserResponseInterface $response)
{
$property = $this->getProperty($response);
$username = $response->getUsername();
//on connect - get the access token and the user ID
$service = $response->getResourceOwner()->getName();
$setter = 'set'.ucfirst($service);
$setter_id = $setter.'Id';
$setter_token = $setter.'AccessToken';
//we "disconnect" previously connected users
if (null !== $previousUser = $this->userManager->findUserBy(array($property => $username))) {
$previousUser->$setter_id(null);
$previousUser->$setter_token(null);
$this->userManager->updateUser($previousUser);
}
//we connect current user
$user->$setter_id($username);
$user->$setter_token($response->getAccessToken());
$this->userManager->updateUser($user);
}
/**
* {#inheritdoc}
*/
public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
$username = $response->getUsername();
$user = $this->userManager->findUserBy(array($this->getProperty($response) => $username));
//when the user is registrating
if (null === $user) {
$service = $response->getResourceOwner()->getName();
$setter = 'set'.ucfirst($service);
$setter_id = $setter.'Id';
$setter_token = $setter.'AccessToken';
// create new user here
$user = $this->userManager->createUser();
$user->$setter_id($username);
$user->$setter_token($response->getAccessToken());
//I have set all requested data with the user's username
//modify here with relevant data
$user->setUsername($username);
$user->setEmail($username);
$user->setPassword($username);
$user->setEnabled(true);
$this->userManager->updateUser($user);
return $user;
}
//if user exists - go with the HWIOAuth way
$user = parent::loadUserByOAuthUserResponse($response);
$serviceName = $response->getResourceOwner()->getName();
$setter = 'set' . ucfirst($serviceName) . 'AccessToken';
//update access token
$user->$setter($response->getAccessToken());
return $user;
}
}
Yeah, this is the error I get in the browser:
ServiceNotFoundException in
CheckExceptionOnInvalidReferenceBehaviorPass.php line 58: The service
"hwi_oauth.security.oauth_utils" has a dependency on a non-existent
service "hwi_oauth.resource_ownermap.name".
While doing a "composer update" I get this
[Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException]
The service "hwi_oauth.security.oauth_utils" has a dependency on a
non-existent service "hwi_oauth.resource_ownermap.name".
Script
Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::clearCache
handling the post-update-cmd event terminated with an exception
[RuntimeException] An error occurred when executing the
"'cache:clear --no-warmup'" command.
In config.yml you have configured hwi_oauth with "firewall_name: name".
But in security.yml you have no firewall with that name "name" (but you have a "main" named firewall not mentioned under hwi_oauth in config.yml). This causes the error message you have.
I don't understand my problem. I just want :
/ redirected /home
/home is not secured but logged user is able to navigate into
the whole website.
Non authenticated user is only able to see the homepage
People can register an account to access the whole website
So it's my security.yml config :
security:
encoders:
Siriru\AntBundle\Entity\User: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
main:
entity: { class: Siriru\AntBundle\Entity\User, property: username }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
root:
pattern: ^/$
security: false
home:
pattern: ^/home$
security: false
login:
pattern: ^/login$
security: false
register:
pattern: ^/account/
security: false
secured_area:
pattern: ^/
form_login:
check_path: /login_check
login_path: /login
username_parameter: username
password_parameter: password
logout:
path: /logout
target: /home
Registration is ok, login too. But after the redirection to the homepage, user is not authenticated (in the symfony profiler "You are not authenticated."). If I reach the secured area, i'm logged but not authenticated.
<?php
namespace Siriru\AntBundle\Controller;
use Siriru\AntBundle\Form\Model\Registration;
use Siriru\AntBundle\Form\Type\RegistrationType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use JMS\SecurityExtraBundle\Annotation\Secure;
class AccountController extends Controller
{
/**
* #Route("/login", name="login")
* #Template()
*/
public function loginAction()
{
if ($this->get('request')->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $this->get('request')->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} else {
$error = $this->get('request')->getSession()->get(SecurityContext::AUTHENTICATION_ERROR);
}
return array(
'last_username' => $this->get('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="logout")
*/
public function logoutAction()
{
// The security layer will intercept this request
}
/**
* #Route("/account/register", name="account_register")
* #Template()
*/
public function registerAction()
{
$form = $this->createForm(new RegistrationType(), new Registration());
return array('form' => $form->createView());
}
/**
* #Route("/account/create", name="account_create")
* #Template()
*/
public function createAction()
{
$em = $this->getDoctrine()->getEntityManager();
$form = $this->createForm(new RegistrationType(), new Registration());
$form->bind($this->getRequest());
if ($form->isValid()) {
$registration = $form->getData();
$user = $registration->getUser();
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($user->getPassword(), $user->getSalt());
$user->setPassword($password);
$em->persist($user);
$em->flush();
return $this->redirect($this->generateUrl('homepage'));
}
return $this->render('SiriruAntBundle:Account:register.html.twig', array('form' => $form->createView()));
}
}
I need some help =) Thank you.
Try to change your firewall configuration to catch all the urls, then set anonymous: ~ and use access_control to restrict all the urls to ROLE_USER.
The problem is that the security session is not shared by default between different firewalls.
Something like this should work:
security:
encoders:
Siriru\AntBundle\Entity\User: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
main:
entity: { class: Siriru\AntBundle\Entity\User, property: username }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
anonymous: ~
form_login:
check_path: /login_check
login_path: /login
username_parameter: username
password_parameter: password
logout:
path: /logout
target: /home
access_control:
- { path: ^/$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/home$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER }
I have this website with a login form and after I successfully logged in, I am redirected to the index. But when I click the back button, it lets me still view the login form which is not good. I want only the login form to be accessible by anonymous viewers only and not users who have logged in already. Is there a simple way to do this in symfony2? thanks
Here is my security.:
jms_security_extra:
secure_all_services: false
expressions: true
security:
encoders:
Mata\UserBundle\Entity\User:
algorithm: sha1
encode_as_base64: false
iterations: 1
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
user_db:
entity: { class: MataUserBundle:User, property: username }
firewalls:
secured_area:
pattern: ^/
anonymous: ~
form_login:
check_path: /login_check
login_path: /login
logout:
path: /logout
target: /
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER }
This may not be the best or proper way to do it, but its been the only way I could figure it out.
In my loginAction method I do this (see the $secured variable). If the user session is authenticated then I redirect them to the home/index page. I don't know of a way to do it via the firewall config because I don't believe the login page would ever have a firewall attached to it.
/**
* #Route("/login", name="login")
* #Template()
*/
public function loginAction()
{
$request = $this->getRequest();
$session = $request->getSession();
// if the session is secured (user is logged in)
// then $secured will be an object with various user information.
$secured = unserialize($session->get('_security_secured'));
if ($secured) {
return $this->redirect($this->generateUrl('home'));
}
// 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);
}
return array(
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
'embed' => $request->isXmlHttpRequest()
);
}
With symfony 4 you can solve it with allow_if :
access_control:
- { path: /login, allow_if: "!is_authenticated()" }
here's the list of expressions available within allow_if string
http://symfony.com/doc/current/security/expressions.html#security-expression-variables