This is going to sound very weird but kindly bear with me. I have built a symfony2 application which runs pretty well on the web.
Some users in the field are having so much trouble accessing the application on their phones in the field because we all know how heavy symfony is. The situation is so bad i'm forced to heavily scale down their access to just a four page access with just three php files, 1 for authentication, one for data entry and one for viewing their entries, all these without using symfony2 but plain php.
Now to my question, how do i check password against database password/salt?
I'm using FOSUserBundle for security
Are you sure you're using FOSUserBundle for security? I think you'll find you're using the core SecurityBundle for that. The way the user's password is stored will depend on how you have configured the security system.
The MessageDigestPasswordEncoder is what is used to encode the passwords. From looking at that code you can replicated it as needed. The gist of it is merge the password and salt ($password.'{'.$salt.'}') and then run it through PHP's hash function hash($this->algorithm, $salted, true) for however many iterations are needed.
Although, not specifically related to the question you asked, I'm a little confused as to what you mean by having to scale back the PHP for mobile users? Server page generation will take just as long for mobile as desktop users so why are you reimplementing outside of the symfony framework?
you can use user manager to check user credentials validity. i've created the following function for such mission.
/**
* authorize user by username and password
*
* #param string $username
* #param string $raw_password
*/
public function authUserByUsernamePassword($username, $raw_password) {
$userManager = $this->container->get('fos_user.user_manager');
$user = $userManager->findUserByUsername($username);
// username not found
if (!$user) {
throw new \Exception("User with username: $username not found!", 0);
}
$encoder_service = $this->container->get('security.encoder_factory');
$encoder = $encoder_service->getEncoder($user);
$encoded_pass = $encoder->encodePassword($raw_password, $user->getSalt());
if($encoded_pass != $user->getPassword()){
throw new \Exception("wrong password!", 0);
}
// Get UsernamePasswordToken
$token = new UsernamePasswordToken($user, $user->getPassword(), 'main', $user->getRoles());
// Set token
$this->authUserByToken($token);
return $this->getUserToken($user);
}
Related
I am currently doing a website wherein the login URLs are varying and displays the data according to the assigned projects to them.
For example, user A can only access www.example.com/projects/proj1. This is the homepage for user A and if he logs in he uses www.example.com/projects/proj1/login
While user B can only access www.example.com/projects/proj2. This is the homepage for user B and if he logs in he uses www.example.com/projects/proj2/login
Please note that proj1 and proj2 are varying depending on the database. So I have to check first that these projects are already registered in the database.
I am thinking of having a route like this.
For web.php
Route::get('/projects/{project_name}', 'PageHandler\CustomPageController#projects');
Route::get('/projects/{project_name}/login', 'PageHandler\CustomPageController#login');
Route::put('/projects/{project_name}/auth/{user}', 'PageHandler\TestUserPageController#auth');
Then my customepagecontroller.php looks like this
class CustomPageController extends Controller
{
public function projects(string $projectName)
{
if (auth()->user() == null)
return redirect('/projects'. '/' . $projectName . '/login');
}
public function login(string $projectName)
{
return view('login')->with('projectName', $projectName);
}
public function auth(Request $request, string $projectName)
{
$username = $request->username;
//How to set $username as logged in?
// rest of the code to show the home page after authentication
}
}
login.blade.php basically just looks like a form submitting username and password and calling auth of CustomPageController with a string parameter for the URL
So my question is how can I set $username as logged in already using the Auth of Laravel? Or should I create my custom Authentication Controllers?
Now, this is the only approach I have in mind for me to enable the logging in of users to varying URLs. Please let me know if you have better approach.
Thank you!
If you only want to limit the project the users can access, I do not see a need to use 2 different login URLs (please correct me if there is a reason why you want different URLs for that), instead, you simply find which project the user belongs to from the database.
For authentication, Laravel allows you to implement authentication in a very easy way, you can refer to the documentation. Using Laravel's authentication would be easier and safer than writing your own one, and even if the default functionalities it provides may not be exactly the same as those you would want to achieve, you can still add your own things, which is still a lot easier than implementing it from scratch.
As for setting a user as logged in with Laravel's authentication services, you can use Auth::login($user);. Here, $user must be an implementation of the Illuminate\Contracts\Auth\Authenticatable contract. You can refer to this part of the documentation for more details.
Let's say I have an invoice entity. Invoice belongs to some user (invoices.user_id).
If the user enters myapp.com/invoices/1 he needs to sign in to gain access to his invoice. That's pretty normal.
Sometimes invoices.user_id is null (invoice owner doesn't have an account in our system), but we have an invoices.phone_number column.
The goal is to create an authentication system based on SMS code verification for users that don't have the account in our system. If the user confirms that he indeed owns phone number related to the invoice (code verification) I want to grant him temporary access (15 min) to this invoice details page (and only this page).
My first idea was to use a JWT token stored in the session.
My second idea was to use a custom firewall.
Is there any better approach?
Create a kernel.request listener. This way you can act, before anything is executed, and whole application is oblivious to the fact that the user can be logged out any minute.
Call a "service" which will validate the token. If the token is not valid, clear authentication status and override the request. For instance, redirect the user to a "you need to pay again" page.
This way you don't need to modify any code, execute any voters and so on, your whole application can be protected.
As for the authentication itself, go for a custom guard, where you can fully control how the authentication process will work.
You can authenticate a dummy user for 15 minutes using the following action:
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
/**
* confirm that the user indeed owns
* phone number related to the invoice (code verification)
*/
//create a user for this task only and fetch it
$user = $em->getRepository(User::class)->find(1);
//firewall name used for authentication in security.yml
$firewall = "main_secured_area";
$token = new UsernamePasswordToken($user, null, $firewall, $user->getRoles());
$this->get('security.token_storage')->setToken($token);
$this->get('session')->set("_security_$firewall", serialize($token));
//$lifetime takes number of seconds to define session timeout 15min = 900sec
$this->container->get('session')->migrate($destroy = false, $lifetime = 900);
//fire the login event manually
$event = new InteractiveLoginEvent($request, $token);
$this->get("event_dispatcher")->dispatch("security.interactive_login", $event);
return $this->render('default/index.html.twig');
}
I use a SQLite database instead of MySQL for authentication on my Laravel app. The registration works perfectly, but the login does not work correctly.
I get the following error:
These credentials do not match our records.
Please, help me to solve it!
When you register a new user, the password about to be stored must be encrypted with the bcrypt() helper, such as bcryp($request->password).
Otherwise the credentials will not match during login time.
Also, if you did not use the users migration packed with Laravel, the password field must be a minimum of 64 characters in lenght.
Add this code in your User model
/**
* Hash password by bcrypt before save in database.
*
* #param type $password
*/
public function setPasswordAttribute($password)
{
if (isset($password)) {
$this->attributes['password'] = bcrypt($password);
}
}
everything is working but you have to ensure that
your SQLite DB is not open somewhere else
if it will open in DB browser or any other such type of tool just close that and try again later
will work fine
I'm using FOSUserBundle. What is the difference between these two?
$this->get('fos_user.user_manager');
...and...
$this->getUser();
I've found I've used both of the above at different times and everything works fine.
I'm guessing the first one is from FOS and the second one is the default one, but I'm guessing I should always use the same one.
This is one piece of code I've used:
$user = $this->getUser();
if($user) {
$email = $user->getEmail();
} else {
$email = "no email";
}
..and another...
$userManager = $this->get('fos_user.user_manager');
$user = $userManager->findUserBy(array('memberID' => '123'));
...so should I have used the same method for both?
With $this->getUser() is only a shortcut to
$this->get('security.context')->getToken()->getUser()
So this means you get the user object according to the current security token. It's perfect and easy, when you want to retrieve the actual logged in user.
But if you want to get other users, fos_user.user_manager is the choice, as it has methods to find users easy and hiding the implementation behind. And it provides also methods for creating new users and updating them. And also if you retrieve the current logged in user with $this->getUser() and made modifcations to them, you should use the fos user manager to update them. Take a look in the docs for more!
They return different objects. $this->get('fos_user.user_manager') returns a FOS\UserBudle\Doctrine\UserManager object and $this->getUser() returns a FOS\UserBundle\Model\User object. The former handles users and the latter is a user. So no, you are using it right.
Where the two differ is in saving a user or creating a new user. If using the FOSUserBundle, you should always use the $this->get('fos_user.user_manager') method. This gives you access to the updateUser() function that works with the FOSUserBundle to make sure it updates all the user attributes that you don't need to explicitly declare in your User model, like date_created and roles.
That function is different than using Doctrine to persist() and then flush() the model.
I'm trying to get authentication working in Symfony2.
My users use a login form somewhere else on the site that is not controlled by symfony2.
what I would like is Symfony to detect the users are already logged in and authenticated by reading a session variable and comparing against the DB.
I don't want to reimplement a login form on the symfony part of the website.
In symfony 1.x, for example, I would simply overload the BasicSecurityUser class and use the setAuthenticated method, but it seems this is not possible in Symfony2.
Is there any simple way of achieving the same result?
Thank you!
Once you know the user name of the authenticated user, you can log them in with:
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
class MyController {
// Takes either userName or an actual user object
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_area.provider.key'); // secured_area
$token = new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());
$this->get('security.context')->setToken($token);
return $user;
}