Laravel Passport Multiauth - php

I am working on an API for 2 frontend apps (vue2/mobile). One uses User model and the other uses the Admin model (Laravel is just an API)
I am using Laravel Passport for authenticating users and admins, i successfully provided access token for users but i'm facing some problem with admins
So for i did
1-> created Admin model
<?php
namespace App;
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Admin extends Authenticatable
{
use HasApiTokens, Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password','role',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
}
2-> created a admins guard which uses passport
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
'admin' => [
'driver' => 'passport',
'provider' => 'admins',
],
],
3-> created Route and Controller for granting access token for admins
Route::post('/oauth/token/admin', [
'uses' => 'Auth\CustomAccessTokenController#issueUserToken'
]);
<?php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use Psr\Http\Message\ServerRequestInterface;
use Laravel\Passport\Http\Controllers\AccessTokenController;
class CustomAccessTokenController extends AccessTokenController
{
/**
* Hooks in before the AccessTokenController issues a token
*
*
* #param ServerRequestInterface $request
* #return mixed
*/
public function issueUserToken(ServerRequestInterface $request)
{
$httpRequest = request();
if ($httpRequest->grant_type == 'password') {
$admin = \App\Admin::where('email', $httpRequest->username)
->where('password', $httpRequest->password)
->first();
//dd($admin);
return $this->issueToken($request);
}
}
}
4-> i tested with Postman
http://localhost:8000/api/oauth/token/admin
client_id:4
client_secret:M4QkLqhPkJ4pGL52429RipassQ3BOjKTJZe3uoWK
grant_type:password
username:admin#gmail.com
password:secret
//i'm getting
{
"error": "invalid_credentials",
"message": "The user credentials were incorrect."
}
//if i use the User model credentials
username:user#gmail.com
password:secret
//i'm getting the access token
{
"token_type": "Bearer",
"expires_in": 31536000,
"access_token": "eyJ0eXAiOiJKV1Qi....",
"refresh_token": "UI354EfJlVdmOhO...."
}
i'm really tired figuring out what went wrong
looking forward for much needed help
thank you

Related

User does not stay logged in after Auth:validate was true

i'm trying to develop a multi tenant, app in laravel, with multiple DBs and subdomains, so far i'm using the default user guard for authenticating in the main domain let's say it's example.com, it works fine, i'm also using a different guard for the subdomains, registration works fine, but the login seems to be broken, it authenticates the user but if i try to Auth:user() or even redirect to a protected route it looks like the user has already logged out.
I'm using relational database as the session driver (to avoid subdomains user to modify the cookies domain and access other subdomains), the sessions seems to be stored correctly in the sessions table of the main domain, but in the subdomain every record has the user_id set as null.
Laravel 8.28.1
PHP 7.4.12
Multi tenancy by https://tenancyforlaravel.com
Here is my config/auth.php file
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
// this is the guard for subdomains
'collaboratore' => [
'driver' => 'session',
'provider' => 'collaboratori',
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'collaboratori' => [
'driver' => 'eloquent',
'model' => App\Models\Collaboratore::class,
],
this is my model for users in the subdomains
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class Collaboratore extends Authenticatable
{
use HasFactory, Notifiable;
protected $table = 'collaboratore';
protected $guard = 'collaboratore';
public $primaryKey = 'id';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'username',
'password',
'email',
// ... other stuff ...
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
and this is my controller for users in the subdomains
public function login(Request $request )
{
// validate request
$credentials = $this->validate($request, [
'email' => 'required|email',
'password' => 'required'
]);
if ( Auth::guard('collaboratore')->attempt( $credentials ) )
{
// login successful
return redirect('/home');
}
//dd("failed");
// login failed
return $request->expectsJson()
? response([ 'message' => 'Invalid credentials', 401 ])
: redirect()->back()->withInput($request->only('email', 'remember'));
}
any help would be appreciated, i'm kinda stuck right now
From Laravel website https://laravel.com/docs/8.x/authentication#introduction
The attempt method is normally used to handle authentication attempt's
from your application's "login" form. If authentication is successful,
you should regenerate the user's session to prevent session fixation:
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return redirect()->intended('dashboard');
}
So you should add $request->session()->regenerate(); inside your if attempt.
it looks like i managed to solve his, the problem was here, i changed
public function __construct()
{
$this->middleware('guest')->except('logout');
}
to this
public function __construct()
{
$this->middleware('web');
}
and now it's working, but the session is still note being stored

Laravel Sanctum Tokens - Two Databases

I am having some problems getting Laravel Sanctum authorising two tables in two separate databases.
I am using Laravel Sanctum tokens for authorisation. I have two tables to authorise users (users & contacts) I have setup two separate guards and can get everything to work on a single database with one token table.
However I want to have the contacts table in a separate database. Doing so creates two personal_access_tokens tables, one in the Users database and the other in the Contacts database, which I don't mind. I can create the tokens just fine, however when I try to authorise contacts using a token, Sanctum is trying to look in the Users personal_access_tokens table, not the Contacts personal_access_tokens table. So essentially it's just looking at the wrong database for the personal_access_tokens table and I don't know how to change that.
My setup is as follows:
Guards:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
/*'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],*/
'users' => [
'driver' => 'sanctum',
'provider' => 'users',
'hash' => false,
],
'contacts' => [
'driver' => 'sanctum',
'provider' => 'contacts',
'hash' => false,
],
],
Providers
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'contacts' => [
'driver' => 'eloquent',
'model' => App\Models\Contact::class,
],
],
User Model
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
Contact Model
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class Contact extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The connection name for the model.
*
* #var string
*/
protected $connection = 'puranet_crm';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'first_name',
'last_name',
'email',
'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
My two api routes for autorisation are:
Route::group(['middleware' => 'auth:sanctum'], function() {
//All secure URL's
Route::get('test',[UserController::class, 'test']);
});
Route::group(['middleware' => 'auth:contacts'], function() {
Route::get('test-contacts',[ContactController::class, 'test']);
});
Contact Controller (this is identical to the UserController with exception to the Model it is referencing)
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Contact;
use Illuminate\Support\Facades\Hash;
class ContactController extends Controller
{
/**
* #param Request $request
* #return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function login(Request $request)
{
$user = Contact::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
return response([
'message' => ['These credentials do not match our records.']
], 404);
}
$token = $user->createToken('contacts-app-token')->plainTextToken;
$response = [
'user' => $user,
'token' => $token
];
return response($response, 201);
}
/**
* #return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function test()
{
return response(["response" => "Test Contacts"], 201);
}
}
You need to overwrite the sanctum model on your project and overwrite the $connection variable inside of it, so you will be able to connect to the database that you would like to, same when you do with normal Models.You can find how to overwrite sanctum model on the Laravel documentation for version 8.
Create a this model in one of your projects to overwrite where sanctum will look for the token.
class PersonalAccessToken extends SanctumPersonalAccessToken{
use HasFactory;
protected $connection = 'name of your connection in database.php';
}
So both sanctum will use the same DB to auth the User.
I hope I helped you :)

Laravel 5.6 Login Attempt Fails - Method RequestGuard::attempt does not exist is returned on Login Submit

I had implemented multi-auth for my application and it was working fine until it just stopped.I've scoured the internet for a solution but to no avail. So I do have Admin Login Controller and the default Laravel Login controller which uses make:auth and implements authenticable for users. My admin login is working fine but the user login fails and returns this
BadMethodCallException Method Illuminate\Auth\RequestGuard::attempt does not exist.
The same thing happens when I try to register a user but this time it returns it returns
BadMethodCallException
Method Illuminate\Auth\RequestGuard::login does not exist.
Despite the error the user registration passes and the fields are actually populated in the users table from the registration form. Since I'm using the default auth I'm expecting it to automatically login , and i guess this is where the problem arises on the attemp method.
It's imperative to note that I am using Passport on the user Model for another module.
Below is how my Login Controller looks like
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 = '/';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
}
And my Admin Login Controller
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
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 = 'admin/dashboard';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest:admin', ['except' => 'logout']);
}
/**
* Show the application's login form.
*
* #return \Illuminate\Http\Response
*/
public function showLoginForm()
{
return view('admin.admin-login');
}
/**
* Get the guard to be used during authentication.
*
* #param Request $request
* #return \Illuminate\Contracts\Auth\StatefulGuard
*/
public function login(Request $request)
{
// Validate the form data
$this->validate($request, [
'email' => 'required|email',
'password' => 'required|min:8'
]);
// Attempt to log the user in
if (Auth::guard('admin')->attempt(['email' => $request->email, 'password' => $request->password])) {
// if successful, then redirect to their intended location
return redirect()->intended(route('admin.dashboard'));
}
// if unsuccessful, then redirect back to the login with the form data
return redirect()->back()->with('flash_message_error', 'Invalid Access: Please Login With Your Credentials.');
}
public function logout()
{
Auth::guard('admin')->logout();
return redirect('admin')->with('flash_message_error', 'Successfully Logged Out');;
}
}
This is my guard config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
'admin-api' => [
'driver' => 'passport',
'provider' => 'admins',
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expire time is the number of minutes that the reset token should be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
'admins' => [
'provider' => 'admins',
'table' => 'password_resets',
'expire' => 15,
],
],
];
And finally below is my user Model
namespace App;
use App\Modules\Event\Bookings;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;
use GrahamCampbell\Markdown\Facades\Markdown;
/**
* #method static find($user_id)
* #method static count()
*/
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'fname','lname', 'email','organization','phone_number', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function participant()
{
return $this->hasMany(Bookings::class);
}
public function posts()
{
return $this->hasMany(Posts::class, 'author_id');
}
public function gravatar()
{
$email = $this->email;
$default = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/50/User_icon-cp.svg/200px-User_icon-cp.svg.png";
$size = 60;
return "https://www.gravatar.com/avatar/" . md5( strtolower( trim( $email ) ) ) . "?d=" . urlencode( $default ) . "&s=" . $size;
}
public function getRouteKeyName()
{
return 'slug';
}
public function getBioHtmlAttribute()
{
return $this->bio ? Markdown::convertToHtml(e($this->bio)) : NULL ;
}
}
Below is My Admin Model
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
/**
* #method static find($user_id)
*/
class Admin extends Authenticatable
{
use Notifiable;
protected $guard = 'admin';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
}
I tried to use api as the default guard and it only worked temporarily and started to return the same error, I know attempt method only works on web middleware so what could be the problem? I've removed the vendor folder and reinstalled it using composer update still nothing.. just stuck.
Would immensly appraciate the help.
So to answer this question, after racking my brain, I decided to clear the application, configuration, and route caches,that did the trick for me.
php artisan cache:clear
You can run the above statement in your console when you wish to clear the application cache. What it does is that this statement clears all caches inside storage\framework\cache.
php artisan route:cache
This clears your route cache. So if you have added a new route or have changed a route controller or action you can use this one to reload the same.
php artisan config:cache
This will clear the caching of the env file and reload it
Finally you can run
composer dump-autoload -o
Composer dump-autoload won’t download a thing.
It just regenerates the list of all classes that need to be included in the project (autoload_classmap.php).

The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed

I am developing an auth api with laravel, passport and postman. I have seen related post but none of them solved my problem. If I try to send a request it shows me this error. I have tried all I can but it just displays this error
{
"error": "invalid_request",
"message": "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed.",
"hint": "Check the `username` parameter"
}
my value for the application is
{
"grant_type" : "password",
"client_id" : "2",
"client_secret" : "fsDFrzGtmpMjoxWtplnvcmgKT3USzKFfKQu6alGF",
"email":"john#gmail.com",
"pssword" : "12345",
"scope" : "*"
}
api.php
<?php
use Illuminate\Http\Request;
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
Route::post('signup', 'SignUp#signUp');
auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
User.php
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password','vCode',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
AuthServiceProvider.php
namespace App\Providers;
use Laravel\Passport\Passport;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* #var array
*/
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
//
Passport::routes();
}
}
Please any solution will be really appreciated
you required 'username' field and passing value in postman parameter
After all your code is absolutly right.nothing error.
You need to add refresh_token parameter
{
"grant_type" : "password",
"refresh_token" : 'your_refresh_token',
"client_id" : "2",
"client_secret" : "fsDFrzGtmpMjoxWtplnvcmgKT3USzKFfKQu6alGF",
"email":"john#gmail.com",
"pssword" : "12345",
"scope" : "*"
}
See the documentation for more information : https://laravel.com/docs/5.8/passport#refreshing-tokens

Two different models for authentication in laravel 5.4

Suppose I have two different models and tables named user and company.
As you know laravel uses User model to manage Authentication. but beacause I have two different model I want can manage them separately.
I'm using laravel 5.4 and I do not know how can do that.
If you are talking about multiple authentication system, then you have to create multiple guards to achieve that.
There is nice answer to the same question.
Can anyone explain Laravel 5.2 Multi Auth with example
It's on Laravel 5.2, but it can be easily implemented on Laravel 5.4.
Create a model App\Company which extends Authenticatable Class. This model will work as user model which will company guard (in the next step)
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Company extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
}
Create an guard and a provider for model App\Company.
// Authenticating guards and providers
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
'company' => [
'driver' => 'session',
'provider' => 'company',
],
],
// Providers
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'company' => [
'driver' => 'eloquent',
'model' => App\Company::class,
]
],
Now you can find user according to the different guards.
$user = Auth::guard('company')->user();
// Or...
$user = auth()->guard('company')->user();
dd($user);
Now create Auth controller for Company App\Http\Controllers\Auth\CompanyLoginController same as Auth\LoginController.
Specify $redirectTo and guard
//Auth\ComapnyLoginController.php
protected $redirectTo = '/comapany';
protected $guard = 'comapany';
public function showLoginForm()
{
if (view()->exists('auth.authenticate')) {
return view('auth.authenticate');
}
return view('comapany.auth.login');
}
now create login form for user - company.auth.login view same as user's login form.
Now create routes
//Login Routes...
Route::group(['prefix'=>'company', 'middleware'=>'company'], function(){
Route::get('/login','Auth\CompanyLoginController#showLoginForm');
Route::post('/login','Auth\CompanyLoginController#login');
// ...
// rest of the company dashboard and other links
// ...
Route::get('/logout','Auth\CompanyLoginController#logout');
});
Create a middleware for company
class RedirectIfNotCompany
{
/**
* 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 = 'company')
{
if (!Auth::guard($guard)->check()) {
return redirect('/');
}
return $next($request);
}
}
and register it to kernal.php
protected $routeMiddleware = [
'company' => \App\Http\Middleware\RedirectIfNotCompany::class,
];
And thats all you need.
Access user by the name of guard
Auth::guard('company')->user()

Categories