I am trying to use JWT for laravel web page instead of session. so I made some changes.
Installed jwt-auth and configure
Then changed default guard as api in config/auth.php
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
...
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
Now I am getting error
(1/1) FatalErrorException Call to undefined method
Illuminate\Auth\TokenGuard::attempt() in AuthenticatesUsers.php (line
75)
How to fix this and start token authentication for laravel web page(blades not API).
I'm also using jwt protecting our api. You should change your config like below:
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
...
'api' => [
'driver' => 'jwt', // KEY POINT!!!
'provider' => 'users',
],
],
Make sure the jwt library installed correctly:
Tymon\JWTAuth\Providers\LaravelServiceProvider::class is added in your config/app.php.
Your user model implements JWTSubject interface if you use eloquent model in your provider.
I found the solution here : https://github.com/tymondesigns/jwt-auth/issues/860
In /routes/api.php - added a few basic authentication routes
Route::post('login', 'Auth\LoginController#login');
Route::get('/user', function (Request $request) {
$user = $request->user();
return dd($user);
})->middleware('auth:api');
In /app/http/Controller/auth/LoginController.php
and then override methods in login contoller
public function login(Request $request)
{
$credentials = $request->only(["email","password"]);
if ($token = $this->guard()->attempt($credentials)) {
return $this->sendLoginResponse($request, $token);
}
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
protected function sendLoginResponse(Request $request, $token)
{
$this->clearLoginAttempts($request);
return $this->authenticated($request, $this->guard()->user(), $token);
}
protected function authenticated(Request $request, $user, $token)
{
setcookie("jwt_token", $token);
return redirect('/');
return response()->json([
'token' => $token,
]);
}
protected function sendFailedLoginResponse(Request $request)
{
return response()->json([
'message' => "not found",
], 401);
}
Adding middleware AddToken
public function handle($request, Closure $next)
{
$token = isset($_COOKIE["jwt_token"])?$_COOKIE["jwt_token"]:"";
//$request['token'] = $token;//this is working
$request->headers->set("Authorization", "Bearer $token");//this is working
$response = $next($request);
//$response->header('header name', 'header value');
return $response;
}
Register middleware in Kernel.php
protected $middleware = [
....
\App\Http\Middleware\AddToken::class,
];
I think you can try this :
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
EDIT
You can find some help from the step by step example. In this example you need to focus on how to configure and use that token base authentication.
Hope this help you well.
Please refer this link. If you are using api as default then laravel authentication will throw an error.
Laravel uses default Session based authentication out of the box with the default scaffolding users-view-controller that you already have. You have additional means of adding your own custom guard in the doc, so you can make use of the guard as needed.
Therefore as #KevinPatel suggested, revert back to the default configuration, then in your route: group the route you want to be under JWT authentication, add the JWTMiddleware, in this case you have to update the controller responsible for your authentication to use the JWTAuth instead of the default auth.
You should check this answer if you need to understand it better check this answer on Laracasts
One recommended way to incorporate the JWTAuth is to go for Dingo API (of course you are not building api, but) because Dingo already added some flesh to the authentication and other routes management - so things are pretty easy to use and configure
Related
First things first, I am using Hyn-Multi Tenant in my laravel 6 application Where there is a central database [connection = system] handles multiple tenant database. So far this package has helped me a lot but my application needs passport implementation for apis which is not documented in the package.
However there are other tutorials which claim passport implementation on Hyn package. I followed them and able to create access token per tenant user.
This is my config/auth.php:
return [
'defaults' => [
'guard' => 'web',
'passwords' => 'system-users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'system',
],
'staff' => [
'driver' => 'session',
'provider' => 'staff',
],
'api' => [
'driver' => 'passport',
'provider' => 'staff',
'hash' => false,
],
'student' => [
'driver' => 'passport',
'provider' => 'student',
'hash' => false,
],
],
'providers' => [
'system' => [
'driver' => 'eloquent',
'model' => App\Models\System\User::class,
],
'staff' => [
'driver' => 'eloquent',
'model' => App\Models\Tenant\Staff::class,
],
'student' => [
'driver' => 'eloquent',
'model' => App\Models\Tenant\Student::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
My each tenant models uses UsesTenantConnection trait
This is my EnforceTenancy middleware
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Config;
class EnforceTenancy
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
Config::set('database.default', 'tenant');
return $next($request);
}
}
This is my AuthServiceProvider.php
public function boot()
{
$this->registerPolicies();
Passport::routes(null, ['middleware' => 'tenancy.enforce']);
// FOLLOWING CODE IS HAVING PROBLEM
//Passport::useTokenModel(OAuthAccessToken::class);
//Passport::useClientModel(OAuthClient::class);
//Passport::useAuthCodeModel(OAuthCode::class);
//Passport::usePersonalAccessClientModel(OAuthPersonalAccessClient::class);
$this->commands([
\Laravel\Passport\Console\InstallCommand::class,
\Laravel\Passport\Console\ClientCommand::class,
\Laravel\Passport\Console\KeysCommand::class,
]);
\Laravel\Passport\Passport::tokensExpireIn(\Carbon\Carbon::now()->addMinutes(10));
\Laravel\Passport\Passport::refreshTokensExpireIn(\Carbon\Carbon::now()->addDays(1));
}
So far all good, now I am going to explain in points,
When I call createToken('MyApp') I am able to generate token on tenant db, for example:
if (Auth::guard('staff')->attempt(['email' => $request->email, 'password' => $request->password])) {
$user = Auth::guard('staff')->user();
$auth_tokens = $user->createToken('MyApp');
$access_token = $auth_tokens->accessToken;
...
}
but to access login protected apis, I am sending bearer access token in header
window.axios
.get("/api/meta",{
headers: fetchAuthHeaders()
})
.then(response => {
if(true == response.data.status) {
var data = response.data.data;
this.school.name = data.school_meta.name;
this.school.logo = data.school_meta.logo;
} else{
alert(response.data.message);
}
})
api.php
Route::domain('{hostname}.lvh.me')->group(function () {
Route::middleware('tenant.exists')->group(function () {
Route::get('/get-oauth-secret', 'Tenant\MetaController#getOAuthData');
Route::post('validate-login','Tenant\AuthController#validateLogin');
Route::middleware(['auth:api'])->group(function (){
Route::get('meta','Tenant\AuthController#getMetaData'); //this api
});
});
});
I am getting response as {"message":"Unauthenticated."}
Once the token is generated in step 1, I copy this token and paste into postman's header section and uncomment the custom passport models in AuthServiceProvider.php as shown below
AuthServiceProvider.php
public function boot()
{
...
// UNCOMMENTED FOLLOWING CUSTOM PASSPORT MODELS
Passport::useTokenModel(OAuthAccessToken::class);
Passport::useClientModel(OAuthClient::class);
Passport::useAuthCodeModel(OAuthCode::class);
Passport::usePersonalAccessClientModel(OAuthPersonalAccessClient::class);
...
}
Now I can access api/meta route but while login and creating token I am getting error:
ErrorException: Trying to get property 'id' of non-object in file /home/winlappy1/Desktop/multi_tenancy/vendor/laravel/passport/src/PersonalAccessTokenFactory.php on line 98
I just want to know where I am going wrong, I know my explanation is quite ambiguous and confusing but thats all how I can explain my issue. I am ready to provide more clarification but I need to resolve this issue.
Try to add
\App\Http\Middleware\EnforceTenancy::class
into the beginning of $middlewarePriority array in Kernel.php
Also use Laravel Passport 9.1.0 which support multi Auth
Try to do this
#AuthServiceProvider
Add this
public function boot()
{
$this->registerPolicies();
This one is to check if the database is Tenant or not
$website = \Hyn\Tenancy\Facades\TenancyFacade::website();
if ($website != null) {
Passport::useClientModel(PassportClient::class);
Passport::useTokenModel(PassportToken::class);
Passport::useAuthCodeModel(PassportAuthCode::class);
Passport::usePersonalAccessClientModel(PassportPersonalAccessClient::class);
}
$this->commands([
\Laravel\Passport\Console\InstallCommand::class,
\Laravel\Passport\Console\ClientCommand::class,
\Laravel\Passport\Console\KeysCommand::class,
]);
\Laravel\Passport\Passport::tokensExpireIn(\Carbon\Carbon::now()->addMinutes(10));
\Laravel\Passport\Passport::refreshTokensExpireIn(\Carbon\Carbon::now()->addDays(1));
}
Along with these add The four models
Like this
Create four Models Which enforce the Tenants
use Hyn\Tenancy\Traits\UsesTenantConnection;
use Laravel\Passport\AuthCode;
class PassportAuthCode extends AuthCode
{use UsesTenantConnection;}
use Hyn\Tenancy\Traits\UsesTenantConnection;
use Laravel\Passport\Client;
class PassportClient extends Client
{use UsesTenantConnection;}
use Hyn\Tenancy\Traits\UsesTenantConnection;
use Laravel\Passport\PersonalAccessClient;
class PassportPersonalAccessClient extends PersonalAccessClient
{use UsesTenantConnection;}
use Hyn\Tenancy\Traits\UsesTenantConnection;
use Laravel\Passport\Token;
class PassportToken extends Token
{use UsesTenantConnection;}
Also use (tenancy.enforce) middleware Enforcetenancy
'tenancy.enforce' => \App\Http\Middleware\EnforceTenancy::class, $Routemiddleware kernel.php
EnforceTenancy.php middleware
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
class EnforceTenancy
{
/**
* Handle an incoming request.
*
* #param Request $request
* #param Closure $next
*
* #return mixed
*/
public function handle($request, Closure $next)
{
Config::set('database.default', 'tenant');
return $next($request);
}
}
Force the tenant routes through tenancy.enforce middleware
Also publish the new migrations and migrate:fresh as new fields are added to the new passport
I'm developing a Laravel 6 app with 4 different users(with a different table for each). For this, I am using multi auth guard and created my first guard following this tutorial online (https://pusher.com/tutorials/multiple-authentication-guards-laravel).
Here is my custom guard "student"
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
'student' => [
'driver' => 'session',
'provider' => 'students',
],
],
And Providers
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'students' => [
'driver' => 'eloquent',
'model' => App\Models\Student::class,
],
],
I have a custom login controller using a custom auth guard named 'student'
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Auth;
use Student;
class LoginController extends Controller
{
protected $redirectTo = '/dashboard';
public function __construct()
{
$this->middleware('guest')->except('destroy');
$this->middleware('student')->except('destroy');
}
public function studentLogin(Request $request)
{
$this->validate($request, [
'email' => 'required|email',
'password' => 'required|min:5'
]);
if (Auth::guard('student')->attempt(['email' => $request->email, 'password' => $request->password], $request->get('remember'))) {
return redirect()->intended('admin/home')->with('smessage', 'Logged in successfully...!');
}
return back()->withInput($request->only('email', 'remember'));
}...
}
Now, Whenever I try to log in, It seems like I am logged in and redirected to the URL defined in the middleware/RedirectIfAuthenticated.php but this error pops up "Trying to get property of non-object" which is from the common header template where no data of logged user are available on auth()->user().
If I use 'middleware'=>'auth:student', I can access the route as student and have the logged user data via auth()->user(). same for admin using 'middleware'=>'auth' but passing both auth and auth:student as an array I can't get the logged user data via auth()->user() for both user.
Here are my routes:
Route::get('/', function () {
return view('welcome');
});
Route::get('/login', 'LoginController#create')->name('login');
Route::post('login', 'LoginController#store');
Route::get('/login/student', 'LoginController#create')->name('studentlogin');
Route::post('/login/student', 'LoginController#studentLogin');
Route::get('/logout', 'LoginController#destroy')->name('logout');
Route::group(['prefix'=>'admin', 'namespace' => 'Dashboard', 'middleware'=> 'auth' ], function () {
Route::get('home', 'DashboardController#display')->name('admin.home');
Route::get('users', 'UserlistController#display')->name('admin.userlist');
Route::post('users', 'UserlistController#store')->name('admin.create');
Route::delete('users/{id}', 'UserlistController#destroy')->name('admin.deleteuser');
});
Route::group(['prefix'=>'student', 'namespace' => 'Students', 'middleware'=> 'auth:student' ], function () {
Route::get('all', 'StudentsController#display')->name('student.all');
Route::delete('all/{id}', 'StudentsController#destroy')->name('student.delete');
Route::post('all', 'StudentsController#store')->name('student.store');
});
Couldn't find any solutions from other related topics on StackOverflow. Please do correct me if I have any mistakes somewhere or let me know if you want to inspect any other files for the code.
Thanks in advance.
You don't have any data on auth()->user() because you're querying the default guard which is web.
If you want to retrieve the logged user through student guard, you have to do auth()->guard('student')->user();
Also remember to pass everytime your guard to auth and middlewares that uses auth.
I.e.: if you need to make some routes restricted only to authenticated users you will need to do:
Route::group(['middleware' => 'auth:student'], function() {
// Your restricted routes
})
Omitting :student will use the default web guard as always
Note that if you need to restrict your routes to a group of users belonging to different guards you can pass those guards to auth:
Route::group(['middleware' => 'auth:web,student'], function() {
// Your restricted routes
}];
In this way Laravel will check for both web and student guards and will automatically set the one that belongs to the authenticated user. You will also be able to retrieve your user only by doing auth()->user() forgetting to pass the right guard
I'm trying to create an API with Bearer Token but I can't figure it out:
What does the route::middleware('auth:api') do
Where's the code of route::middleware('auth:api')
So, I have the following code in my Routes\Api.php file:
Route::get('/login', function (Request $request)
{
if(Auth::guard()->attempt(['email' => $request->email, 'password' => $request->password]) == FALSE)
return response()->json(['status' => FALSE]);
$user = Users::select('id', 'name', 'api_token', 'created_at')->where('email', $request->email)->firstOrFail();
return response()->json(['status' => TRUE, 'user' => $user]);
});
Route::middleware('auth:api')->get('/bookings', function (Request $request)
{
return response()->json(['its working!']);
});
I'm able to successfully connect to the route /login and retrieve the api_token. Now this token must be used in the /bookings route in order to authenticate.
I was hopping the middleware('auth:api')verify my CURL headers for the Authorization: Bearer zzzzzzzzz, but its not working.
So basically I need to understand how do I change the code logic behind auth:api or if I should create a new middleware and check for the request headers?
Diclamer
If you need custom code to handle authentication you should create your own middleware and authentication guard and use it instead of the default one that Laravel provides.
Your questions
What does the route::middleware('auth:api') do
It states that the route should implement the middleware "auth" and the middleware group "api".
Where's the code of route::middleware('auth:api')
All middleware in Laravel is defined in app/Http/Kernel.php.
In there you will probably see something like
protected $middlewareGroups = [
....,
'api' => [
'throttle:60,1',
'bindings',
],
];
and
protected $routeMiddleware = [
...,
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];
This means that a route using the middleware auth:api implements the api middleware group (in this case the ThrottleRequests and SubstituteBinding middleware) and the auth middleware (Authenticate).
The actual authentication guard used depends on the configuration in your auth.php config file:
'guards' => [
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
In the case above a TokenGuard is used (laravel/framework/src/Illuminate/Auth/TokenGuard.php).
So to answer your question, the code for the auth middleware can be found at
laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php
Auth middleware in laravel to authenticate users using my custom guard but everytime i call a route with that middleware I get the error that :
Route [login] not defined.
Right now, i am just trying to make sure that the middleware is being called.This is what i have done so far:
public function handle($request, Closure $next, $guard = null)
{
return "hi";
if (Auth::guard('api')->guest()) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
}
return redirect()->guest('hi/login');
}
return $next($request);
}
the above is the handle method for authenticate.php. this is my code for guard
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'access_token',
'provider' => 'users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
],
The Routes:
This is the route group in which i have a sub-group that implements the auth
middleware
Route::group(['prefix' => 'app'], function() use ($router) { }
This is the route i am testing the middleware on Route::get('/subscribedcompanies','PromotionController#getFavoriteCompanies');
One more Thing i'd like to add is i'm trying to authenticate based on access_token from database. i have changed the authenticate and credentials function in login controller as follows:
protected function credentials(Request $request)
{
return array_merge($request->header('authorization'));
}
public function authenticate(Request $request)
{
$credentials = $request->header('authorization');
if (Auth::attempt($credentials)) {
// Authentication passed...
return redirect()->intended('/');
}
}
just give name to your login route as login
Route::post('/login', 'LoginController#index')->name('login');
In Laravel 5.4, is there a way to send the password reset link to a separate authentication guard instead of the default one. I am using the default PasswordResetController which does the job in this way
public function company(Request $request)
{
$this->validate(request(), [
'email' => 'required|email',
]);
$response = Password::sendResetLink([
'email' => $request->email
]);
//overridden if condition
if($response == "passwords.sent")
{
return back()->with('message','Password reset link has been sent, please check your email');
}
return back()->with('message', 'No such email address in our records, try again');
}
The sendResetLink() method checks and sends the reset link to the default guard, but I am defining a new guard in auth.php called web
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'companies',
],
sendResetLink method is like this
public function sendResetLink(array $credentials)
{
// First we will check to see if we found a user at the given credentials and
// if we did not we will redirect back to this current URI with a piece of
// "flash" data in the session to indicate to the developers the errors.
$user = $this->getUser($credentials);
if (is_null($user)) {
return static::INVALID_USER;
}
Any way for this method to check in a separate table or use a separate auth guard?
Here is my method of how to send password reset link to a guard which is a part of multi-auth system.
I am going to assume you have properly set your new guard in config/auth.php
which may look like below code:
I use the word admin for the new guard's name for better understanding.
'guards' => [
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
]
'providers' => [
'admins' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
],
]
'passwords' => [
'admins' => [
'provider' => 'admins',
'table' => 'password_resets',
'expire' => 15,
],
]
You will have to create new controllers(AdminForgotPasswordController and AdminResetPasswordController) for your new guard
They both use the Password facade and AdminResetPasswordController uses Auth facade as well.
Modify the construct function according to your new guard.
So add this to both controllers, because we have specific type of guest users.
public function __construct()
{
$this->middleware('guest:admin');
}
Now we need to tell AdminResetPasswordController to use the proper guard for authenticating.
So add this method to the controller
protected function guard()
{
return Auth::guard('admin');
}
Now add this piece of code to both controllers.
protected function broker()
{
return Password::broker('admins'); //set password broker name according to guard which you have set in config/auth.php
}
Notice: It's not the only step for implementing password reset for guard you will have to take other steps like creating new routes, notifications, forms and corresponding views.
In your auth.php configuration file, you may configure multiple "guards", which may be used to define authentication behavior for multiple user tables. You can customize the included ResetPasswordController to use the guard of your choice by overriding the guard method on the controller. This method should return a guard instance:
protected function guard()
{
return Auth::guard('guard-name');
}
The magic lies, in using the broker - the PasswordBroker for your custom guard. But, you make sure to set up multiple password brokers in your auth.php configuration file.
protected function broker()
{
return Password::broker('name');
}
One simple way could be
$response = Password::broker('guard-name')->sendResetLink([
'email' => $request->email
]);
also don't miss reset function
$response = Password::broker('guard-name')->reset([
'email' => $request->email
],function(){// save changes})