Login form using createForm with symfony2 - symfony

I am newbie in Symfony. I just want to known if is it possible to create login form using createForm method.
I already have a LoginTYpe class implemanting buildForm method which create an array whith all necessary fields for the form (_username, _password, submit button).
In my controller, i use :
$form = $this->createForm(new LoginType(), $login, array('action' => $this->generateUrl('login_check')));
$login is just an instance of flat data class with members (_username, _password) getters and setters in Entity directory.
I render with :
return $this->render('LppdgProfessionalBookletBundle:Login:login.html.twig', array('error'=> $error, 'form' => $form->createView()));
Source of HTML form show :
name="login[_username]" for "name" field and name="login[_password] for"password" field.
Is there any way to avoid name field to be in array.
Just more : when using manual form all works fine.
Thanks.
Laurent

Set the names in your app/config/security.yml file:
form_login:
provider: cerad_account_user_provider
login_path: cerad_account_login
check_path: cerad_account_login_check
default_target_path: cerad_tourn_home
username_parameter: cerad_account_signin[username] # Adjust these three lines
password_parameter: cerad_account_signin[password]
csrf_parameter: cerad_account_signin[_token]
csrf_provider: form.csrf_provider
intention: authenticate

Related

JWTRefreshTokenBundle change user_identity_field Symfony 5.4 + ApiPlatform

I am using : Symfony 5.4 + ApiPlatform + JWTRefreshTokenBundle 1.1
JWTRefreshTokenBundle => https://github.com/markitosgv/JWTRefreshTokenBundle
I need to change this parameter "user_identity_field" but there is no way to change this :
I tried to change the Yaml =>
gesdinet_jwt_refresh_token:
user_identity_field: email
user_provider: app_user_provider
I tried to modify this function in my user provider (app_user_provider) entity User.php :
public function getUserIdentifier(): string
{
return (string) $this->id;
}
Right now the better I can get is to have the ID instead of the E-mail in the user name column in my database, but as soon as I try to refresh the token, I get this message => " 401 "Invalid credentials".
I am tried to have "ID" instead of "E-MAIL" as user_identity_field.
Has anyone found a solution ?
Thanks.
Sf 5,4+
#gesdinet_jwt_refresh_token.yaml
gesdinet_jwt_refresh_token:
user_identity_field: email
ttl: 2592000
firewall: login
user_provider: security.user.provider.concrete.app_user_provider
i think you need to change the app_user_provider in the security.yaml file :
app_user_provider:
entity:
class: App\Entity\User
property: id
jwt:
lexik_jwt:
class: App\Entity\User
i think the problem is : symfony try to authneticate user with the app_user_provider and you have a conflict because id's are in your JWT but framework search for email.
For verify that you can check your tokens on : https://jwt.io/
The JWTRefreshTokenBundle (gesdinet/jwt-refresh-token-bundle) is build upon the JWTAuthenticationBundle (lexik/jwt-authentication-bundle), which is the bundle that defines the user_identity_field configuration:
# config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
user_identity_field: 'email'

Symfony CSRF check failing

Getting:
"The CSRF token is invalid. Please try to resubmit the form."
Controller:
$message = new Message();
$form = $this->createForm(new MessageType($locales), $message);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
...
// never reached
}
}
Inspecting the raw POST data, it does include a _token field:
message[title]: Test
message[subtitle]:
message[_token]:0LpwU3llG-FhyEc0o12b7rU0Pg2zSAb7xpUwAF1Xw3g
So it's not the issue lots are having missing {{ form_rest(form) }}
Have you read this from the docs (http://symfony.com/doc/current/cookbook/security/csrf_in_login_form.html)?
First, configure the Security component so it can use CSRF protection. The Security component needs a CSRF token provider. You can set this to use the default provider available in the Security component
# app/config/security.yml
security:
# ...
firewalls:
secured_area:
# ...
form_login:
# ...
csrf_token_generator: security.csrf.token_manager
I think maybe you need to specify which token manager to use for the _token

Login in symfony2

I'm trying to implement very basic authentication in Symfony2. Here are main parts of the code I really don't see any problem
EDIT
complete security.yml
jms_security_extra:
secure_all_services: false
expressions: true
security:
encoders:
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:
memory:
users:
user: { password: userpass, roles: [ 'ROLE_USER' ] }
admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
firewalls:
login:
pattern: ^/login
anonymous: ~
secured_area:
pattern: ^/
stateless: true
form_login:
login_path: /login
check_path: /login_check
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER }
This works fine, anonymous user is always redirected to loginAction controller.
EDIT
Here is the complete code
<?php
namespace AcmeBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
class SecurityController extends Controller {
public function loginAction() {
$providerKey = 'secured_area';
$token = new UsernamePasswordToken('test', 'test', $providerKey, array('ROLE_USER'));
$this->container->get('security.context')->setToken($token);
return $this->redirect($this->generateUrl('fronthomepage'));
}
}
I don't see any problem, anonymous user is redirected to loginAction, there is created authenticated user, saved to token and than redirected to secured area as an authenticated user. Unfortunately my code ends with redirect loop which looks like security firewall doesn't accept user as authenticated. Do you see any problem?
Well, your controller job is to render just form but not to populate security context. Symfony2 security firewall will do that for you automatically. You don't need to handle it unless you want to build you own custom authentication.
In other words, your job is to display the login form and any login
errors that may have occurred, but the security system itself takes
care of checking the submitted username and password and
authenticating the user.
Please read this document for clear picture.
If you want to do some custom stuff when a user logs in, in Symfony2 you have to add an event listener that will fire after the user successfully logged in. The event that is fired is security.interactive_login and to hook to it you have to specify this in services.yml file form your bundle Resources/config directory:
Pretty sure you need an actual user object before setting an authenticated user. I did something like this:
class BaseController
protected function setUser($userName)
{
if (is_object($userName)) $user = $userName;
else
{
$userProvider = $this->get('zayso_core.user.provider');
// Need try/catch here
$user = $userProvider->loadUserByUsername($userName);
}
$providerKey = 'secured_area';
$providerKey = $this->container->getParameter('zayso_core.provider.key'); // secured_area
$token = new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());
$this->get('security.context')->setToken($token);
return $user;
}
However doing something like this bypasses much of the security system and is not recommended. I also wanted to use a 3rd party authentication system (Janrain). I looked at the authentication system and initially could not make heads or tails out of it. This was before the cookbook entry existed.
I know it seems overkill but once you work through things then it starts to make more sense. And you get access to a bunch of nifty security functions. It took me quite some time to start to understand the authentication system but it was worth it in the end.
Hints:
1. Work through the cook book backward. I had a real hard time understanding what was going on but I started with adding a new firewall to security.yml and then adding the alias for my security factory. I then sort of traced through what the factory was being asked to do. From there I got the listener to fire up and again traced through the calls. Finally the authentication manager comes into play. Again, time consuming, but worth it in the end. Learned a lot.
One thing that drove me crazy is that classes are scattered all over the place. And the naming leaves something to be desired. Very hard to get an overview. I ended up making my own authentication bundle then putting everything under security.
If you want another example of a working bundle then take a look at: https://github.com/cerad/cerad/tree/master/src/Cerad/Bundle/JanrainBundle

FOSFacebookBundle : Choose username before registration

I installed FOSUserBundle and FOSFacebookBundle using this method : How to make a separate url for signin via Facebook using FOSFacebookBundle in Symfony2. Actually I didn't really understand the whole security thing process, but it is working fine now.
When someone is using facebook signup, I would like the possibility to choose an username before being registred (instead of the facebook id as username)... what I do is that I send a POST parameter to the facebook login route but I can't find the controller where the registration is being processed.
What would be the best practise ? Where should I retrieve the username (the POST param) and set it ?
Here is my configuration in security.yml :
firewalls:
public:
pattern: ^/
fos_facebook:
app_url: "http://www.appName.com"
server_url: "http://local.appName.com/app_dev.php/"
login_path: /login
check_path: /login_check/facebook
provider: appName.facebook.provider
form_login:
login_path: /login
check_path: /login_check/form
provider: fos_userbundle
csrf_provider: form.csrf_provider
and here is the routing I use to signup with facebook :
_security_check_facebook:
pattern: /login_check/facebook
There is no controller processing authentication, ever authentication method add a listener that will trigger on check_path, and do the trick.
Authentication process shouldn't be responsible to add custom user data. If you followed FOSFacebookBundle documentation you should have a custom user provider that store the user on database, this may confuse a bit but this is not a registration step, is an authentication step. Adding a username is much similar to a profile editing.
To help you i should see your implementation (how do you send post parameter ? ) but you could try something like this:
send post parameter to a custom controller (implemented by you)
temporary store username (maybe user session data ?)
trigger authentication process (this depend on your implementation but essentially we are talking about authenticate on facebook and then post to login_check path)
add username at the authenticated user
This could work, but I never did something like this.
Old question, but I was working on a similar problem. I wanted to add a role to a user when they were registered on the first time they connected with Facebook.
If you follower these steps from the FOSFacebookBundle documentation, then you can tap into the Facebook connected user's "registration", take a look at the FacebookProvider class.
In loadUserByUsername method there is this part:
if (!empty($fbdata)) {
if (empty($user)) {
$user = $this->userManager->createUser();
$user->setEnabled(true);
$user->setPassword('');
}
$user->setFBData($fbdata);
...
After the setPassword within the if is where I added the role for my user.
For your original problem of choosing the username... notice that call to setFBData?
In the User entity the method looks like this:
public function setFBData($fbdata)
{
if (isset($fbdata['id'])) {
$this->setFacebookId($fbdata['id']);
$this->addRole('ROLE_FACEBOOK');
}
if (isset($fbdata['first_name'])) {
$this->setFirstname($fbdata['first_name']);
}
if (isset($fbdata['last_name'])) {
$this->setLastname($fbdata['last_name']);
}
if (isset($fbdata['email'])) {
$this->setEmail($fbdata['email']);
}
}
and setFacebookId() looks like this:
public function setFacebookId($facebookId)
{
$this->facebookId = $facebookId;
$this->setUsername($facebookId);
}
So that's where the username comes from. I guess that is a flow where you could plug your chosen username into and set it instead of the $facebookId.
I did another way for this.
I used GraphAPI.
public function setFacebookId($facebookId)
{
$this->facebookId = $facebookId;
$username = json_decode(file_get_contents('http://graph.facebook.com/'.$this->facebookId))->username;
$this->setUsername($username);
}
It takes the Facebook username, an unique one.

symfony2 login by username or email

I'm using the standard authentication mechanism of Symfony2 and I want to let the user use either his username or email to login, but I can't find out why it's not working. I've tested the repository class and it works as expected. I've followed this how-to.
Here's my user provider class:
<?php
namespace My\UserBundle\Entity;
use Doctrine\ORM\EntityRepository ,
Symfony\Component\Security\Core\User\UserProviderInterface ,
Symfony\Component\Security\Core\User\UserInterface;
/**
* UserRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class UserRepository extends EntityRepository implements UserProviderInterface
{
function loadUserByUsername($username)
{
$qb = $this->createQueryBuilder('u') ;
return
$qb->select('u')
->where(
$qb->expr()->orx(
$qb->expr()->like('u.username' ,':username') ,
$qb->expr()->like('u.email' ,':username')
)
)
//->andWhere($qb->expr()->eq('u.enabled' ,'true') )
->setParameters(array('username' =>$username ) )
->getQuery()
->getResult() ;
}
function refreshUser(UserInterface $user)
{
return $this->loadUserByUsername($user->getUsername() );
}
function supportsClass($class)
{
return $class === 'My\UserBundle\Entity\User';
}
}
I propose a more simple approach that only requires to edit security.yml file.
You must to create two diferent security providers but both using the same User class. The first has username as property and the second has email as property.
Then you create a chain_provider that includes the two providers and use it in your firewall section
security:
providers:
chain_provider:
chain:
providers: [db_username, db_email]
db_username:
entity:
class: MyUserBundle:User
property: username
db_email:
entity:
class: MyUserBundle:User
property: email
firewalls:
default:
anonymous: ~
provider: chain_provider
form_login:
login_path: /login
check_path: /login
I don't know if this approach is a clean practice, but is fast, simple and it works ok.
well guys the thing is in my security.yml i had this
providers:
main:
entity: { class: My\UserBundle\Entity\User ,property : username}
so i had to take off that parameter property :username
Taking off the property: username lets the UserProviderInterface load the user as expected when it logs in, but does not call the refreshUser() method as expected. I put in checks to see if it gets called but it doesn't.
The class that reloads the user on each access is ContextListener::refreshUser(TokenInterface $token) method. In this the interface iterates through the UserProviders and calls the refreshUser that first returns a non-null user.
I could make sure of this because, in the original load, I combine all different entities to make one SQL call instead of 7. And when the user reloads, it calls 7 times.
Also the method EntityUserProvider::refreshUser() doesn't call the repository's method and instead reloads from the database directly.
Your provider class is correct, and you are correct that the problem is in security.yml, however, your solution is incorrect.
According to the documentation, your security.yml file should look like this:
security:
# ...
providers:
administrators:
entity: { class: MyUserBundle:User }
Notice that the class is defined as the Bundle, not the direct class.
The way you have your code right now, symfony is completly ignoring your repository class until you define your security.yml correctly. And as #Anand pointed out, just removing the property does not invoke refreshUser. However, it looks like if you are using your own Repository, you do not need to define the property (since it's being defined in your query).

Resources