i've reviewed some articles and forums to figure out how to implement JWT authentication in my backend.
the problem is that i've seen different approaches and idk which one is correct.
consider that i've already installed JWT package.
after that some do this:
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
...
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
and then in Controllers constructor using
$this->middleware('auth:api');
to validate user. they say setting 'driver' => 'jwt' will make default guard jwt. also they use
$token = auth()->attempt($credentials)
to validate token. this was first group approach.
second ones never change anything in config/auth.php (no driver change - no gaurd change ...).
they use a middleware in app/Http/Kernel.php (routeMiddleware):
'auth.jwt' => \Tymon\JWTAuth\Http\Middleware\Authenticate::class,
and when making routes they apply it using a group, like:
Route::group(['middleware' => 'auth.jwt'], function () {
Route::get('logout', 'ApiController#logout');
Route::get('user', 'ApiController#getAuthUser');
Route::get('products', 'ProductController#index');
Route::get('products/{id}', 'ProductController#show');
Route::post('products', 'ProductController#store');
Route::put('products/{id}', 'ProductController#update');
Route::delete('products/{id}', 'ProductController#destroy');
});
also they use $jwt_token = JWTAuth::attempt($input) or $this->user = JWTAuth::parseToken()->authenticate(); to get and validate user.
im'm confused with these approaches, should i use jwt as default guard? or i just add it to my packages and use it explicitly?
Related
I am Trying to implement multi auth api in laravel but couldn't protect the api routes. I tried almost every resources available online but failed to achieve my goal. I have used three guards for api authentication user, partner and admin. I am using the following guards
'user-api' => [
'driver' => 'passport',
'provider' => 'users',
],
'partner-api' => [
'driver' => 'passport',
'provider' => 'partners',
],
'admin-api' => [
'driver' => 'passport',
'provider' => 'admins',
],
and my api routes are
// User routes
Route::group(["middleware" => 'auth:user-api'], function () {
Route::get("/usertest", function(){
return "User Test Successful";
});
});
// Partner Routes
Route::group(["middleware" => 'auth:partner-api'], function () {
Route::get("/partnertest", function(){
return "Partner Test Successful";
});
});
// Admin Routes
Route::group(["middleware" => 'auth:admin-api'], function () {
Route::get("/admintest", function(){
return "Admin Test Successful";
});
});
The Problems are
Using User Token:
Now whenever I test using user token it works fine user can access only his own routes not other routes.
Using Partner Token:
with partner token I can access both partner routes and admin routes.
Using Admin Token:
with admin token I can access both admin and partner routes
I just want each type of user can access their own routes not others routes.
i created laravel cms using vue and axios.
i want get current user that sending post requests
so i followed laravel api documentation and take this structure
// bootstrap.js
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
window.axios.defaults.headers.common['Authorization'] = `Bearer ${window.api_token}`;
// vue component file
axios.post('/api/v1/person', this.person)
.then(data => {
this.$emit('added', data.data);
this.person.id = data.data.data.id
});
// Route in api.php
Route::prefix('v1')->name('api.')->group(function () {
/** Person Routes */
Route::prefix('person')->namespace('Person')->name('person.')->group(function(){
Route::post('/', 'PersonController#index');
});
});
//in laravel controller i retrun
return response()->json(auth('api')->user());
but i get this result
even i checked console headers and Authorization header set properly
i can get all post data but laravel don`t pass me the user
also i made a repository of this project in github
if you want can follow this link
https://github.com/mohammadZx/crm
In the documentation it states that you have to set the correct guard for Passport to work. Update auth.php config.
'guards' => [
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
by default, laravel trying to find user by hashing token and for this reason laravel return null because your user api tokens not hashed. so if you change auth.php setting to don't searching hashes api_tokens, this problem will be fixed
'guards' => [
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false
],
],
First of all, i'm not a pro in PHP development or Laravel but will try to explain my question as well as possible for me. I also try to give enough information but when something is missing, please let me know whats missing and i will add it!
Some important context about my project:
Laravel 6.18 (will update soon if my first goal is reached)
I use Hyn/Multi-tenant to make my application mutli tenant.
I use a Vue front end, i give a bearer token to Vue via the app.js
The application should be a multi tenant application where each tenant has its own user table. I first built everything as "single tenant". When the basic functionality was implemented and worked fine, i added Hyn/MT to make it multi tenant, moved the tenant specific tables to the tenant db folder and updated the models. Now i know it was better to start with Hyn/MT before building all the functionality but so far i got everything working fine.
After i added the multi tenant support to my project and fixed the broken functionality i decided to add an admin specific area to manage the tenants. To achieve this i added a SystemU ser table to the master database which contains the admin users. After that i update my web.php so it gives the subdomain to the LoginController.guard() function. My code:
// web.php
Route::group(array('domain' => '{subdomain}.festipay.xlan'), function () {
Route::post('login', 'Auth\LoginController#login');
});
// LoginController.php
protected function guard()
{
if (Request::route("subdomain") == "admin") {
return Auth::guard('admin_web');
} else {
return Auth::guard('web');
}
}
I also updated my config/auth.php, it looks like this now:
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'admin_web' => [
'driver' => 'session',
'provider' => 'admin_users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'admin_users' => [
'driver' => 'eloquent',
'model' => App\SystemUser::class,
]
],
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
],
Except for the mentoined changes i did not implement any admin specific logic yet. So i expect that the admin users are handeled exactly the same except for the way they are authenticated.
The tenant users who log in to e.g. tenant_x.domain.com are redirected to /dashboard when they login and are redirected back to /login when they log out. The admin users who log in to admin.domain.com are not redirected to /dashboard when the login is successfull but are redirected back to /login again. Offcourse this is not the expected behaviour as it should be (currenly) the same as the tenant users (so a redirect to /dasboard when the login is succesfull)
I think that the authentication them selve works fine as the LoginController.attemptLogin() returns true when i use valid admin credentials and false (and view shows wrong credetials) when i use invalid credentials.
I found in this post that is may be a session issue and i tried to apply the solution mentoined in that post. Unfortunately did adding protected $primaryKey = 'id'; to the SystemUser class not solve the issue. I also compared the tenant User class with the SystemUser class but they are almost identical exccept for unimportant fields i removed from the SystemUser like address.
I have no idea how i can find out where the issue occurs or how to solve this. The goal is that an admin which logged in succesfully is redirect to another page as the /dashboard. Can someone help me find out what goes wrong? i'm already happy when someone can help me to get the same behaviour for the admin's as the tenants currently have.
Thanks in advance!
Update 1 #David Barker
When its about the session, i think this is important to know as well:
- I use a Vue front end, i give a bearer tokento Vue via theapp.js``
My session config:
<?php
use Illuminate\Support\Str;
return [
'driver' => env('SESSION_DRIVER', 'file'),
'lifetime' => env('SESSION_LIFETIME', 120),
'expire_on_close' => false,
'encrypt' => false,
'files' => storage_path('framework/sessions'),
'connection' => env('SESSION_CONNECTION', null),
'table' => 'sessions',
'store' => env('SESSION_STORE', null),
'lottery' => [2, 100],
'cookie' => env(
'SESSION_COOKIE',
Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
),
'path' => '/',
'domain' => env('SESSION_DOMAIN', null),
'secure' => env('SESSION_SECURE_COOKIE', false),
'http_only' => true,
'same_site' => null,
];
I did a dd($request->session();) in the LoginController->attemptLogin(); function, see the result bellow. The result is the same for both users except for the id and _token. (i cleaned the cookies before the login attempt in both cases)
Illuminate\Session\Store {#490 ▼
#id: "7WI7JUWPnS4pg3EHvaxk5TOKaM9l9UXJi1zJNKuG"
#name: "festipay_session"
#attributes: array:1 [▼
"_token" => "mtMWanYGMUxFHivOqAaEmVQnHDE0hvwKkHMgCswg"
]
#handler: Illuminate\Session\FileSessionHandler {#489 ▼
#files: Illuminate\Filesystem\Filesystem {#198}
#path: "/var/www/domain.com/storage/framework/sessions"
#minutes: "120"
}
#started: true
}
Maybe this is also interesting infomation. Left are the responses for the admin (after i clicked the login button) and right the working tenant login.
I finally found the issue. It was very easy to solve. I did not specify the auth guard for the Route::group.
It was like this:
Route::group(['middleware' => 'auth'], function () {
Route::get('/', function () { return redirect('/dashboard'); });
Route::get('/dashboard', function () { return view('dashboard'); })->name('dashboard');
Route::get('/logout', 'Auth\LoginController#logout')->name
I changed it to this to make it work:
Route::group(['middleware' => 'auth:system_user_web,web'], function () {
Route::get('/', function () { return redirect('/dashboard'); });
Route::get('/dashboard', function () { return view('dashboard'); })->name('dashboard');
Route::get('/logout', 'Auth\LoginController#logout')->name
I am using Laravel 5.6 with spatie/laravel-permission version 2.9 also using Laravel Passport as auth driver with $guard = 'api'.
When I am trying to assign an array of permission like ['edit_project', 'add_project' 'delete_project'] to a role with help of this function
public function assignPermissions($role, $permissions)
{
$role = Role::findByName($role);
$role->givePermissionTo($permissions);
return $role;
}
but getting the error There is no permission namededit_projectfor guardapi`.
Also I have at config/auth.php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| 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.
|
| Supported: "session", "token"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
/*
|--------------------------------------------------------------------------
| 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,
],
// '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,
],
],
];
if there is any solution please help me with it thanks.
as well I am seeding the permission table by help of Larvel seeder which my permission table looks at the first time like below which the guard_name is web.
but manually I am changing the guard_name field to "api" which my permission table became like this.
After creating permissions, running the following commands should work as it worked for me.
php artisan cache:forget spatie.permission.cache
then
php artisan cache:clear
Note: In Ubuntu you may need to run these commands as sudo...
Clear your cache php artisan cache:clear
if this does not work use sudo php artisan cache:clear it worked for me once i use sudo
The package uses the default guard unless instructed otherwise. The way to instruct it otherwise is to add the following to the Role class public $guard_name = 'api';. Of course adding that to the class in the vendor directory is a bad idea so you'd want to extend it and specify the guard like this
use Spatie\Permission\Models\Role as OriginalRole;
class Role extends OriginalRole
{
public $guard_name = 'api';
}
Then if you haven't done so already, generate the config file with php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="config"
Lastly you'll want to register your Role in config/permissions.php by changing 'role' => Spatie\Permission\Models\Role::class, to 'role' => \App\Models\Role::class, (of course this will vary based on where your Role class is)
Also the example from your question mentions add_project but the database shows create_project so make sure you're using the same names everywhere.
Move the web and api places from
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
To
'guards' => [
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
'web' => [
'driver' => 'session',
'provider' => 'users',
],
]
run php artisan cache:clear
It might be a permissions issue. run below command.
sudo php artisan permission:cache-reset
In your user model add protected $guard_name = 'api'; This will override the default guard which is web.
You need to specify the guard when creating a role or permission failure of which spatie will take on the first guard that appears in the config/auth in this case "web"
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
You need to approach as follows:
// Create a manager role for users authenticating with the api guard:
$role = Role::create(['guard_name' => 'api', 'name' => 'manager']);
// Define a `edit_project` permission for the admin users belonging to the api guard
$permission = Permission::create(['guard_name' => 'api', 'name' => 'edit_project']);
If you want to do this from your code you can clear the cache straight from your code.
\DB::table('permissions')->insert([['name' => 'create Stuff', 'guard_name' => 'web']]);
\Artisan::call('cache:clear');
$role = Role::findByName('admin');
$role->givePermissionTo('create Stuff');
Empty the role and permission tables in the database and then fill in the tables again. I had this error and I fixed it this way
Sometimes, we'd like to separate users and admins in different 2 tables.
I think it is a good practice.
I am looking if that is possible in Laravel 5.
Before reading the following, you are supposed to have basic knowledge on ServiceProvider, Facade and IoC in Laravel 5. Here we go.
According to the doc of Laravel, you could find the Facade 'Auth' is refering to the Illuminate\Auth\AuthManager, which has a magic __call(). You could see the major function is not in AuthManager, but in Illuminate\Auth\Guard
Guard has a Provider. This provider has a $model property, according to which the EloquentUserProvider would create this model by "new $model". These are all we need to know. Here goes the code.
1.We need to create a AdminAuthServiceProvider.
public function register(){
Auth::extend('adminEloquent', function($app){
// you can use Config::get() to retrieve the model class name from config file
$myProvider = new EloquentUserProvider($app['hash'], '\App\AdminModel')
return new Guard($myProvider, $app['session.store']);
})
$app->singleton('auth.driver_admin', function($app){
return Auth::driver('adminEloquent');
});
}
2.Facade:
class AdminAuth extends Facade {
protected static function getFacadeAccessor() { return 'auth.driver_admin'; }
}
3. add the alias to Kernel:
'aliases' => [
//has to be beneath the 'Auth' alias
'AdminAuth' => '\App\Facades\AdminAuth'
]
Hope this could be helpful.
I have created a laravel package where you can handle multiple authentication.
Step 1 : Composer require
Firstly, composer require the multiauth package
composer require sarav/laravel-multiauth dev-master
Step 2 : Replacing default auth service provider
Replace
Illuminate\Auth\AuthServiceProvider::class
with
Sarav\Multiauth\MultiauthServiceProvider
in your config/app.php file
Step 3 : Modify auth.php
Modify your config/auth.php file to something like this
'multi' => [
'user' => [
'driver' => 'eloquent',
'model' => App\User::class,
'table' => 'users'
],
'admin' => [
'driver' => 'eloquent',
'model' => App\Admin::class,
'table' => 'admins'
]
],
Thats it! Now you can try multiple authentication by passing the user as first parameter. For example
\Auth::loginUsingId("user", 1); // Login user with id 1
\Auth::loginUsingId("admin", 1); // Login admin with id 1
// Attempts to login user with email id johndoe#gmail.com
\Auth::attempt("user", ['email' => 'johndoe#gmail.com', 'password' => 'password']);
// Attempts to login admin with email id johndoe#gmail.com
\Auth::attempt("admin", ['email' => 'johndoe#gmail.com', 'password' => 'password']);
For more detailed documentation
http://sarav.co/blog/multiple-authentication-in-laravel/
http://sarav.co/blog/multiple-authentication-in-laravel-continued/