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 :)
Related
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
I'm trying to implements an LDAP authentication in an application for my company. I'm using Laravel 5.8 and the LdapRecord package (https://github.com/DirectoryTree/LdapRecord-Laravel).
I have succeed to connect the application with the LDAP server but the authentication still not working and idk why... :(
Here is my code :
The LoginController
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use LdapRecord\Container;
use Illuminate\Http\Request;
use App\User;
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;
/**
* LDAP Connection
*/
private $connection;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->connection = Container::getConnection('default');
$this->middleware('guest')->except('logout');
}
public function username() {
return 'username';
}
protected function credentials(Request $request)
{
return [
'comptent' => $request->username,
'password' => $request->password,
];
}
}
Here is the configuration - auth.php :
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'ldap',
],
// some code
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Ldap\User::class,
],
'ldap' => [
'driver' => 'ldap',
'model' => LdapRecord\Models\ActiveDirectory\User::class,
'rules' => [],
'database' => [
'model' => App\Ldap\User::class,
'sync_passwords' => false,
'sync_attributes' => [
'LASTNAME' => 'sn',
'FIRSTNAME' => 'givenname',
'ACTIVE_DIRECTORY_USER' => 'comptent'
The user model - User.php:
<?php
namespace App\Ldap;
//use Illuminate\Database\Eloquent\Model;
use LdapRecord\Laravel\Auth\HasLdapUser;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use LdapRecord\Laravel\Auth\AuthenticatesWithLdap;
use LdapRecord\Laravel\Auth\LdapAuthenticatable;
use LdapRecord\Models\Model;
/**
* Class User
*
* #property int $ID_USER
* #property string $LASTNAME
* #property string $FIRSTNAME
* #property string $ACTIVE_DIRECTORY_USER
* #property int $ID_ROLE
*
* #property Role $role
*
* #package App\Models
*/
class User extends Model
{
use Notifiable, AuthenticatesWithLdap;
/**
* The object classes of the LDAP model.
*
* #var array
*/
public static $objectClasses = [];
protected $table = 'user';
protected $primaryKey = 'ID_USER';
public $incrementing = false;
public $timestamps = false;
protected $casts = [
'ID_USER' => 'int',
'ID_ROLE' => 'int'
];
protected $fillable = [
'LASTNAME',
'FIRSTNAME',
'ACTIVE_DIRECTORY_USER',
'ID_ROLE'
];
public function role()
{
return $this->belongsTo(Role::class, 'ID_ROLE');
}
}
Here is some logs that i have when i try to login :
[2020-02-19 15:49:12] local.INFO: LDAP (ldap://srv-gldap1:389) - Operation: Listing - Base DN: ou=utilisateurs,dc=rms,dc=fr - Filter: (objectclass=*) - Selected: (*) - Time Elapsed: 922.65
[2020-02-19 15:49:12] local.INFO: LDAP (ldap://srvil-gdldap1:389) - Operation: Search - Base DN: ou=utilisateurs,dc=rms,dc=fr - Filter: (comptentdomaine=hippolyte.massicot#rms.local) - Selected: (*) - Time Elapsed: 101.91
Is there anyone who has already used this package and can help me on my code ?
thank you in advance :)
public function login( Request $request ) {
$fields = [
'email' => $request->email,
'password' => $request->password,
];
$access = Auth::attempt( $fields );
echo $access;
if ( $access ) {
$user = Auth::teacher();
$token = $user->createToken('MyApp')->accessToken;
return response()->json( [
"message" => "Login realizado com sucesso!",
"data" => [
'user' => $user,
'token' => $token
]
], 200 );
} else {
return response()->json( [
"message" => "Email ou senha inválidos!",
"data" => null,
"return" => $access,
], 401 );
}
I have this function to login, I am try with a Model Teacher, but always auth::attempt gave me false, but if I am try with User Model the result is true.
My Model Teacher
<?php
namespace App;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
use App\Student;
use App\Rating;
use App\Commentary;
use App\User;
class Teacher extends Authenticatable
{
use Notifiable;
use HasApiTokens;
/**
* 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',
];
public function students(){
return $this->belongsToMany('App\Student')->withTimestamps();
}
public function ratings(){
return $this->hasMany('App\Rating');
}
public function commentaries(){
return $this->hasMany('App\Commentary');
}
public function updateTeacher(Request $req){
$validator = Validator::make($request->all(),[
]);
if($validator->fails()){
return response()->json($validator->errors());
}
if ($req->name)
$this->name = $req->name;
if ($req->email)
$this->email = $req->email;
if ($req->password)
$this->password = $req->password;
if ($req->number)
$this->number = $req->number;
if ($req->birth)
$this->birth = $req->birth;
if ($req->CPF)
$this->CPF = $req->CPF;
if ($req->lesson_price)
$this->lesson_price = $req->lesson_price;
if ($req->rent_price)
$this->rent_price = $req->rent_price;
if ($req->description)
$this->description = $req->description;
if ($req->district)
$this->district = $req->district;
if ($req->zone)
$this->zone = $req->zone;
if ($req->instruments)
$this->instruments = $req->instruments;
if ($req->certification)
$this->certification = $req->certification;
$this->save();
}
public function listTeachers(){
$paginator = Teacher::paginate(10);
return response()->json([$paginator]);
}
public function showTeacher($id){
$teacher = Teacher::findOrFail($id);
return response()->json([$teacher]);
}
}
My Model User
<?php
namespace App;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use Notifiable;
use HasApiTokens;
/**
* 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',
];
}
So i am think that Teacher have the same functions and most operations like User, thus the function auth::attempt should works with Teacher.
add teacher model to the guard in config/auth.php and then use
Auth::guard('teacher')->attempt($credentials)
'guards' => [
'user' => [
'driver' => 'session',
'provider' => 'users',
],
'teacher' => [
'driver' => 'session',
'provider' => 'teachers',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'teachers' => [
'driver' => 'eloquent',
'model' => App\Teacher::class,
],
],
Default Laravel authentication only works with User model. It is specified in config/auth.php. If you want to use another model for authentication you can just change in auth.php.
If you want multiple models to be used for Authentication, for example, both User and Teacher , you need to look into Guards in Laravel.
Here is a link to a tutorial which explains this technique.
HOW TO USE MULTIPLE AUTHENTICATION GUARDS IN A LARAVEL APP
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()
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