REST-API in Laravel 8 using API Authentication.
Introduction
I have Analytics Model Authenticable to authenticate the request in web rather than using default User Model with corresponding analytics and user table table. I have migrated api_token field in analytics table. However, I'm getting below error in response while accessing the API route in POSTMAN.
Response
{
"message": "Argument 1 passed to Illuminate\\Auth\\TokenGuard::__construct() must implement interface Illuminate\\Contracts\\Auth\\UserProvider, null given, called in source\\vendor\\laravel\\framework\\src\\Illuminate\\Auth\\AuthManager.php on line 162",
"exception": "TypeError",
}
source\vendor\laravel\framework\src\Illuminate\Auth\AuthManager.php on line 162
public function createTokenDriver($name, $config)
{
$guard = new TokenGuard(
$this->createUserProvider($config['provider'] ?? null),
$this->app['request'],
$config['input_key'] ?? 'api_token',
$config['storage_key'] ?? 'api_token',
$config['hash'] ?? false // **** This is line 162 **** //
);
I tried changing line 162 as $config['hash'] ?? true but still getting same error.
Note: The Analytics and User Model are Authenticable. While I have the api_token field in the analytics table
Request:
I'm sending GET instance of HTTP Request on endpoint
http://example.com/api/user?api_token=token(this is unhashed token)
Below is the following configuration.
route/api.php
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
The Analytics and User Model are follow:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Notification;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Notifications\AnalyticsResetPassword;
use Illuminate\Database\Eloquent\Model;
class Analytics extends Authenticatable
{
use Notifiable;
public function sendPasswordResetNotification($token)
{
$this->notify(new AnalyticsResetPassword($token));
}
protected $table = "analytics";
protected $fillable = ['name', 'email', 'password', 'mobile', api_token', ];
protected $hidden = ['password', 'api_token', 'remember_token', ];
}
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
protected $fillable = ['name', 'email', 'password', ];
protected $hidden = ['password', 'remember_token',];
protected $casts = ['email_verified_at' => 'datetime',];
}
The guard and provider array in config/auth.php configuration file:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'analytics' => [
'driver' => 'session',
'provider' => 'analytics',
],
'api' => [
'driver' => 'token',
'provider' => 'user',
'hash' => true,
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'analytics' => [
'driver' => 'eloquent',
'model' => App\Analytics::class,
],
],
The token generating method in Controller
public function token(Request $request)
{
$token = Str::random(60);
$user = Auth::user();
$user->api_token = hash('sha256', $token);
$user->save();
return redirect('/analytics/security')->with('success', 'Token Generated Successfully!')->with("token" , $token);
}
'api' => [
'driver' => 'token',
'provider' => 'user',
'hash' => true,
],
I think the problem is the provider property within this guard configuration, as there is no entry for user within the providers - you only have users and analytics
Related
In Laravel 9, I am trying to hit the login API with the custom guard client, I am getting the following error. Please help.
BadMethodCallException: Method Laravel\Passport\Guards\TokenGuard::attempt does not exist.
config/Auth.php
'guards' => [
...
'client' => [
'driver' => 'passport',
'provider' => 'client',
],
],
'providers' => [
...
'client' => [
'driver' => 'eloquent',
'model' => App\Models\Client::class,
],
],
Error line: if(!$authGuard->attempt($login)){
api/AuthController.php
public function login(Request $request){
$login = $request->validate([
'email' => 'required|string',
'password' => 'required|string',
]);
try {
$authGuard = Auth::guard('client');
if(!$authGuard->attempt($login)){
$data = 'Invalid Login Credentials';
$code = 401;
} else {
$user = $authGuard->user();
$token = $user->createToken('user')->accessToken;
$code = 200;
$data = [
'user' => $user,
'token' => $token,
];
}
} catch (Exception $e) {
$data = ['error' => $e->getMessage()];
}
return response()->json($data, $code);
}
Models/Client.php
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;
class Client extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
Screenshot:
I think Auth::attempt() is not compatible with passport.
So, you can use Auth::check() method instead.
attempt() is only available to guards implementing the StatefulGuard interface.
So i agree with John that attempt is not compatible with Passport.
You can try this it should work :
auth()->guard('client')->setUser($login); or Auth::guard('client')->setUser($login);
I solved it by changing the driver from passport to session in config/auth.php
'clients' => [
'driver' => 'session',
'provider' => 'clients',
],
I am not sure this is the correct solution, but it works.
Please feel free to post the answer if there is any better solution
Thanks
I have got 2 pages (admin and customers) but I am living a problem. So if I entered the admin page and I can't enter the customer's page. So both admin and customers page same time I can't enter. I want both same time work. If customer logout. The admin is outing the system How do it? I write code at the bottom.
config/auth.php
return [
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
'customer' => [
'driver' => 'session',
'provider' => 'customers',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'customers' => [
'driver' => 'eloquent',
'model' => App\Customer::class,
],
],
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
],
];
app/Customer
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Customer extends Authenticatable
{
use Notifiable;
protected $guard = "customer";
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password','adres',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
}
app/User.php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User 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',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
app/http/controller/auth/LoginController.php
namespace App\Http\Controllers\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use 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';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
public function logout(Request $request) {
Auth::logout();
return redirect('/login');
}
}
app/http/controller/customer/auth/LoginController.php
namespace App\Http\Controllers\Customer\Auth;
use App\Customer;
use App\Http\Controllers\Controller;
use App\Setting;
use App\User;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
class LoginController extends Controller
{
use AuthenticatesUsers;
protected $redirectTo = '/customer';
public function __construct()
{
$this->middleware('guest:customer')->except('logout');
}
public function showLoginForm(){
$setting = Setting::where('id',1)->get();
return view('customer.auth.login',['setting'=>$setting]);
}
public function register(){
$setting = Setting::where('id',1)->get();
return view('customer.auth.register',['setting'=>$setting]);
}
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
'adres' => ['string', 'min:8', 'confirmed'],
]);
}
public function store(Request $request){
$all = $request->except('_token');
$rules = array('email' => 'unique:customers,email');
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
$setting = Setting::where('id',1)->get();
$errorugur = 'That email address is already registered. You sure you don\'t have an account? :/';
return view('customer.auth.error',['errorugur'=>$errorugur,'setting'=>$setting]);
}
else {
$data = [
'name' => $all['name'],
'email' => $all['email'],
'adres' => $all['adres'],
'password' => md5($all['password'])];
$create = Customer::create($data);
if ($create) {
return redirect()->back()->with('status','Added Successfully :)');
}
else {
return redirect()->back()->with('status','Sorry Couldnt Add :/');
}
}
}
public function logout(Request $request)
{
$this->guard()->logout();
$request->session()->invalidate();
return redirect()->route('customer.login');
}
protected function guard()
{
return Auth::guard('customer');
}
}
routes/web.php
Route::get('/logout', '\App\Http\Controllers\Auth\LoginController#logout'); // Admin logout
/* Customer Web */
Route::namespace("Customer")->prefix('customer')->group(function(){
Route::get('/', 'HomeController#index')->name('customer.home');
Route::get('/sale', 'HomeController#sale')->name('customer.sale');
Route::get('/information', 'HomeController#information')->name('customer.information');
Route::post('/update', 'HomeController#update')->name('customer.update');
Route::namespace('Auth')->group(function(){
Route::get('/register', 'LoginController#register')->name('customer.register');
Route::post('/store', 'LoginController#store')->name('customer.store');
Route::get('/login', 'LoginController#showLoginForm')->name('customer.login');
Route::post('/login', 'LoginController#login');
Route::get('/logout', 'LoginController#logout')->name('customer.logout');
});
});
I know, you are confused because of me too. I start laravel new. If You need information you should write me. Thx Everyone
First, I registered as a standard member. There is no role definition. Then from the panel I created. I created a new user page. Here I want to create a new user and set the role to this user. Although everything is ok I get the "Spatie\Permission\Exceptions\GuardDoesNotMatch The given role or permission should use guard web, api instead of admin." error.
I use another package, the passport module, in the software. I don't know if it has an effect.
Firstly my User model as below
<?php
namespace App;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Laravel\Passport\HasApiTokens;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
use HasRoles, HasApiTokens, 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',
];
}
UserController
<?php
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use RealRashid\SweetAlert\Facades\Alert;
use Illuminate\Http\Request;
use App\DataTables\UsersDataTable;
use Datatables;
use Session;
use DB;
use Auth;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
class UserController extends Controller
{
// use RegistersUsers;
public function __construct() {
$this->middleware(['auth', 'isAdmin']);
//isAdmin middleware lets only users with a //specific permission permission to access these resources
}
public function index()
{
$roles = Role::all();
return view('panel.users.create')->with(compact('roles'));
}
public function register(Request $data)
{
try {
DB::beginTransaction();
$validatedData = $data->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:6', 'confirmed'],
'roles' => ['required'],
]);
$user = new User;
$user->name = $data->name;
$user->email = $data->email;
$user->password = Hash::make($data['password']);
$user->save();
$roles = $data['roles'];
$role_r = Role::where('id', '=', $roles)->firstOrFail();
$user->assignRole($role_r);
DB::commit();
return redirect()->route('user.list')->withErrors($validatedData)->with('toast_success','New User Added');
}
catch (Exception $e) {
try {
DB::rollBack();
}
catch (Exception $e) {
}
return response([
'error' => true,
'message' => $e->getMessage() . $e->getLine()
]);
}
}
public function list(UsersDataTable $dataTable)
{
return $dataTable->render('panel.users.list');
}
}
AdminMiddleware
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use App\User;
class AdminMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$user = User::all()->count();
if (!($user == 1)) {
if (!Auth::user()->hasPermissionTo('Administer roles & permissions'))
//If user does //not have this permission
{
abort('401');
}
}
return $next($request);
}
}
config/auth.php
<?php
return [
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
'hash' => false,
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
],
'password_timeout' => 10800,
];
I searched on the internet but I couldn't find a solution. Where do you think I am doing wrong or wrong.
At first, You have to define "admin" role in config/auth.php as following:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admin',
]
],
Next, you have to make 2 model for normal user and admin as following:
class Admin extends Authenticatable
{
// this code will define user's guard
protected $guard = 'admin';
// rest of code
}
class User extends Authenticatable
{
protected $table = 'users';
protected $hidden = [
'password', 'remember_token',
];
// rest of code
}
Finally, you have to use these 2 models when user login as following:
class LoginController extends Controller
{
protected function attemptLogin(Request $request)
{
$user = \App\Models\User::where([
'email' => $request->email,
'password' => (md5($request->password))
])->first();
if ($user) {
$this->guard()->login($user, $request->has('remember'));
return true;
}
return false;
}
use AuthenticatesUsers;
public function __construct()
{
$this->middleware('guest')->except('logout');
}
}
class AdminLoginController extends Controller
{
protected function guard(){
return Auth::guard('admin');
}
protected function attemptLogin(Request $request)
{
$user = \App\Models\Admin::where([
'email' => $request->email,
'password' => md5($request->password)
])->first();
if ($user) {
$this->guard()->login($user, $request->has('remember'));
return true;
}
return false;
}
use AuthenticatesUsers;
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest:admin')->except('logout');
}
}
That's all. Hope to help
I'm building an API using Laravel. For authentication and security I am using Passport: https://laravel.com/docs/5.7/passport
I followed all the steps in the documentation. I am working with several profiles that pass through authentication, and I came across a problem, the token used by a middleware can be applied in other middleware.
In my config / auth.php file it looks like this:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'user' => [
'driver' => 'passport',
'provider' => 'users',
],
'producer' => [
'driver' => 'passport',
'provider' => 'producers',
],
'coordinator' => [
'driver' => 'passport',
'provider' => 'coordinators',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'producers' => [
'driver' => 'eloquent',
'model' => App\Producer::class,
],
'coordinators' => [
'driver' => 'eloquent',
'model' => App\Coordinator::class,
],
],
The Coordinator model looks like this:
class Coordinator extends Authenticatable{
use HasApiTokens, Notifiable;
protected $table = 'coordinators';
protected $guard = 'coordinator';
protected $fillable = [
'coordinator_name', 'email', 'password', 'cpf_cnpj', 'phone'
];
protected $hidden = [
'password',
];
public function events(){
return $this->belongsToMany('App\Event')->using('App\EventCoordinator');
}}
And the Model Producer looks like this:
class Producer extends Authenticatable{
use HasApiTokens, Notifiable;
protected $guard = 'producer';
protected $fillable = [
'name', 'email', 'password', 'cpf_cnpj', 'phone', 'street', 'neighborhood', 'city', 'state', 'number', 'zipcode', 'complement'
];
protected $table = 'producers';
protected $hidden = [
'password',
];
public function events(){
return $this->hasMany('App\Event');
}}
On the routes I'm using the middleware set in auth.php
Route::middleware('auth:producer')->group(function() {
Route::get('events', 'ProducerController#events');
});
Route::middleware('auth:coordinator')->group(function() {
Route::get('events', 'CoordinatorController#events');
});
And finally the events method in the CoordinatorController looks like this:
public function events(){
try{
if(Auth::guard('coordinator')->check()){
$events = Auth::user()->events;
return response()->json(['events' => $events], 200);
}else{
return response()->json(['error' => ['message' => 'Usuário não autenticado.']], 421);
}
}catch(\Exception $err){
return response()->json(['error' => ['code' => $err->getCode(), 'message' => $err->getMessage()]], 400);
}
}
and in ProducerController:
public function events(){
try{
try{
if(Auth::guard('producer')->check()){
$events = Auth::user()->events;
return response()->json(['events' => $events], 200);
}else{
return response()->json(['error' => ['message' => 'Usuário não autenticado.']], 421);
}
}catch(\Exception $err){
return response()->json(['error' => ['code' => $err->getCode(), 'message' => $err->getMessage()]], 400);
}
}
I am using Postman for testing, and when I use the Producer token to access the Coordinator method it works even though using different middleware in the route. Can anyone help? Something is missing?
as far as I know, passport didn't support MultiAuth
you can use widely available 3rd party multiauth support for laravel, like this package.
I am trying to get laravel's multi auth setup. I already have a consumer login from my consumers table working. but now I need to set up the business login from the businesses table. (currently the business login is pulling data from the consumers table.
I am trying to get it to return JSON (JWT) vs Returning a login blade which I have found most of the info online has done.
Businesses migration
class CreateBusinessTable extends Migration
{
public function up()
{
Schema::create('businesses', function (Blueprint $table) {
$table->increments('bus_id', 11);
$table->string('bus_name', 50);
$table->string('bus_address', 50);
$table->string('bus_city', 50);
$table->string('bus_prov', 50);
$table->string('bus_postal', 50);
$table->string('bus_phone', 50);
$table->string('email', 50);
$table->string('password', 20);
$table->double('max_donatable', 10, 2);
$table->integer('cashback_percent');
$table->binary('bus_cover_photo');
$table->binary('bus_profile_pic');
$table->timestamps();
$table->rememberToken();
$table->engine = 'InnoDB';
});
}
Business Model
namespace App;
use Illuminate\Contracts\Auth\CanResetPassword;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Business extends Authenticatable implements JWTSubject//Model
{
use Notifiable;
protected $table = 'businesses';
protected $primaryKey = 'bus_id';
protected $gaurd = 'business';
protected $fillable = [ 'bus_id', 'bus_name', 'bus_address', 'bus_city', 'bus_prov', 'bus_postal', 'bus_phone', 'email', 'password', 'cashback_percent', 'bus_cover_photo', 'bus_profile_pic'];
protected $hidden = [
'password', 'remember_token',
];
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* #return array
*/
public function getJWTCustomClaims()
{
return [];
}
}
Business controller
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
class businessAuthController extends Controller
{
public function login()
{
$credentials = request(['email', 'password']);
if (! $token = /*auth()*/Auth::guard('business')->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
}
Handler.php unauthenticated function
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Unauthenticated.'], 401);
}
$guard = array_get($exception->guards(), 0);
switch ($guard) {
case 'business':
$login = 'business.login';
break;
default:
$login = 'login';
break;
}
return redirect()->guest(route($login));
}
Kernel.php
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'business' => \App\Http\Middleware\Business::class,
];
}
Providers
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Consumer::class,
],
'businesses' => [
'driver' => 'eloquent',
'model' => App\Business::class,
],
],
Gaurds
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
'business' => [
'driver' => 'session',
'provider' => 'businesses',
],
'business-api' => [
'driver' => 'jwt',
'provider' => 'businesses',
],
],