How to avoid multiple user sessions in symfony 4? - php

How can I prevent a register user from accessing the site from multiple devices?
I create a function onSecurityInteractiveLogin in a EventListener
<?php
namespace App\EventListener;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
class LoginListener
{
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
$session = $event->getRequest()->getSession();
//TODO validate if that user is logged in
}
}
But I can't get if the user is logged in other device and close that session, I found a solution but in Symfony 2.3 in here.
Can anybody explain me a solution in Symfony 4?

The short answer is that you can't.
The longer answer is more complex: there's no way for your site to know which devices belong to an anonymous user; from the point of view of the server, all the requests look like they are coming from random places (which they are).
However, once the user has logged in, you can enforce more restrictions. The trick lies in how you issue authentication tokens and how to associate them with a user record in your application. There are a lot of ways to skin that cat, but they all involve keeping track of how many authentications have been granted to a user's account. Usually authentication requests get tracked with the user's IP and the user agent string from their browser to help distinguish one of the user's logins from another. E.g. when you log into GMail, you can view all the logins that have accessed the account -- that could help you identify a fraudulent login, but it also shows you how many devices (or browsers) have been used to log in.

Related

Laravel Protecting Routes from other System Request

We have this system that our students access to get access to other platforms that we provide, such as Office 365 with student license and other programs...
We have access to create this access links, create an route and add some availables TAGS that they provide, such as student email, student unique code, student cellphone...
This links work as a bridge to our Laravel 6.0 application, that link should send the student to an internal page that they can create their office 365 account (if they dont already have) and redefine their passwords, but the problem is, I cannot garantee that this logged user will not change manually (from dev inspector) the data that is passed from the route parameter, and access other student data and change their Office password.
The point is, I can control that this page can be only accessed from this previous URL (this system that the student is logged), and It works, but I cannot do a Middleware from my application that check's if the user is logged in other application, and we don't have any API to check authentication from this system..
Is there any way to protect our routes from this other system?
Controller method that receive this parameters coming from the other system and verify the previous URL:
public function index($ra, $email){
if(url()->previous() != "https://other.system/" ){
return view('errors.503');
}
$usuario = UsuariosMicrosoft::where('login', '=', $ra)->get();
return view('portaloffice.pagina', compact('ra', 'email', 'usuario'));
}
This is my route:
Route::get('office365/{ra}/{email}', 'PortalOffice\PortalOfficeController#index')->name('portaloffice.usuario');
It's generally a pretty bad idea to secure things by keeping URLs secret. They're fairly easily sniffed or guessed.
The way projects usually protect from unauthorised access is to use the Auth guard, and with a relationship between the model you're trying to protect and the User model.
After setting up the models, relationships and guards you end up with something like this in your controller:
$user = Auth::user();
$user->UsuariosMicrosoft->get()
return $user;

Laravel 5.0 multiauth

I have an application which has two parts back-end, and front-end. In the back-end admin can log in, and in the front-end the client can log in. Now it has been implemented. All application's query is done by logged in user id in both admin and client end.
Now my app needs a functionality where admin can view client data as same as client see their profile.There are a lot of things in client end. I can you use Auth::loginUsingId($client_id). Here client profile is showing perfectly but admin loggin session is lost as expected.
How to achieve this while admin login remain and admin can see client full data?
Let me introduce the simpliest way to have login as client functionality. First, define asuser and returnback routes.
Routes and actions
Route::get('/asuser/{user}', 'AdminController#asuser')
->where('user', '[0-9]+')
->name('asuser');
Route::get('/returnback', 'ClientController#returnback')
->name('returnback');
In admin's controller:
public function asuser(User $client, Request $request) {
/* insert checking if user has right either here with some field
* like $user->is_admin or using middleware settings and Policy
*/
# who user is
$fromId = Auth::user()->getId();
# logging as a client
Auth::login($client, true);
# but keeping admin in a session
$request->session()->put('adm_id', $fromId);
return redirect()->route('some-client-route')
->with('status', 'You are logged in as a client');
}
And for returning back ClientController
public function returnback(Request $request) {
$fromId = Auth::user()->getId();
# getting admin id
$admId = $request->session()->pull('adm_id');
$adminUser = User::find($admId);
if (!$adminUser) {
return redirect()->back()
->with('status', 'Not allowed');
}
# logging out as a client and logging in as admin
Auth::logout();
Auth::login($adminUser, true);
return redirect()->route('some-admin-route')
->with('status', 'Welcome back!');
}
Is it ready for production
No, it's not. That's not a great solution, it's just a glimpse how to use it. Sessions have lifetime, so if admin doesn't return back in its lifetime, session variables are lost and he becomes a client (if remember me=true, as in the code above). You can store value not in a session but in a database column.
In addition as t1gor mentioned, you must pay attention to the fact that you can't log client's actions and send events when admin is a client. That's the most serious problem of logging as a client. Anyway, I suppose, it is easier to solve that, than to move all the auth logic out of the views.
Well, hope it is helpful.
I think a good way to manage client/user profiles is to implement an user management section at your backend, display and edit your users and their profiles there.
Laravel does not provide mixed sessions. You can only be authenticated as one user at a time. If you really need this kind functionality in Laravel 5.0 you could solve this by hackish user ping-pong (e.g. login temporarily as client and switching back to admin right after).
But it seems like your problem is more Authorization-related (in contrast to Authentication). Laravel implemented an authorization layer in v5.1.11. Since v5.0 is not supported anymore you should update regardless of this feature.
You can find more information about authorization in the official documentation: https://laravel.com/docs/5.1/authorization
I would rather suggest you separate the view logic e.g. business logic into some common layer rather then doing a "login-as-client" functionality. Even though it looks like a short-cut, you'll have a whole lot of things to think about.
For instance, how do you log application events now? Add a check everwhere that the session has a adm_id and log it instead of userId? This is just one example.
What I would have done:
Separate the view (e.g. user profiles, user content, etc.) from the session so that it is accessed by the ID in the URL or whatever else method, not by currently logged in user id.
Implement a propper role-based ACL. There are plenty of packages already. In your example, you wouold have an admin role and a client role, both havin permission object view-client-profile, for instance.
In the end, this might take a lot more time for development, but would defenitely save you some time debugging/troubleshooting with the angry client on the phone. Hope that helps.
I think middleware is the best possible option to filter the content between the admin and the normal user,because the code in the middleware run before any function call.
You just only need to set the usertype in the session and filter accordingly.
Visit:https://laravel.com/docs/5.4/middleware

Custom login controller in Symfony2

For the last two days I read through multiple resources on authentication in Symfony2. I think I understand most parts, however I feel that I do not understand the whole thing. I successfully created two authentication providers and a UserProvider but I don't understand how to customize the login process.
What I try to do is writing a REST API that will be consumed by a client that I also write. The client will display a form and sent the credentials via Ajax. Therefore, I don't want to use HTTP basic auth (I don't want a browser window to pop up), I don't want to use OAuth or any token (but use credentials) and I don't want to use a form_login (the login form is rendered by Angular).
Using my custom authentication provider, I can verify that a client is logged in, using a cookie I set in the login controller. However, I don't understand how I would login (or what logged in means in Symfony) a user if I'd try to use the session/Symfony security bundle. I could just avoid all security things from Symfony and roll my own implementation but I would prefer to understand how Symfony expects the authentication provider and the login controller to work together.
What I want is one route where a client can check if it is logged in, login and logout.
GET /session # return the session if it exists, or 401
POST /session { session: { email: testy#mctest, password: test1234 }} # login
DELETE /session # destroy session
How would I achieve this in a "Symfony" way?
(Bonuspoints for explaining the general authentication concept in Symfony, outside of the authentication provider)
I'll try to make it the more verbose and simplest that I can from what I personally know :-).
This is what I understood from when I implemented my own Security layer based on the Symfony Security component. If something is not right, please tell me and I will correct it to make it more accurate.
The Security component works with firewalls. A firewall defined a "zone" that is under the responsibility of the Security Component.
For each firewall, you would usually match a pattern that would be matched with the current request. If it matches, then the Security Component tries to determine if the request is allowed to access the resource.
In order to do this, the Security component is divided into two steps :
First : authenticate the user. If the user is not authenticated, we cannot authorize him. So the component will go through its Authentication Manager in order to authenticate the user. Authenticating a user in the Symfony way means trying to create a Token (which is actually juste an instance of the TokenInterface class) matching the current request that is going to be authenticated, and then try to authenticate it.
This Token is supposed to be created thanks to Authentication Listener(s) (classes implementing the ListenerInterface interface). For instance, one will usually use a UserProvider to set a User into the Token object.
Then, when the token is created, it doesn't mean that the Token is authenticated. This Token will be authenticated thanks to the Authentication Manager. An often use kind of Authentication Managers is based on providers that will check if the credentials (or something else) is wrong with the current token. For example, a DaoAuthenticationProvider will check if the password given with the token user matches with the one in what our users provider gives to us. If not, it fails : either another provider can authenticate the current token, or this will be an authentication fail.
http://symfony.com/doc/current/components/security/firewall.html
Second : authorizing the user. For this, I suggest you to read the chapter about it in the Symfony documentation online. You can find it here. It is based on an AccessDecisionManager that will decide if the current user, based on the authenticated token, is now allowed to access the resource. This is usually made thanks to classes called Voters that will vote in order to decide if a user is allowed to access the current resource.
A voter could vote no, another could vote yes, and another could vote I don't know. The final decision is made from the AccessDecisionManager that will, based on how it is configured, determine if these votes will either allow the user ("Any yes allows the user" or forbid him ("Any no is strict").
I think that this is the basis of what you should understand, to me, about the Security component. Keep in mind that it can be very tricky to understand first, especially for the Authentication part.
Last but not least, I highly recommend you to read this whole chapter : Here !. It is the key and your bible if you want to understand what's happening inside this tricky but awesome component.
Now, for your login issue : what would be useful for you is to check how to create your custom Listener (see the authenticating part of my answer) in order to create your own business logic of "how do I authenticate my user based on the current request". Your form would be an entry point of a firewall zone that would be then pointing to this firewall zone. This listener would check if the information provided by your form is inside the request, and then create the Token. Then, you would have a custom way to authenticate your token thanks to the information you provide.
(Sorry for my english !)

How would you go about authentication for sharing classes between a public api and site?

So right now I have, for example, a class setup for users:
userClass.php
class User {
static functionlistAll($params) {
return (Array of all users);
}
}
it is used for the api:
/api/users.php
include('userClass.php');
echo json_encode(User::listAll($_GET));
and then the class is also used internally for the site as well.
Now it gets more complicated when authentication is required. The id of the user logged into the site is stored in $_SESSION['uid'], and each app that authenticates with it is granted an access key. How would authentication work for a class setup such as this?
Do you really want to create an API that can list all users on your website? This doesn't seem like a good idea. But as for the authentication bit, that can be quite tricky to get right, and shouldn't be rushed. Simply storing a unique ID is a bad idea, because then all it takes is for someone to change the ID, and voilla, they can authenticate as somebody else. Your basic idea is right, but you need some sort of digital signature (that differs from user to user) that proves that your website set the ID, and not some hacker trying to authenticate as somebody else. You probably want to change this signature frequently, so that even if someone captures a transaction with your website, they can't forge the ID.
Even better than manually authenticating, though, why not let someone else deal with it? One of the worst things about today's websites is having to create a new username and password. Why not use OAuth and FacebookConnect, and then any user who has a Google Account, a Facebook Account, or any OpenID account can automatically sign into your website using their pre-existing credentials.

storing data on an "anonymous" user

I'm currently working on developing a Symfony2 app that will not only accept
user registrations, but will allow visitors to go through almost the
entire flow of the site without creating an account or logging in. Design ideas look something like this (suggestions/improvements welcome):
When a user logs in to their account, data will be persisted to the user/related entities as normal
When an anonymous user hits the site for the first time, an "anonymous user entity" is created for them as if they'd registered, but with something like USER_<session_id> as an identifier instead of a personalized username. Any activity they perform on the site is persisted to this anonymous user entity
When an anonymous user chooses to register, their anonymous user entity is upgraded to a registered user entity, preserving their data for future use
If an anonymous user leaves the site without registering, the anonymous user entity should be cleared after a while to prevent buildup of dead data
What's the best way to go about this? Specifically, what is considered "best practice" for creating/manipulating a User entity for an anonymous user without having to place code into every controller?
I would advise against using the IP address for this, as it could cause problems for users behind a NAT.
Using a custom cookie, or the sessionId (PHPSESSID) cookie as an identifier for tracking purposes would be a better idea. Google uses this strategy for its ads business. Stand on the shoulders of giants!
I have something similar to this that I've had to do. What I did was collected the anonymous users ip address (using ($_SERVER['REMOTE_ADDR'])). I then used the ip address for tracking purposes. You can then use that when they register to append their past usage to their newly created account.
You can then just run a simple query to drop any ip address that hasn't had activity in a while (most users have dymanic ip addresses so it will change every so often anyways).

Categories