Cannot create access token with Laravel passport? - php

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

Related

Why Method App\Http\Controllers\RegisterController::guard does not exist and MemberExtra table not fill with data in laravel

thanks for helping
I have some error when insert data i nto database, that is guard does not exit when I try to register by using this controller
namespace App\Http\Controllers;
use App\Models\MemberExtra;
use App\Models\User;
use App\Http\Controllers\Controller;
use Carbon\Carbon;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Http\Request;
use Illuminate\Auth\Events\Registered;
class RegisterController extends Controller
{
public function __construct()
{
$this->middleware('guest');
}
public function index(){
return view('users.register');
}
protected function validator(array $data)
{
return Validator::make($data, [
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6',
'referrer_id' => 'required',
'position' => 'required',
'username' => 'required',
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return \App\User
*/
protected function create(array $data)
{
$ref_id = $data['referrer_id'];
$poss = $data['position'];
$posid = getLastChildOfLR($ref_id,$poss);
return User::create([
'email' => $data['email'],
'password' => bcrypt($data['password']),
'referrer_id' => $data['referrer_id'],
'position' => $data['position'],
'username' => $data['username'],
'join_date' => Carbon::today(),
'posid' => $posid
]);
}
public function register(Request $request)
{
$this->validator($request->all())->validate();
event(new Registered($user = $this->create($request->all())));
$this->guard()->login($user);
MemberExtra::create([
'user_id' => $user['id'],
'left_paid' => 0,
'right_paid' => 0,
'left_free' => 0,
'right_free' => 0,
'left_bv' => 0,
'right_bv' => 0,
]);
updateMemberBelow($user['id'], 'FREE');
return $this->registered($request, $user) ?: redirect()->route('home');
}
}
when register , show this error
and I refresh the page , user data is inserted into database but MemberExtra data still not insert
And this guard does not exist error happen again and again whenever register.
How can fix this error
Can someone help me
In your controller I didn't see the RegistersUsers trait which have guard function.
You have missed to write below in your controller. By default this function exist in RegistersUsers this trait
/**
* Get the guard to be used during registration.
*
* #return \Illuminate\Contracts\Auth\StatefulGuard
*/
protected function guard()
{
return Auth::guard('your guard name');
}

User does not stay logged in after Auth:validate was true

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

Auth::attempt always return false even with proper input

Here are the facades I used
namespace App\Http\Controllers;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
I've successfully created user signup page with hashed password using bcrypt.
//Get singnup view
public function getSignup()
{
return view('user.signup');
}
//Process signup
public function postSignup(Request $request)
{
$this->validate($request, [
'email' => 'email|required|unique:users',
'password' => 'required|min:4'
]);
$user = new User([
'email' => $request->input('email'),
'password' => bcrypt($request->input('password')),
]);
$user->save();
return redirect()->route('product.index');
}
And now I'm stuck at the signin page. The Auth::attempt always return false. I even tried to store a plain password in my database and signin without bcrypt but it still returned false. I have no idea where I'm wrong right now.
//Get signin view
public function getSignin()
{
return view('user.signin');
}
//Process signin
public function postSignin(Request $request)
{
$this->validate($request, [
'email' => 'email|required',
'password' => 'required|min:4'
]);
$credentials = array(
'email' => $request->input('email'),
'password' => bcrypt($request->input('password'))
);
if(Auth::attempt($credentials))
{
return redirect()->route('user.profile');
}
return redirect()->route('product.index');
}
You don't need bcrypt() in Auth::attempt(). Remove it and try again.
In config\auth, change guard driver setting is set to api.
'defaults' => [
'guards' => 'api',
'passwords' => 'users'
]
But Laravel doesn't support attempt() function with guard api. Thus, you should use some packages like Passport (You can reference here)
Or simplier, just configure you guard driver with Auth::guard('api')->attempt($credentials)
Hope this solve your problem.

Laravel passport for different table than 'users'

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,
],
],

Laravel 5.6 authentication with JWT and ADLDAP

I have both my ldap server set up (with adldap2/adldap2-laravel) and my JWT set up (with tymon/jwt-auth) for a SPA built with Vue/Vuetify and Laravel api backend. The JWT is all set up to the point where if I leave my provider as eloquent, I can get a successful login attempt with my eloquent users:
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
]
],
As soon as I change the driver to adldap and attempt a username/password that is known to be valid in our ldap system, I am stuck on an unauthorized error. Does anyone have any advice or resources to marry these two? I know that there are a lot of differences with laravel/passport sessions and JWT, but I'm not seeing a simple solution. Here is my AuthController:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class AuthController extends Controller
{
use AuthenticatesUsers;
/**
* Create a new AuthController instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('jwt', ['except' => ['login']]);
}
public function login()
{
$credentials = request(['username', 'password']);
if (! $token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
public function me()
{
return response()->json(auth()->user());
}
public function logout()
{
auth()->logout();
return response()->json(['message' => 'Successfully logged out']);
}
public function refresh()
{
return $this->respondWithToken(auth()->refresh());
}
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60,
'user' => auth()->user()->name
]);
}
}

Categories