Using custom authentication on Laravel 6 - php

I would like to manually authenticate the users in my company. The issue is that, I have 2 tables, called Student and Staff in the Oracle database.
As for the Student table, I get the idea of overriding the built in Auth method provided through the auth scaffolding command as the username and password are stored right into the table.
As for the Staff table, the password is stored a different column/table and encrypted using a stored procedure/package so the only way to get the user validation is by calling the package which only returns 0 or 1 only.
What I have done,
I wrote my own Routes, and added my own functions in LoginController.
public function loginStaff(Request $req){
$username = Str::upper($req->input('username'));
$password = $req->input('password');
$users = PortalUser::where('ID', $username)->firstOrFail();
if ($users->user_type == 'STAFF'){
$queryResult = DB::select('select PACKAGE.validStaff(?,?) from dual',[$username, $password]);
if($queryResult == 1){
//this is where I would like to auth the user.
//using Auth::Attempt and Auth::Login will only run the default query
}
}
I have successfully returned value of 1 and 0 in the controller.
So is there anything that I am missing?
Or should I manually set the session by myself using the session() method?
Thank you.

If you want to manually authenticate users, you can easily use sessions. Have the following code as reference:
//this is where I would like to auth the user.
//using Auth::Attempt and Auth::Login will only run the default query
// typically you store it using the user ID, but you can modify the session using other values.
session()->put('user_id', user id from database here);
And if you want to check whether user is authenticated, modify RedirectIfAuthenticated middleware to this:
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (session()->has('user_id')) {
return redirect( custom path here );
}
return $next($request);
}
}
When you want to logout the user, simply destroy the session key
session()->forget('user_id');
**Note: ** many broadcasting and addons use Laravel's authentication system (Guards) and you may need to hook into their code if you want to use them with your custom auth system

Laravel provides Custom Session Drivers which you can use to create or delete your sessions
<?php
namespace App\Extensions;
class MongoSessionHandler implements \SessionHandlerInterface
{
public function open($savePath, $sessionName) {}
public function close() {}
public function read($sessionId) {}
public function write($sessionId, $data) {}
public function destroy($sessionId) {}
public function gc($lifetime) {}
}
Hope it helps, if not then comment down below. Will help you out.
###### Update #######
I think then you do have to make custom HTTP sessions from Laravel
Step 1: Create another table in your database for session, like this;
Schema::create('sessions', function ($table) {
$table->string('id')->unique();
$table->unsignedInteger('user_id')->nullable();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->text('payload');
$table->integer('last_activity');
});
Step 2: Store data in the session, you will typically use the put method or the session helper;
// Via a request instance...
$request->session()->put('key', 'value');
// Via the global helper...
session(['key' => 'value']);
Step 3: Get the key for specific user when your function returns 1
$value = $request->session()->get('key', function () {
return 'default';
});
Step 4: Delete the session, after some time you need to delete the session for security reasons then you can do.
$value = $request->session()->pull('key', 'default');

Related

Function AFTER authentication and BEFORE view (laravel)

I'm trying to get settings from the database and put them in the config,
my function need the user id so it can bring his settings only,
in the service provider ( boot function ) there is no authentication yet, can you please advise me to the right place to run my function, please note that I need it to run before the view get rendered because there are settings for the layout inside it, this is my function :
// public static becouse it's inside Class//
public static function getAppSettings(){
if (!config('settings') && Auth::check()) {
$user_id = Auth::user()->id;
$settings = AppSettings::where('user_id', $user_id)->get()->all();
$settings = Cache::remember('settings', 60, function () use ($settings) {
// Laravel >= 5.2, use 'lists' instead of 'pluck' for Laravel <= 5.1
return $settings->pluck('value', 'key')->all();
});
config()->set('settings', $settings);
}else{
// this is for testing//
dd('no');
}
}
without the auth, it can work inside the service provider ( boot function ) but it will bring all settings for all the users.
You can create middleware for this.Middleware calls after routes and before controller
php artisan make:middleware Settings
This will create below class
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class Settings
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
// exicute your logic here
return $next($request);
}
}
You can call your method inside handle and before next
You can read more about this in
https://laravel.com/docs/8.x/middleware

Invalidating Session not logging out - Laravel 5

So basically a user can stay logged in accross iPhone, Chrome, Firefox and multiple browsers and I want to stop that, and only allow the user to be logged in at (1 session only at a time).
Meaning:
When the user logs in somehwere else... it logs them out on the other.
I have added the following to the very bottom of my LoginController.php
/**
* The user has been authenticated.
*
*
* #return mixed
*/
protected function authenticated()
{
\Auth::logoutOtherDevices(request($password));
}
I also uncommented the line: \Illuminate\Session\Middleware\AuthenticateSession::class, in my Kernel.php
But it still allows the user to stay logged in across many browsers.
I would like it to invalidate the session correctly and log the user OUT everywhere else wherever it is logged in.
Here is my complete LoginController.php just incase I have made some mistake:
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo = '/dashboard';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
$this->middleware('guest:admin', ['except' => 'logout']);
}
public function username()
{
return 'username';
}
public function logoutUser()
{
$this->guard()->logout();
$this->session()->invalidate();
return redirect('/login');
}
}
/**
* The user has been authenticated.
*
*
* #return mixed
*/
protected function authenticated()
{
\Auth::logoutOtherDevices(request($password));
}
SO, basically this is a custom logoutotherdevices which I have made with the help of middleware.
Basically the idea is to pass active_key value as random string and keep it in user table for that user as well as store it in session, we need to create new value at time of registration,update the column value for every user login and set it to null on logout, so if in not matches with the value stored in session, we would logout the user from that session.
Step 1:
Create the table column which stores random string in user table.
For that I have added a new column in user table(App\User Model), named active_key.
$table->string('active_key')->nullable();
Added a column in migration. Then migrate it using php artisan migrate.
NOTE: If you don't want to create a new column you use the remember_token column key though I will suggest not to use it,as it is originally used for some other purpose.
IMP : At the end, add active_key to $fillable array in User Model
Step 2:
Note in your Auth\LoginController, we would override the authenticated and logout method,
Just as the class Auth\LoginController begins, we can see trait included as use AuthenticatesUsers, I have changed it to
use AuthenticatesUsers{
logout as logoutFromAuthenticatesUsers;
}
so to change the default logout() function name defined in trait AuthenticatesUsers to logoutFromAuthenticatesUsers() and then I have used it in logout function as seen below.
protected function authenticated(Request $request, $user)
{
$this->generateAndUpdateActiveKey($request, $user);
return redirect()->intended($this->redirectPath());
}
public function logout(Request $request)
{
$user = \Auth::user();
if(!$user->update(['active_key' => null])){
throw new \Illuminate\Database\Eloquent\ModelNotFoundException();
}
if($request->session()->has('active_key')){
$request->session()->forget('active_key');
}
$this->logoutFromAuthenticatesUsers($request);
return redirect()->intended($this->redirectPath());
}
What it will do is authenticated method will be called after the user is authenticated successfully, then the generateAndUpdateActiveKey() will update the active_key column in the user table and store the same value in session.
generateAndUpdateActiveKey() is defined in App\Http\Controller to avoid redundancy.
public function generateAndUpdateActiveKey(\Illuminate\Http\Request $request, \App\User $user){
$activeKey = substr(md5(time().\Auth::user()->name),0,16);
$request->session()->put('active_key', $activeKey);
// $request->session()->put('loggedin_ip',$request->ip());
if(!$user->update(['active_key' => $activeKey])){
throw new \Illuminate\Database\Eloquent\ModelNotFoundException();
}
}
In case of logout, we override it from the trait AuthenticatesUsers used to delete the active_key value for the login user from the users table and then remove it from session then call the logout function.
To create new active_key value when a user registers
For that I have overridden the registered() method from trait RegistersUsers( added as use RegistersUsers;)
So in, Auth\RegisterController.php
protected function registered(Request $request, $user)
{
$this->generateAndUpdateActiveKey($request, $user); // same method defined in controller
return redirect()->intended($this->redirectPath());
}
Step 3:
With all set up we now just need to check that every time the page is refreshed, the current session is the only session for that user.
For that we use a middleware, I have named it ActiveLogin.
php artisan make:middleware ActiveLogin will create a basic layout of middleware in App\Http\Middleware -> ActiveLogin.php.
Register the middleware in App\Http\kernel.php, I have added it as route middleware, you can even add it along the group of web middleware to check as group for each routes.
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
.....
'activelogin' => \App\Http\Middleware\ActiveLogin::class,
];
We will change the ActiveLogin Middleware as
<?php
namespace App\Http\Middleware;
use Closure;
class ActiveLogin
{
/**
* Handle an incoming request to check the current user is logged in current device.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ( \Auth::check() ){
if ($request->session()->has('active_key')){
if(\Auth::user()->active_key != null && strcmp(\Auth::user()->active_key, $request->session()->get('active_key')) !== 0){
\Auth::logout();
return redirect()->to('/')->with(['warning' => 'Your session has expired because your account is logged in somewhere else.']);
}
}
}
return $next($request);
}
}
Then you can use it in your routes web.php
Route::group(['middleware' => ['activelogin']], function(){
Route::get('/', function () {
return view('welcome');
});
Route::get('/home', 'HomeController#index')->name('home');
});
You can show your warning in welcome.blade.php as
#if(session()->has('warning'))
<div class="alert alert-warning" role="alert">
{{ Session::get('warning') }}
</div>
#endif
Note: If you add it in route group of web then it will be applied automatically to all the routes in web.php .
Demo
This is the first login of user try2 from separate session(incognito mode),
This is the second login of user try2 from separate session(normal mode),
Once the same user logs in from separate session(normal mode), if user tries to go to some other link or refeshes page from the first session(in incognito), user is logged out from first session with warning message as shown in welcome page picture below

how to login using username instead of email in laravel nova?

In my existing laravel project, I handle logins with a username.
I've already replaced email with username on app\Nova\User.php
please help me how can I do login with username in laravel nova.
Thanks in advance.
I realize this is an old post but I haven't found anything more recent and I figured I'd show my method of handling this situation.
I've created a new controller
<?php
namespace App\Http\Controllers\Nova;
use Illuminate\Http\Request;
class LoginController extends \Laravel\Nova\Http\Controllers\LoginController
{
public function authLogin(Request $request)
{
$request->request->add(['username' => $request->email]);
return $this->login($request);
}
public function username()
{
return 'username';
}
}
and create a new route to override the nova route
Route::post('nova/login', 'Nova\LoginController#authLogin');
So you'll notice I extended the Nova controller. In our controller we'll add the username function that will return your 'username' field.
Now Nova will use the username field provided from your new function to login.
BUT...... it's not that simple buddy boy.
Now Nova thinks the form is going to pass your username field as 'username' provided by your new username function.
So let's create a function called authLogin in our new controller. We'll grab the current form field 'email' and append a new 'username' field to our request.
Now pass the request off to the login function provided by Nova with the new field appended to the request and let Nova handle the rest.
I'm not totally sure this method is a 'correct' way but it prevents from overriding core files or relying on copied core functions.
After study about this issue, I've solved my own question.
here's the way I go.
app\Provider\NovaServiceProvider.php
use Illuminate\Support\Facades\Route;
//.....
protected function routes()
{
$this->myLoginRoutes();
Nova::routes()
// ->withAuthenticationRoutes()
->withPasswordResetRoutes()
->register();
}
/** ADD YOUR OWN LOGIN ROUTE */
public function myLoginRoutes($middleware = ['web'])
{
Route::namespace('App\Nova\Http\Controllers\Auth')
->middleware($middleware)
->as('nova.')
->prefix(Nova::path())
->group(function () {
Route::get('/login', 'LoginController#showLoginForm');
Route::post('/login', 'LoginController#login')->name('login');
});
}
Copy
nova\src\resources\views\*
to
app\resources\views\vendor\nova\*
and you can free to modify what you want in view.
Copy
nova\src\Http\Controllers\LoginController.php
to
app\Nova\Http\Controllers\Auth\LoginController.php
and modify namespace to App\Nova\Http\Controllers\Auth;
app\Nova\Http\Controllers\Auth\LoginController.php
// ADD THIS METHOD TO OVERRIDE
public function username()
{
return 'username';
}
assume you've changed email to username in users migration
for me it helped to use the loadViewsFrom method in the boot function of the NovaServiceProvider
$this->loadViewsFrom(__DIR__.'/../resources/views/vendor/nova', 'nova');
Just copy the templates needed to app/resources/views/vendor/nova/...
See also: https://laravel.com/docs/5.6/packages#views
This change in addition to the change in the user model should do most of the trick
/**
* Get the login username to be used by the controller.
*
* #return string
*/
public function username()
{
return 'username';
}
regards,
Andreas
Add this into your
novaFolder\src\Http\Controllers
--
/**
* Get the login username to be used by the controller.
*
* #return string
*/
public function username()
{
return 'username';
}
This should do it, don't forget to add username column field into your table

set custom sql for Authorization in Laravel 5

I am new on Laravel and use Authorization. I am looking for the way to change default sql for Auth. Actually, Laravel does it using this simple sql command at below:
SELECT * FROM users WHERE login="something" AND password = "something" LIMIT 1
I am trying to change default sql like this:
SELECT
u.id, u.name, c.company
FROM
users u, companies c
WHERE
u.login="something" AND
u.password = "something" AND
u.companyId = c.id
LIMIT 1
I understood that I should create custom Authorization system: crate new user Provider and Auth Provider.
Firstly, I created Auth folder inside App and added there CustomUserProvider.php
CustomUserProvider.php
<?php namespace App\Auth;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Contracts\Auth\UserProvider as UserProviderInterface;
use App\Models\User;
class CustomUserProvider implements UserProviderInterface {
protected $model;
public function __construct(UserContract $model)
{
$this->model = $model;
}
public function retrieveById($identifier)
{
}
public function retrieveByToken($identifier, $token)
{
}
public function updateRememberToken(UserContract $user, $token)
{
}
public function retrieveByCredentials(array $credentials)
{
}
public function validateCredentials(UserContract $user, array $credentials)
{
}
}
My customAuthProvider.php file, in App/Providers:
<?php namespace App\Providers;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use App\Auth\CustomUserProvider;
use Illuminate\Support\ServiceProvider;
class CustomAuthProvider extends ServiceProvider {
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
$this->app['auth']->extend('custom',function()
{
return new CustomUserProvider(new User);
});
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
}
At the end I set driver to custom in config/Auth.php
'driver' => 'custom'
I am looking for the way using this custom classes how can I use custom sql command for Authorization (Login)?
Or maybe this way is wrong?
If all you need are additional constraints on the query that fetches user from the database during authentication, there is much simpler way to do that.
First of all, Laravel offers an AuthenticatesUsers trait that you can use in your controller to handle authentication requests. The default implementation fetches user from the database using username field and then, if matching user is found, it validates their password.
The list of attributes that is used to fetch user from the database can be customized by overriding getCredentials method in your controller. In your case the following should be enough to load user using their username and company id:
protected function getCredentials(Request $request)
{
return $request->only($this->loginUsername(), 'password', 'companyId);
}
Once you add that, user should provide their username, companyId and password in the login form and they will be authenticated only if there exists a user with given username that belongs to given company and the password provided is valid.
UPDATE: If you decide not to use the trait, but want to authenticate users manually, you can do so in a really similar manner. When calling Auth::attempt() you just need to pass all the criteria that should be used to authenticate the user, e.g.:
Auth::attempt([
'username' => Input::get('username'),
'companyId' => Input::get('companyId'),
'password' => Input::get('password')
]);
I tried this package and it helped me:
https://github.com/ollieread/multiauth/

How to log user out of Symfony 2 application using it's internal handlers

Symfony implements the functionality of logging user out and killing cookies. There is a LogoutListener which delegates those action to couple of logout handlers: CookieClearingLogoutHandler and SessionLogoutHandler.
If we want to log user out of application manually, I think the best course of action would be to call those handlers and not to implement (duplicate) such low-level logic yourself.
Is it possible to do so?
You can implement an extended logout-listener by overriding the default security.logout_listener service.
The default LogoutListener::requiresLogin(...) method only checks if the request-path equals a firewall's logout_path setting.
It looks like this:
protected function requiresLogout(Request $request)
{
return $this->httpUtils->checkRequestPath(
$request, $this->options['logout_path']
);
}
Now let's assume you want to perform a logout if the session-parameter logout is set to true.
Therefore we extend the LogoutListener class and add our custom requiresLogout() method with additional logic.
namespace Your\Name\Space;
use Symfony\Component\Security\Http\Firewall\LogoutListener;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class MyLogoutListener extends LogoutListener {
/**
* Whether this request is asking for logout.
*
* #param Request $request
*
* #return Boolean
*/
protected function requiresLogout(Request $request)
{
if ( $request->getSession()->get('logout') ) {
return true;
}
return parent::requiresLogout($request);
}
Afterwards we simply override thesecurity.logout_listener.class container-parameter with our custom class in our config.yml .
parameters:
security.logout_listener.class: \Your\Name\Space\MyLogoutListener
Now you can logout a user inside a ...Controller like this:
public function someAction(Request $request) {
// ... some logic here
if ($condition) {
$request->getSession()->set('logout', true);
}
}

Categories