I'm developing currently multi auth with Laravel Passport, so the app is gonna have users, and devices, and when i try to register with the devices it saves it to the devices database ,and if i try to login it gives me the Bearer token. But right now i want to get user middleware 'auth:api' or other way to get device information via token,but its seems that the tokens are stored in oauth_access_token table and with user_id .So is there a way to user laravel passport for another table except for users ? Thanks ?
Here is my code for Devices:
<?php
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
use SMartins\PassportMultiauth\HasMultiAuthApiTokens;
class Device extends Authenticatable{
use Notifiable,HasApiTokens;
protected $fillable = [
'name', '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',
];
}
Device Controller :
<?php
namespace App\Http\Controllers;
use App\Device;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class DeviceController extends Controller{
//register
public function signupDevice(Request $request){
//cant registed with the same email twice
if(sizeof(Device::where('name','=',$request->query('name'))->get()) > 0)
return response()->json(['name has already been taken'],500);
$request->validate([
'name' => 'required|string',
'password' => 'required|string|confirmed']);
$device =new Device(
[
'name'=>$request->name,
'password'=>bcrypt($request->password)
]);
$device->save();
return response()->json([
'message' => 'Successfully created device!'
], 201);
}
public function login(Request $request){
//validate the data input
$request->validate([
'name' => 'required|string',
'password' => 'required|string',]);
//attempt returns true if the user is in the database
$credentials = request(['name', 'password']);
if(!Auth::guard('device')->attempt($credentials))
return response()->json([
'message' => 'Unauthorized'
], 401);
//get the device
$device = $request->user('device');
//create token PAT
$tokenResult = $device->createToken('Personal Access Token');
$token = $tokenResult->token;
if ($request->remember_me)
$token->expires_at = Carbon::now()->addWeeks(1);
//save the token
$token->save();
return response()->json([
'access_token' => $tokenResult->accessToken,
'token_type' => 'Bearer',
'expires_at' => Carbon::parse(
$tokenResult->token->expires_at
)->toDateTimeString()
],200);
}
public function index(Request $request)
{
return response()->json($request->user());
}
}
Routes:
//routes for device auth
Route::group(
[
'prefix'=>'auth/device'
],function ()
{
Route::post('signup','DeviceController#signupDevice');
Route::post('login','DeviceController#login');
Route::group(
[
'middleware'=>'device'
],function(){
//all the routes that go throught middleware
Route::get('index','DeviceController#index');
});
});
Okay so As I was doing this myself I ended up in a roadblock:
The tokens registred in the database may end up with duplicate user_id, because your two tables will have different auto-increment values.
The token has a name property, so when a customer (in your case a device) that has the same user_id, you can differentiate them with the name.
To achieve what you are asking you need to declare another provider and another guard. It can be found at config/auth.php
<?php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'user' => [
'driver' => 'session',
'provider' => 'users',
],
'user-api'=>[
'driver'=>'passport',
'provider'=>'users',
'hash'=>false,
],
'customer' => [
'driver' => 'session',
'provider' => 'customers',
],
'customer-api'=>[
'driver'=>'passport',
'provider'=>'customers',
'hash'=>false,
],
],
...
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'customers'=>[
'driver'=>'eloquent',
'model'=>App\Customer::class,
],
],
...
the new model need to implement new properties/class to make it work:
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
//
class Customer extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
protected $guarded=['id'];
protected $fillable = [
/*fillable props*/
];
//
protected $hidden=[
/*Hidden props*/
];
//
protected $casts=[
/*Your casts*/
];
public function getAuthPassword()
{
return $this->password;
}
}
Now you need to prefix http request with the api middleware, inside App/Providers/RouteServiceProvider:
public function boot()
{
$this->configureRateLimiting();
$this->routes(function () {
Route::prefix('api')
->middleware('api')
->group(base_path('routes/api.php'));
}
Then when you declare a route use the new auth middleware guard with the passport driver you declared:
Route::group( ['prefix' => 'customer','middleware' => ['auth:customer-api'] ],function(){
/*Authenticated staff route here*/
});
When you log in you should use the auth guard with the session driver:
public function customerLogin(Request $request)
{
$validator = Validator::make($request->all(), [
'email' => 'required|email',
'password' => 'required',
]);
if($validator->fails()){
return response()->json(['error' => $validator->errors()->all()]);
}
if(auth()->guard('customer')->attempt(['email' => request('email'), 'password' => request('password')])){
config(['auth.guards.api.provider' => 'customer']);
$customer = Customer::select('customers.*')->find(auth()->guard('customer')->user()->id);
$success = $customer;
$success['token'] = $customer->createToken('Customer'.$customer->name,['customer'])->accessToken;
return response()->json($success, 200);
}else{
return response()->json(['error' => ['Email and Password are Wrong.']], 200);
}
}
Here is the token output table data for different authentication
{
"id": "2a8526b24bd89a47f29474a86ba350c843cd4f7c5b0785c34d908efe00a4715c43502dbd9f789b83",
"user_id": "19",
"client_id": "15",
"name": "user;SuperAdmin",
"scopes": "[\"user\"]",
"revoked": "0",
"created_at": "2021-02-24 02:10:31",
"updated_at": "2021-02-24 02:10:31",
"expires_at": "2021-02-25 02:10:31"
},
{
"id": "388792d1c191529c65f1fb67d58972d2b26aae19d99c8df1c2321ec100bedff96a38b7724626f1cb",
"user_id": "53",
"client_id": "15",
"name": "Customer;1First.1Last#example.com",
"scopes": "[\"customer\"]",
"revoked": "0",
"created_at": "2021-02-24 02:10:28",
"updated_at": "2021-02-24 02:10:28",
"expires_at": "2021-02-25 02:10:28"
}
I did not include scopes. But take note if you want to restrict route access you should implement them.
For example if your table name is tbl_user then you should create model as below and add model path inside config/auth.php
Model:
<?php
namespace App\model\Users;
use Illuminate\Database\Eloquent\Model;
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class UserModel extends Authenticatable
{
use HasApiTokens, Notifiable;
protected $table = 'tbl_user';
public $timestamps = false;
}
config/auth.php
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\model\Users\UserModel::class,
],
],
Related
I'm doing something wrong?
My If (Auth::attempt($validated)) {} always fails when I change default user. But work with default user. Maybe i forgot any config in my new user 'personal'?
config/auth.php
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\Personal::class,
],
App/Models/Personal.php
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Personal extends Authenticatable
{
use HasFactory;
protected $table = 'personals';
protected $fillable = [
'name',
'email',
'password',
];
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
];
}
App\Http\Requests\AuthRequest.php
use Illuminate\Foundation\Http\FormRequest;
class AuthRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'email' => 'required|email',
'password' => 'required'
];
}
}
App\Http\Controller\AuthController.php
public function login(AuthRequest $request)
{
$validated = $request->validated();
if ($validated) {
\Log::info($validated); //<- so far it works
if (Auth::attempt($validated)) { //<- this if always fails with personal model, but it work with default user model
\Log::info('test');
$personal = Auth::user();
return redirect()->route('dashboard');
}
return redirect()->back()->withInput()->withErrors(['algo deu errado']);
}
return redirect()->back()->withInput()->withErrors(['Preencha todos os campos']);
}
$validated = [ 'email' => 'teste#outlook.com', 'password' => 'teste123', ];
I tried implementing Passport to my project and upon Login attempt, I cannot return the access token.
This is my User model
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasFactory, Notifiable, HasApiTokens;
/**
* The attributes that are mass assignable.
*
* #var array<int, string>
*/
And this is the Login controller, the login works, if i try to just return the authenticated user it works, but createToken() method is not recognized for some reason
<?php
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
class LoginController extends Controller
{
public function Login(Request $request){
$login = $request->validate([
'email' => 'required|string|max:255',
'password' => 'required|string|max:255',
]);
//Check wether the login credentials are valid
if( !Auth::attempt($login)){
return response(['message' => 'Invalid login credentials'], 401);
}
else{
return(Auth::user()->createToken());
}
}
}
And this is the config file for the auth.php, I added the api driver and provider as the documentation suggested
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
The reason behind this is that you cannot pass any value to the access token. Do the below code :
$token = auth()->user()->createToken('API Token')->accessToken;
return response(['user' => auth()->user(), 'token' => $token]);
Try out this.
public function Login(Request $request){
$login = $request->validate([
'email' => 'required|string|max:255',
'password' => 'required|string|max:255',
]);
//Check wether the login credentials are valid
if( !Auth::attempt($login)){
return response(['message' => 'Invalid login credentials'], 401);
}
else{
$token = Auth::user()->createToken('TutsForWeb')->accessToken
return $token;
}
}
refer this doc
https://laravel.com/docs/8.x/passport#managing-personal-access-tokens
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 have a custom guard named 'customers'.
Config/Auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
'customers' => [
'driver' => 'session',
'provider' => 'customers',
],
],
The login page of the default guard has a method to redirect the already logged in users of the that guard.
LoginController.php
public function __construct()
{
$this->middleware('guest')->except('logout');
}
But this redirects the logged in users of other guards too.
I have tried modifying the abouve method
public function __construct()
{
$this->middleware('guest:web')->except('logout');
}
But no results.
My customer model
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Notifications\CustomerResetPasswordNotification;
class Customer extends Authenticatable
{
use SoftDeletes;
use Notifiable;
protected $guard = 'customers';
use \Askedio\SoftCascade\Traits\SoftCascadeTrait;
protected $dates = ['deleted_at'];
protected $guarded = ['id'];
protected $softCascade = ['address'];
protected $hidden = ['password', 'remember_token'];
public function address()
{
return $this->hasMany(AddressBook::class);
}
public function ipaddress()
{
return $this->hasMany(IpAddress::class);
}
public function sendPasswordResetNotification($token)
{
$this->notify(new CustomerResetPasswordNotification($token));
}
public function coupons()
{
return $this->belongsToMany(Coupon::class);
}
}
Use this
public function __construct()
{
$this->middleware('auth:customer')->except('logout');
}
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