Laravel custom login username and password mapping - laravel

I am upgrading a legacy application.
I am using Laravel 8 without ORM with DB first approach since the DB is already there.
I want to use
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return redirect()->intended('shop');
}
When I am configuring auth.php I saw inside the providers 'driver' => 'database' was commented. I want to use auth.php providers like following
'providers' => [
// 'users' => [
// 'driver' => 'eloquent',
// 'model' => App\Models\User::class,
// ],
'users' => [
'driver' => 'database',
'table' => 'shop_users',
],
]
Is there any way I can map the user name password like following
'providers' => [
// 'users' => [
// 'driver' => 'eloquent',
// 'model' => App\Models\User::class,
// ],
'users' => [
'driver' => 'database',
'table' => 'shop_users',
'username' => 'email', //Mapping auth username to table column
'password' => 'password' //Mapping auth password to table password
],
]

Make sure this model call is extended Authenticatable
we need to add a new function inside app/Http/Auth/LoginController.php like below:
public function username(){
return 'email'; // this string is column of shop_users table which we are going use for login
}

Related

How to login to a different database and continue working with default one?

Imagine you have this inside .env file:
DB_CONNECTION=global
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=global
DB_USERNAME=dbuser
DB_PASSWORD=password
And you have one more connection inside config/database.php called tenant.
Now when I try to log in I switch the connection to tenant and I use the username and password from that database, I can log in, log out and all works fine.
But now I want to be able to log in with a user that doesn't exist in this tenant DB.
I started to overwrite the login() function like this:
Tip: remember that at this point the connection is set to tenant!
public function login(Request $request)
{
$parts = explode(':', $request['username']);
if ($parts[0] === 'global') {
\DB::purge(\DB::getDatabaseName());
\DB::reconnect('global');
\Schema::connection('global')->getConnection()->reconnect();
}
::::::::::::::::::
But this doesn't work... It doesn't switch the connection
I want to log in with a user from global DB and continue to work with tenant DB...
Is this even possible?
Update:
I already solved the part of how to switch the DB connection...
The problem is only authentication to a different one!
To avoid having to play with the default database connection, you should create two different model per database authenticatable user with each one its own $connection declared on the model
class UserGlobal extends User
{
protected $connection = 'global';
}
class UserTennant extends User
{
protected $connection = 'tennant';
}
You will need to do multi-authentification middlewares or use laravel's auth using this guide laravel simple multi-auth to have an idea.
// config/auth.php
<?php
[...]
'guards' => [
[...]
'global' => [
'driver' => 'session',
'provider' => 'globals',
],
'tennant' => [
'driver' => 'session',
'provider' => 'tennants',
],
],
[...]
'providers' => [
[...]
'globals' => [
'driver' => 'eloquent',
'model' => App\UserGlobal::class,
],
'tennants' => [
'driver' => 'eloquent',
'model' => App\UserTennant::class,
],
],
You need also to declare both database connection in config/database.php
'connections' => [
'global' => [
'driver' => 'mysql',
'host' => env('DB_HOST_GLOBAL', '127.0.0.1'),
...
],
'tennant' => [
'driver' => 'mysql',
'host' => env('DB_HOST_TENNANT', '127.0.0.1'),
...
],
]
What login would look like
public function login(Request $request)
{
$tennant= $this->loginGuard($request->get('email'), $request->get('password'), auth('tennant'));
$global= $this->loginGuard($request->get('email'), $request->get('password'), auth('global'));
if (!$tennant && !$global) {
return 'wrong credential';
}
return 'welcome';
}
private function loginGuard($email, $password, $guard)
{
$token = $guard->attempt(['email' => $email, 'password' => $password]);
if (!$token || !$guard->user()->isLoggingIn()) {
return null;
}
return $guard->user();
}

Laravel Passport Multiple Authentication using Guards

Can we use laravel passport with different guards to authenticate APIs for two different types of users.
For example we have driver app for driver user and vendor app for vendor user. Both have their different models Driver and Vendor.
How can we use different guards to authenticate both types of users using Laravel Passport?
I managed to create multiple auths (with laravel/passport) by using a simple middlware.
Step 1: config/auth.php
Add your user classes to providers
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'basic_users', // default
],
],
...
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'admin_users' => [
'driver' => 'eloquent',
'model' => App\AdminUser::class,
],
'basic_users' => [
'driver' => 'eloquent',
'model' => App\BasicUser::class,
],
],
Clean the cache via CLI
php artisan config:cache
Step 2: Create middleware
php artisan make:middleware AdminUserProvider
Open the newly created middleware in app/Http/Middleware and update the hand method like below
public function handle($request, Closure $next)
{
config(['auth.guards.api.provider' => 'admin_users']);
return $next($request);
}
Step 3: Register your middleware
Add the newly created middleware to $routeMiddleware
protected $routeMiddleware = [
...
'auth.admin' => \App\Http\Middleware\AdminUserProvider::class,
];
and make sure it's at the top of $middlewarePriority
protected $middlewarePriority = [
\App\Http\Middleware\AdminUserProvider::class,
...
];
Step 4: Add middleware to route
Route::group(['middleware' => ['auth.admin','auth:api']], function() {
Step 5: LoginControllers (AdminUserController & BasicUserController)
public function login()
{
$validatedData = request()->validate([
'email' => 'required',
'password' => 'required|min:6'
]);
// get user object
$user = AdminUser::where('email', request()->email)->first();
// do the passwords match?
if (!Hash::check(request()->password, $user->password)) {
// no they don't
return response()->json(['error' => 'Unauthorized'], 401);
}
// log the user in (needed for future requests)
Auth::login($user);
// get new token
$tokenResult = $user->createToken($this->tokenName);
// return token in json response
return response()->json(['success' => ['token' => $tokenResult->accessToken]], 200);
}
In summary:
The login controllers use Eloquent models to get the user object and then log the user in through Auth::login($user)
Then for future requests that need authentication, the new middleware will change the api auth guard provider to the correct class.
Edit: Passport now has support for multiple guard user providers. Please refer the following links for more infos:
Multiple Authentication Guards
Support For Multiple Guards
Old answer (I would not recommend it)
Here is an example of auth.php and api.php to start with
config/auth.php
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
*/
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'driver-api' => [
'driver' => 'passport',
'provider' => 'drivers',
],
'vendor-api' => [
'driver' => 'passport',
'provider' => 'vendors',
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'drivers' => [
'driver' => 'eloquent',
'model' => App\Driver::class,
],
'vendors' => [
'driver' => 'eloquent',
'model' => App\Vendor::class,
],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
'drivers' => [
'provider' => 'drivers',
'table' => 'password_resets',
'expire' => 60,
],
'vendors' => [
'provider' => 'vendors',
'table' => 'password_resets',
'expire' => 60,
],
],
];
routes/api.php
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
*/
Route::group(['namespace' => 'Driver', 'prefix' => 'driver/v1', 'middleware' => 'auth:driver-api'], function() {
// define your routes here for the "drivers"
});
Route::group(['namespace' => 'Vendor', 'prefix' => 'vendor/v1', 'middleware' => 'auth:vendor-api'], function() {
// define your routes here for the "vendors"
});
You have to modify this files:
File: vendor\laravel\passport\src\Bridge\UserRepository.php
Copy/Paste getUserEntityByUserCredentials to make a duplicate of it and name it getEntityByUserCredentials
Then, in the new duplicated function, find the below:
$provider = config('auth.guards.api.provider');
And Replace it with:
$provider = config('auth.guards.'.$provider.'.provider');
File: vendor\league\oauth2-server\src\Grant\PasswordGrant.php
in : validateUser method add after $username and $password :
$customProvider = $this->getRequestParameter('customProvider', $request);
if (is_null($customProvider)) {
throw OAuthServerException::invalidRequest('customProvider');
}
And this instead of the original line
$user = $this->userRepository->getEntityByUserCredentials(
$username,
$password,
$this->getIdentifier(),
$client,
$customProvider
);
After doing this you'll be able to pass an extra key/value pair to your access token request, like for example:
grant_type => password,
client_id => someclientid
client_secret => somesecret,
username => someuser,
password => somepass,
client_scope => *,
provider => driver-api // Or vendor-api
I hope this will be helpful for you
After spent time I have found that in Laravel 7 there is no custom code required except some configuration. For details please check this answer I have tested & implemented in my projects
Multi Auth with Laravel 5.4 and Passport
You don't necessarily need to change config for each request.
You need a client for each guard. After creating clients by running
passport:install
Make sure you specified provider field in database. It should be same value as auth.providers config.
After creating clients and guards, use following code when creating an access token.
App::clearResolvedInstance(ClientRepository::class);
app()->singleton(ClientRepository::class, function () {
return new ClientRepository(User::CLIENT_ID, null); // Client id of the model
});
Make sure you specified provider in oauth_clients table.

Laravel 5.4 Authentication to a different table not working

In my laravel application, I need to make a login to customers table. So, I made a CustomerLoginController.
My CustomerLoginController looks like:
class CustomerLoginController extends Controller
{
public function login(Request $request)
{
// Validate the data
$this->validate($request,[
'email' => 'required|email',
'password' => 'required'
]);
$credentials=[
'email' => $request->get('email'),
'password' => $request->get('password')
];
// Log The Customer In
if (Auth::guard('customer')->attempt(['email'=> $request['email'],'password' => $request['password']]))
{
// If Authentication passed...
dd($credentials);
return redirect(route('first'));
}
// If Authentication not successful, redirect back to login form with the inputs
return redirect()->back()
->withInput($request->only($this->username()));
}
public function username()
{
return 'email';
}
I have setup the customer guard and providers in the config/Auth.php. My config/Auth.php file:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'customer' => [
'driver' => 'session',
'provider' => 'customers',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'customers' => [
'driver' => 'eloquent',
'model' => App\Models\Customer::class,
],
],
The Problem is that even when the credentials are correct the login attempt is always unsuccessful.
What am I missing?
And what about Customer Model.
it should extend Authenticatable same as User Model.
class User extends Authenticatable
{
/* your code */
}
class Customer extends Authenticatable
/* your code */
{
}
and Don't Forgot to use Authenticatable in Model. :)
use Illuminate\Foundation\Auth\User as Authenticatable;

Laravel Multiple Auth using JWT auth every-time i get wrong password

I have 2 user i.e user and bus and for them i have different model
I am using laravel 5.5 with tymondesigns/jwt-auth 1.0.0-rc.1 verson
user is working perfect for user i am getting token also
but bus user us getting 'invalid_email_or_password'
link to full code full source code link
here is my user model:
class User extends Authenticatable implements JWTSubject{
use Notifiable;
protected $fillable = ['name', 'email', 'password'];
public function getJWTIdentifier()
{
return $this->getKey();
}
public function getJWTCustomClaims()
{
return [];
}}
my config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
'bus' => [
'driver' => 'session',
'provider' => 'buses',
],
'bus-api' => [
'driver' => 'jwt',
'provider' => 'buses',
],
],
my provider are :
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
],
//next
'buses' => [
'driver' => 'eloquent',
'model' => App\Bus::class,
],
my
my buslogincontroller which is taking user name & password for user table
public function busLogin(Request $request)
{
\Config::set('jwt.user', "App\Bus");
\Config::set('auth.providers.users.model', \App\Bus::class);
$credentials = $request->only('email', 'password');
try {
\Config::set('jwt.user', "App\Bus");
\Config::set('auth.providers.users.model', \App\Bus::class);
if (!$token = JWTAuth::attempt($credentials)) {
\Config::set('jwt.user', "App\Bus");
\Config::set('auth.providers.users.model', \App\Bus::class);
return response()->json([
'response' => 'error',
'message' => 'invalid_email_or_password',
]);
}
} catch (JWTAuthException $e) {
return response()->json([
'response' => 'error',
'message' => 'failed_to_create_token',
]);
}
return response()->json([
'response' => 'success',
'result' => [
'token' => $token,
'message' => 'I am Admin user',
'user' => '99',
],
]);
}
my routes api:
Route::post('bus/auth/login', 'Bus\Auth#busLogin');
Route::post('bus/auth/register', 'Bus\Auth#busRegister');
whenever I try to login with Bus model username & password i get invalid login in buscontroller login route but if i try to login with user model credintials i get token in return
how to setup multiple auth with jwtauth with laravel 5.5
There is no need to change the providers in config/auth.php.
You can change the __construct function in each of your controllers as follows. So that jwt know which model to authenticate.
BusController
function __construct()
{
Config::set('jwt.user', Bus::class);
Config::set('auth.providers', ['users' => [
'driver' => 'eloquent',
'model' => Bus::class,
]]);
}
AdminController
function __construct()
{
Config::set('jwt.user', Admin::class);
Config::set('auth.providers', ['users' => [
'driver' => 'eloquent',
'model' => Admin::class,
]]);
}
You just need to set below code before route call in route api
\Config::set('jwt.user', "App\Bus");
\Config::set('auth.providers.users.model', \App\Bus::class);

customizing login credentials for a single guard in laravel multi-authentication

I have multi-authentication set up in my laravel app. I set up different guards and providers for admin, doctor and patient. I've also set up the redirects for the various guards in my config\auth.php.
The only issue i'm having right now is, I want doctors to login with a unique number called MDCN which is saved to the database table instead of email. Please how do i customize the login for the doctors?
Ps: I've also set up the models for each, and i use laravel default auth.
my guard:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
'admin-api' => [
'driver' => 'token',
'provider' => 'admins',
],
'doctor' => [
'driver' => 'session',
'provider' => 'doctors',
],
'doctor-api' => [
'driver' => 'token',
'provider' => 'doctors',
],
],
and provider:
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
],
'doctors' => [
'driver' => 'eloquent',
'model' => App\Models\Doctor::class,
],
],
In the App\Http\Controllers\Auth\LoginController you can override a method which returns by what fields the authentication should be checked and there you can pass the MDCN if you're logging a doctor:
protected function credentials(Request $request)
{
return $request->only('MDCN', 'password');
}
i finally solved this, thank to Alex Curtis.
In order to do this, you need to customize each model to tell it what you want to use to login.
So on any model that you want to log in with a username instead of an email you need to add this block of code to the model:
public function username()
{
return 'username';
}
And just make sure that the value you return (username in this example) matches the column name in that model's table that has the identifier you are looking to use.
Obviously you would also want to make sure you have a column in the DB to hold this data, you want it to be mass-assignable (by customizing the $fillable array in the model) and you also want the column to be indexed and unique.
To get this you want to create the column in a migration that looks like this:
$table->string('username')->unique();
This will make a unique index for that column.
The default is email, so if you are using email then you don't need to worry about this, but any model using something other than email will need this change made to that user type's model.

Resources