I am trying multiguard authentication for api when i login for admin i am getting
following error
BadMethodCallException
Method Illuminate\Auth\Req
uestGuard::attempt does not exist.
here is my login method in controller
public function login(Request $request){
if(Auth::guard('admin-api')->attempt(['email' => request('email'), 'password' => request('password')]))
{
// if successful, then redirect to their intended location
$user = Auth::guard('admin-api');
$success['token'] = $user->createToken('admin')->accessToken;
return response()->json(['success' => $success], $this->successStatus);
}
else{
return response()->json(['error'=>'Unauthorised'], 401);
}
}
my api.php
Route::prefix('admin')->group(function () {
Route::post('login', 'API\Admin\AdminController#login')->name('admin.login');
Route::post('register', 'API\Admin\AdminController#register')->name('admin.register');
Route::group(['middleware' => 'auth:admin-api'], function(){
Route::post('get-details', 'API\Admin\AdminController#getDetails');
});
});
my admin model
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;
class Admin extends Authenticatable
{
use HasApiTokens, Notifiable;
protected $table = 'admin';
protected $guard = 'admin-api';
/**
* 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',
];
}
please tell me any other inputs you want
A better hack is this
$credentials = ['email' => $request->username, 'password' => $request->password];
//Since we are not using guard web in API request, we have to add it here (
// guard('web') ) to access the Auth::attempt function,
// the Auth::attempt needs web guard and crsf token, but using it here bypasses
// the post with crsf.
if (Auth::guard('web')->attempt($credentials, false, false)) {
dd('user is OK');
}else{
dd('user is NOT OK');
}
Unfortunately the Laravel Facade for Auth does not expect you to use it for the api guard since sessions and cookies will be set, Thus does not support ->attempt() function. But the API middle-ware disables session and cookies since it is stateless. So here is the hack.
Get to your confi\auth and create a similar web instance for your api guards thus
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'drivers-web' => [
'driver' => 'session',
'provider' => 'drivers',
],
'api' => [
'driver' => 'passport',//previously "token"
'provider' => 'users',//This will be switched regularly from the middleware between drivers and users
],
'drivers-api' => [
'driver' => 'passport',//previously "token"
'provider' => 'drivers',
],
],
Never the less, You can use passport to generate your client access tokens which are stateless sessions. And also serve well for authentication.
you cannot use Auth::guard('admin-api')->attempt with a guard with driver value is token or passport so you can repeat the guard and make one with session driver and the second one with passport then you can use the session one to make difference between earch other you can see a reference and source code from here https://web-brackets.com/discussion/103/method-illuminate-auth-requestguard-attempt-does-not-exist-
To me, I changed drivers for the web from passport to session. Laravel's cache had to be cleared to be able to go back to the session driver
php artisan cache:clear
Related
I need to authenticate temporary users of a third party app. In addition, users only get a signed URL to login (there is no username/password). The temporary users get created on the fly and logged in after verifying the signed URL and some query params. Because I also have "traditional" users in my app I am using an additional database table called "clients", an additional provder 'clients' and an additional guard called 'vcs' for the authentication workflow.
The authentication workflow (user clicks on the signed URL, a new Client is created and saved to the database as well as logged in as new user) is working fine. The session is created correctly and send to the browser in the laravel_session cookie. The problem is, that all subsequent requests to my API seem to be unauthenticated.
config/auth.php:
<?php
return [
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'vcs' => [
'driver' => 'session',
'provider' => 'clients',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'clients' => [
'driver' => 'eloquent',
'model' => App\Models\Client::class,
],
],
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
],
'password_timeout' => 10800,
];
My client model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Client extends Authenticatable
{
use HasFactory, HasApiTokens;
protected $guard = "vcs";
/**
* The primary key associated with the table.
*
* #var string
*/
protected $primaryKey = 'uuid';
/**
* Indicates if the model's ID is auto-incrementing.
*
* #var bool
*/
public $incrementing = false;
protected $keyType = 'string';
/**
* Get the route key for the model.
*
* #return string
*/
public function getRouteKeyName()
{
return 'uuid';
}
}
The clients get a signed URL which points to the following controller action. The action checks for a valid query parameter in the URL (simplified for this thread). After that a new Client model gets created and the new Client gets logged in using the 'vcs' guard:
<?php
namespace App\Http\Controllers\VCS;
use Illuminate\Http\Request;
use App\Models\Client;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Auth;
class AuthController extends Controller
{
public function redirectWithCookie(Request $request)
{
// reduced for the sake of simplicity here
$credential = $request->someURLParameter;
if ($credential) {
$client = new Client;
$client->uuid = Str::uuid()->toString();
$client->ip = $request->ip();
$client->status = 'pending';
$client->save();
Auth::guard('vcs')->login($client, $remember = true);
// this logs the authenticated user correctly!
Log::info('Authenticated User: ' . Auth::guard('vcs')->user());
$cookieValue = json_encode(array('uuid' => $client->uuid));
$cookie = cookie('mycookie', $cookieValue);
$redirectUrl = config('my.redirect.url');
return redirect()->away($redirectUrl)->withCookie($cookie);
}
return response(['message' => 'Invalid URL', 'error' => 'url'], 422);
}
}
routes/web.php:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\VCS\AuthController;
Route::get('/', function () {
return ['Laravel' => app()->version()];
});
Route::get('vcs/auth', [AuthController::class, 'redirectWithCookie'])->name('vcs.auth');
require __DIR__.'/auth.php';
routes/api.php:
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\VCS\RoomController;
Route::middleware(['auth:sanctum'])->get('/user', function (Request $request) {
return $request->user();
})->name('profile');
Route::middleware(['auth:vcs'])->group(function () {
Route::get('rooms', [RoomController::class, 'rooms']);
});
After the redirect I get a laravel_session as a cookie which should authenticate my subsequent requests. The problem is that I can't call any API routes with the custom guard and I am not authenticated anymore although the browser is sending my session cookie with the request. For example calling the /api/rooms GET-endpoint defined in the api.php results in a redirect to the login page.
I also see that the user is not authenticated in the auth-middleware:
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* #param \Illuminate\Http\Request $request
* #return string|null
*/
protected function redirectTo($request)
{
Log::info('Authenticated User: ' . Auth::guard('vcs')->user());
}
}
The Log just returns an empty string so the user is not authenticated:
[2022-11-06 13:44:30] local.INFO: Authenticated User:
So my question is: How can I use a custom guard for my API routes after manually logging new users in?
I also tried the same workflow using Insomnia as a REST Client:
Login by URL:
whichs gives me a sessions cookie.
Access some API Route:
Which results in an Unauthorized-Status-Code..
I have error when do login in api by jwt ..when i do register its good will but in login display error
InvalidArgumentException: Auth guard [customers] is not defined. in
file C:\Users\Ahmed\Desktop\project
web\laravel_pro\vendor\laravel\framework\src\Illuminate\Auth\AuthManager.php
on line 84
this is the controller
public function login(Request $request){
$credentials = $request->only('email', 'password');
if (Auth::guard('customer')->attempt($credentials))
{
$user= JWTAuth::guard('customer')->user();
$jwt = JwtAuth::generateToken($user);
return response()->json(compact('jwt'));
}else{
return response()->json(['error' => 'invalid_credentials'], 400);
}
this is the api route
Route::post('login','UserController#login');
this is the auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
'customer'=>[
'driver' => 'token',
'provider'=> 'customers',
]
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'customers' => [
'driver' => 'eloquent',
'model' => App\Customer::class,
],
please any body help my to Solution..Thank you
First Change your guard's driver from token to session and then run the below commands
php artisan cache:clear
php artisan config:cache
First guess: in config/auth.php the driver for your desired guard should be 'jwt' instead of 'token', because 'token' driver works against a fixed hash in the users table, as opposed to jwt that is volatile and doesn't query the DB to figure out the user.
Second guess : you're using the same controller for "internal users" ('api' guard), and "customer users" (should use 'customer' guard). This is confusing. Where are you discriminating the user type?
Regarding the error
Auth guard [customers] is not defined.
Check that it's not a typo and you always address it as 'customer', since 'customers' is not a guard but a provider. For example, check if the route middleware (e.g. Route::['middleware'=>'auth:customer']
Having said that...
I guess you installed tymondesigns/jwt-auth, whose configuration guide points to a scenario where you replace the api guard to use so, under that assumpion, make sure you've implemented the following modifications:
If using Laravel 5.4 or below, you would need to follow these steps. On newer version they are handled for you so you can skip this step
As I said above, in config/auth.php the driver for your desired guard should be 'jwt' instead of 'token', because 'token' driver works against a fixed hash in the users table, as opposed to jwt that is volatile and doesn't query the DB to figure out the user.
You need to implement the JWTSubject interface on your Customer model (just add things, don't need to touch the other methods)
use Tymon\JWTAuth\Contracts\JWTSubject;
class Customer extends Authenticatable implements JWTSubject
{
/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* #return mixed
*/
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()
{
// just an example here, change it to whatever you want
return [
'picture' => 'picture', // array values are the fieldname in the DB
'twitter' => 'twitter_url'
];
}
...
Your controller should implement the corresponding guard in the constructor, and whitelist the login method since there is no JWT in the incoming headers.
// UserController
public function __construct()
{
$this->middleware('auth:users', ['except' => ['login']]);
}
public function login()
{
$credentials = request(['email', 'password']);
// token is returned from attempt
if (! $token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60
]);
}
... other methods ...
I'd start by discarding the obvious:
are you still able to login as user (api guard)? if yo do, try to change the driver to 'jwt'
I used to be for laravel 5.5 and earlier than https://github.com/Hesto/multi-auth .
But this repository don't update for laravel 5.6.
How to create multi auth in Laravel 5.6?
After lots of digging and lots of questions & answers I have finally managed to work Laravel 5.6 Multi Auth with two table, So I'm writing Answer of my own Question.
How to implement Multi Auth in Larvel
As Mentioned above.
Two table admin and users
Laravel 5.2 has a new artisan command.
php artisan make:auth
it will generate basic login/register route, view and controller for user table.
Make a admin table as users table for simplicity.
Controller For Admin
app/Http/Controllers/AdminAuth/AuthController
app/Http/Controllers/AdminAuth/PasswordController
(note: I just copied these files from app/Http/Controllers/Auth/AuthController here)
config/auth.php
//Authenticating guards
'guards' => [
'user' =>[
'driver' => 'session',
'provider' => 'user',
],
'admin' => [
'driver' => 'session',
'provider' => 'admin',
],
],
//User Providers
'providers' => [
'user' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'admin' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
]
],
//Resetting Password
'passwords' => [
'clients' => [
'provider' => 'client',
'email' => 'auth.emails.password',
'table' => 'password_resets',
'expire' => 60,
],
'admins' => [
'provider' => 'admin',
'email' => 'auth.emails.password',
'table' => 'password_resets',
'expire' => 60,
],
],
route.php
Route::group(['middleware' => ['web']], function () {
//Login Routes...
Route::get('/admin/login','AdminAuth\AuthController#showLoginForm');
Route::post('/admin/login','AdminAuth\AuthController#login');
Route::get('/admin/logout','AdminAuth\AuthController#logout');
// Registration Routes...
Route::get('admin/register', 'AdminAuth\AuthController#showRegistrationForm');
Route::post('admin/register', 'AdminAuth\AuthController#register');
Route::get('/admin', 'AdminController#index');
});
AdminAuth/AuthController.php
Add two methods and specify $redirectTo and $guard
protected $redirectTo = '/admin';
protected $guard = 'admin';
public function showLoginForm()
{
if (view()->exists('auth.authenticate')) {
return view('auth.authenticate');
}
return view('admin.auth.login');
}
public function showRegistrationForm()
{
return view('admin.auth.register');
}
it will help you to open another login form for admin
creating a middleware for admin
class RedirectIfNotAdmin
{
/**
* 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 = 'admin')
{
if (!Auth::guard($guard)->check()) {
return redirect('/');
}
return $next($request);
}
}
register middleware in kernel.php
protected $routeMiddleware = [
'admin' => \App\Http\Middleware\RedirectIfNotAdmin::class,
];
use this middleware in AdminController
e.g.,
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
class AdminController extends Controller
{
public function __construct(){
$this->middleware('admin');
}
public function index(){
return view('admin.dashboard');
}
}
That's all needed to make it working and also to get json of authenticated admin use
Auth::guard('admin')->user()
We can access authenticated user directly using
Auth::user()
but if you have two authentication table then you have to use
Auth::guard('guard_name')->user()
for logout
Auth::guard('guard_name')->user()->logout()
for authenticated user json
Auth::guard('guard_name')->user()
How can I build an authentication system for customer?
I have used laravel built in authentication system for my admin panel where built in user model and users table already used.
Now I want to build another authentication system for my customer where customer model and customers table will be used.
How can I do this in laravel 5.2 ?
How to implement Multi Auth in Larvel 5.2
As Mentioned above. Two table admin and users
Laravel 5.2 has a new artisan command.
php artisan make:auth
it will generate basic login/register route, view and controller for user table.
Make a admin table as users table for simplicity.
Controller For Admin
app/Http/Controllers/AdminAuth/AuthController
app/Http/Controllers/AdminAuth/PasswordController
(note: I just copied these files from app/Http/Controllers/Auth/AuthController here)
config/auth.php
//Authenticating guards
'guards' => [
'user' =>[
'driver' => 'session',
'provider' => 'user',
],
'admin' => [
'driver' => 'session',
'provider' => 'admin',
],
],
//User Providers
'providers' => [
'user' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'admin' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
]
],
//Resetting Password
'passwords' => [
'clients' => [
'provider' => 'client',
'email' => 'auth.emails.password',
'table' => 'password_resets',
'expire' => 60,
],
'admins' => [
'provider' => 'admin',
'email' => 'auth.emails.password',
'table' => 'password_resets',
'expire' => 60,
],
],
route.php
Route::group(['middleware' => ['web']], function () {
//Login Routes...
Route::get('/admin/login','AdminAuth\AuthController#showLoginForm');
Route::post('/admin/login','AdminAuth\AuthController#login');
Route::get('/admin/logout','AdminAuth\AuthController#logout');
// Registration Routes...
Route::get('admin/register', 'AdminAuth\AuthController#showRegistrationForm');
Route::post('admin/register', 'AdminAuth\AuthController#register');
Route::get('/admin', 'AdminController#index');
});
AdminAuth/AuthController.php
Add two methods and specify $redirectTo and $guard
protected $redirectTo = '/admin';
protected $guard = 'admin';
public function showLoginForm()
{
if (view()->exists('auth.authenticate')) {
return view('auth.authenticate');
}
return view('admin.auth.login');
}
public function showRegistrationForm()
{
return view('admin.auth.register');
}
it will help you to open another login form for admin
creating a middleware for admin
class RedirectIfNotAdmin
{
/**
* 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 = 'admin')
{
if (!Auth::guard($guard)->check()) {
return redirect('/');
}
return $next($request);
}
}
register middleware in kernel.php
protected $routeMiddleware = [
'admin' => \App\Http\Middleware\RedirectIfNotAdmin::class,
];
use this middleware in AdminController e.g.,
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
class AdminController extends Controller
{
public function __construct(){
$this->middleware('admin');
}
public function index(){
return view('admin.dashboard');
}
}
That's all needed to make it working and also to get json of authenticated admin use
Auth::guard('admin')->user()
Edit - 1
We can access authenticated user directly using
Auth::user() but if you have two authentication table then you have to use
Auth::guard('guard_name')->user()
for logout
Auth::guard('guard_name')->user()->logout()
for authenticated user json
Auth::guard('guard_name')->user()
I have made multi auth but i have problem with final code. I have code like this
php artisan make:auth
it will generate basic login/register route, view and controller for user table.
Make a admin table as users table for simplicity.
Controller For Admin
app/Http/Controllers/AdminAuth/AuthController
app/Http/Controllers/AdminAuth/PasswordController
(note: I just copied these files from app/Http/Controllers/Auth/AuthController here)
config/auth.php
//Authenticating guards
'guards' => [
'user' =>[
'driver' => 'session',
'provider' => 'user',
],
'admin' => [
'driver' => 'session',
'provider' => 'admin',
],
],
//User Providers
'providers' => [
'user' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'admin' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
]
],
//Resetting Password
'passwords' => [
'clients' => [
'provider' => 'client',
'email' => 'auth.emails.password',
'table' => 'password_resets',
'expire' => 60,
],
'admins' => [
'provider' => 'admin',
'email' => 'auth.emails.password',
'table' => 'password_resets',
'expire' => 60,
],
],
route.php
Route::group(['middleware' => ['web']], function () {
//Login Routes...
Route::get('/admin/login','AdminAuth\AuthController#showLoginForm');
Route::post('/admin/login','AdminAuth\AuthController#login');
Route::get('/admin/logout','AdminAuth\AuthController#logout');
// Registration Routes...
Route::get('admin/register', 'AdminAuth\AuthController#showRegistrationForm');
Route::post('admin/register', 'AdminAuth\AuthController#register');
Route::get('/admin', 'AdminController#index');
});
AdminAuth/AuthController.php
Add two methods and specify $redirectTo and $guard
protected $redirectTo = '/admin';
protected $guard = 'admin';
public function showLoginForm()
{
if (view()->exists('auth.authenticate')) {
return view('auth.authenticate');
}
return view('admin.auth.login');
}
public function showRegistrationForm()
{
return view('admin.auth.register');
}
it will help you to open another login form for admin
creating a middleware for admin
class RedirectIfNotAdmin
{
/**
* 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 = 'admin')
{
if (!Auth::guard($guard)->check()) {
return redirect('/');
}
return $next($request);
}
}
register middleware in kernel.php
protected $routeMiddleware = [
'admin' => \App\Http\Middleware\RedirectIfNotAdmin::class,
];
use this middleware in AdminController e.g.,
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
class AdminController extends Controller
{
public function __construct(){
$this->middleware('admin');
}
public function index(){
return view('admin.dashboard');
}
}
And what does this code mean Auth::guard('admin')->user() ? And where must i type that code?
And what does this code mean Auth::guard('admin')->user() ?
In simple word, Auth::guard('admin')->user() is used when you need to get details of logged in user. But, in multi auth system, there can be two logged in users (admin/client). So you need to specify that which user you want to get. So by guard('admin'), you tell to get user from admin table.
Where must i type that code?
As from answer, you can understand that where must you use it. But still I can explain with example. Suppose there are multiple admins. Each can approve users request (like post/comments etc). So when an admin approve any request, then to insert id of that admin into approved_by column of post, you must use this line.