Hey I am having an issue on my prod website trying to log in with Laravel passport. It says my Lcobucci JWT Parser is not instantiable. It works for me locally but not on my remote.
How can I resolve this?
Error:
exception: "Illuminate\Contracts\Container\BindingResolutionException"
file: "/var/www/html/vendor/laravel/framework/src/Illuminate/Container/Container.php"
line: 1038
message: "Target [Lcobucci\JWT\Parser] is not instantiable while building [Laravel\Passport\PersonalAccessTokenFactory]."
trace: [,…]
Login Controller Method:
public function login(Request $request) {
$login = $request->validate([
'email' => 'required:string',
'password' => 'required:string'
]);
if(filter_var($request->email, FILTER_VALIDATE_EMAIL)) {
//user sent their email
Auth::attempt(['email' => $request->email, 'password' => $request->password]);
} else {
//they sent their username instead
Auth::attempt(['username' => $request->email, 'password' => $request->password]);
}
if(!Auth::check()) {
return response([
'status' => 'fail',
'message' => 'Invalid credentials'
]);
}
$accessToken = Auth::user()
->createToken('authToken')
->accessToken;
return response([
'status' => 'success',
'user' => new User_Resource(Auth::user()),
'access_token' => $accessToken
]);
}
I encountered the same issue as well, in your project composer.json add "lcobucci/jwt": "3.3.3" and execute composer update.
I found this solution on: https://github.com/laravel/passport/issues/1381.
Laravel : "8" and Lcobucci\JWT : "^3.4"
Solution:
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Token\Parser;
.
.
...
public function GetTokenId(Request $request)
{
// Get the Access_Token from the request
$Token = $request->bearerToken();
// Parse the Access_Token to get the claims from them the jti(Json Token Id)
$TokenId = (new Parser(new JoseEncoder()))->parse($token)->claims()
->all()['jti'];
return $tokenId;
}
if anyone still getting this error that's because $verifiedIdToken->getClaim('sub') has been deprecated in 3.4 and removed in 4.0.
use $verifiedIdToken->claims()->get('sub') this insted.
It keeps giving failed via my controller when trying to make the post request. I'm trying to make a file upload and storing that file name associated with the user into my db. I'm not sure what I'm doing wrong here, I've tried many ways to fix this but to no avail as I've hit a wall. I believe it may be the way my code's written in my controller but I'm not too sure.
The error I'm getting in the logs is Call to a member function photos() on null which means
auth()->user() is not detecting the authenticated user and there lies the problem which begs the question - how? I'm logged in using correct credentials without issues. How come I can't validate in a separate controller?
What am I doing wrong and how can I fix this?
Note: My React.js and Laravel code bases are separated.
Here's my react form submission:
handleSubmit(e) {
e.preventDefault();
console.log("here in submitHandler()");
let access_token = Cookies.get("access_token").slice(13, -8);
const headers = {
Authorization: `Bearer ${access_token}`
}
console.log(this.state.file_path);
axios.post('http://myendpoint/api/auth/dashboard', this.state.file_path, {headers})
.then(response => {
console.log(response);
}).catch(error => {
console.log(error);
})
};
Here's my FileUploadController.php:
public function uploadTest(Request $request) {
if(auth()->user()) {
auth()->user()->photos()->create([
'file_path' => $request->file('fileToUpload')->getClientOriginalExtension()
]);
return response()->json($request->session()->get("user"));
}
return "failed";
}
Here's my User model:
public function photos() {
return $this->hasMany(Photo::class);
}
Here's my Photo model:
public function user() {
return $this->belongsTo(User::class);
}
Here's my auth for creating user and logging in (AuthController.php):
public function signup(Request $request) {
$request->validate([
'name' => 'required|string',
'email' => 'required|string|email|unique:users',
'password' => 'required|string|confirmed'
]);
$user = new User([
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password)
]);
$user->save();
return response()->json([
'message' => 'Successfully created user!'
], 201);
}
public function login(Request $request) {
$request->validate([
'email' => 'required|string|email',
'password' => 'required|string',
'remember_me' => 'boolean'
]);
$credentials = request(['email', 'password']);
if(!Auth::attempt($credentials))
return response()->json([
'message' => 'Unauthorized'
], 401);
$user = $request->user();
$tokenResult = $user->createToken('Personal Access Token');
$token = $tokenResult->token;
if ($request->remember_me)
$token->expires_at = Carbon::now()->addWeeks(1);
$token->save();
$request->session()->put("user", $user);
return response()->json([
'access_token' => $tokenResult->accessToken,
'token_type' => 'Bearer',
'expires_at' => Carbon::parse(
$tokenResult->token->expires_at
)->toDateTimeString(),
$user
]);
}
I have three types of Authenticatable model and I need to have separate JWT authentication for each. Let me explain more about my issue.
I'm using MongoDB as my database and Laravel MongoDB is the package that I use.
User, Admin, and ServiceProvider are my models.
To having JWT auth in Laravel I use jwt-auth package. It's ok with user model (collection). when I want to use JWT with any of other models It not work and do everything with user again.
I search a lot an I found out that to change the provider user model I can use Config::set(); method like below,
Config::set('jwt.user', Admin::class);
Config::set('auth.providers.users.model', Admin::class);
But no effect on JWT auth. (I checked the value of 'jwt.user' and 'auth.providers.users.model' with Config::get() method and returned it, It has been changed to 'App\Admin').
Need to say, My codes are as simple as possible according to the documentation of the package.
Here is my UserController code:
class UserController extends Controller
{
public function login(Request $request)
{
$validator = Validator::make($request->all(), [
'email' => 'required|string|email|max:255',
'password' => 'required|min:6'
]);
if ($validator->fails()) {
return response()->json($validator->errors());
}
$credentials = $request->only('email', 'password');
try {
if (!$token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (JWTException $e) {
return response()->json(['error' => 'could_not_create_token'], 500);
}
$user = User::where('email', $request->email)->first();
return response()->json([
'user' => $user,
'token' => $token
]);
}
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'email' => 'required|string|email|max:255|unique:users',
'phone' => 'required|valid_phone|unique:users',
'password' => 'required|min:6',
'first_name' => 'required',
'last_name' => 'required',
]);
if ($validator->fails()) {
return response()->json($validator->errors());
}
User::create([
'phone' => $request->get('phone'),
'first_name' => $request->get('first_name'),
'last_name' => $request->get('last_name'),
'city_abbr' => $request->get('city_abbr'),
'email' => $request->get('email'),
'password' => bcrypt($request->get('password')),
]);
$user = User::first();
$token = JWTAuth::fromUser($user);
return response()->json([
'user' => $user,
'token' => $token
]);
}
}
And my AdminController:
class AdminController extends Controller
{
public function login(Request $request)
{
$validator = Validator::make($request->all(), [
'email' => 'required|string|email|max:255',
'password' => 'required|min:6'
]);
if ($validator->fails()) {
return response()->json($validator->errors());
}
$credentials = $request->only('email', 'password');
Config::set('jwt.user', Admin::class);
Config::set('auth.providers.users.model', Admin::class);
try {
if (!$token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (JWTException $e) {
return response()->json(['error' => 'could_not_create_token'], 500);
}
$admin = Admin::where('email', $request->email)->first();
return response()->json([
'admin' => $admin,
'token' => $token
]);
}
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'email' => 'required|string|email|max:255|unique:admins',
'phone' => 'required|valid_phone|unique:admins',
'password' => 'required|min:6',
'name' => 'required',
]);
if ($validator->fails()) {
return response()->json($validator->errors());
}
$admin = Admin::create([
'phone' => $request->get('phone'),
'name' => $request->get('name'),
'access' => $request->get('access'),
'email' => $request->get('email'),
'password' => bcrypt($request->get('password')),
]);
Config::set('jwt.user', Admin::class);
Config::set('auth.providers.users.model', Admin::class);
$token = JWTAuth::fromUser($admin);
return response()->json([
'admin' => $admin,
'token' => $token
]);
}
}
Am I wrong in somewhere?
Is there any solution for this?
Update:
To be sure about MongoDB functionality, I test all of above doings with a relational database, actually MySQL. Nothing changed!
JWTAuth generates token but when I run toUser method with any models except User, it returns null!
Any solution will be appreciated.
Here is what you must fo to add multi auth ability with JWT to my project.
In tymon JWT auth package. In JWTAuthServiceProvider, Change Tymon\JWTAuth\JWTAuth and Tymon\JWTAuth\Providers\User\UserInterface definition type from singleton to bind in bootBindings method.
Defined a new middleware and below code is its handle method:
public function handle($request, Closure $next){
if (!$request->header('Auth-Type')) {
return response()->json([
'success' => 0,
'result' => 'auth type could not found!'
]);
}
switch ($request->header('Auth-Type')) {
case 'user':
$auth_class = 'App\User';
break;
case 'admin':
$auth_class = 'App\Admin';
break;
case 'provider':
$auth_class = 'App\ServiceProvider';
break;
default:
$auth_class = 'App\User';
}
if (!Helpers::modifyJWTAuthUser($auth_class))
return response()->json([
'status' => 0,
'error' => 'provider not found!'
]);
return $next($request); }
Defined a function with name modifyJWTAuthUser in Helpers and here is its inner:
public static function modifyJWTAuthUser($user_class){
if (!$user_class ||
(
$user_class != 'App\User' &&
$user_class != 'App\Admin' &&
$user_class != 'App\ServiceProvider'
))
return false;
try {
Config::set('jwt.user', $user_class);
Config::set('auth.providers.users.model', $user_class);
app()->make('tymon.jwt.provider.user');
return true;
} catch (\Exception $e) {
return false;
} }
Introduced another $routeMiddleware like below in Kernel.php:
...
'modify.jwt.auth.user' => ChangeJWTAuthUser::class,
and the last step, Adding 'modify.jwt.auth.user' middleware to the routes that you want.
But even with this steps, You must have encountered a new issue. It was about getting the auth token by credentials in login and getting auth user from the token. (It seems that changing config value not effect on JWTAuth::attempt($credentials) and $this->auth->authenticate($token))
To solve the getting auth user from the token issue:
Create a new middleware CustomGetUserFromTokenwhich extends of Tymon'sjwt.authmiddleware, I meanGetUserFromTokenand in line 35, and **replace**$user = $this->auth->authenticate($token);with$user = JWTAuth::toUser($token);`
And to solve getting the auth token by credentials in login issue:
At first, Find the auth user and after that, check the user existence and valid the password with Hash::check() method, if these conditions return true, Generate a token from the user. Here is login code:
$admin = Admin::where('email', $request->email)->first();
if (!$admin || !Hash::check($request->get('password'), $admin->password)) {
return response()->json([
'success' => '0',
'error' => 'invalid_credentials'
], 401);
}
I'm not sure about this way but I think it's true until finding a correct way to do!
Conclusion:
Having multi JWT auth ability in Laravel perhaps have many other ways to do but I did like this and shared it to be helpful.
I think the only important point of this issue was app()->make('tymon.jwt.provider.user');, the ability to remake user provider after config values change.
Any other solutions will be appreciated.
You should use just one model (actually table) for authentication. When you save user and admin you can handle it. But when a user has request with jwt token, you cann't know which model will return (Admin or User)?
Use only User model for authentication and Admin model extends from User.
Redesign database like this:
users table : id, email, password, is_admin
user_details table : id, user_id, first_name, last_name, city_abbr, phone
admin_details table: id, user_id, name, phone
Put this your Admin Model for overriding all queries:
protected $table = "users";
public function newQuery()
{
return parent::newQuery()
->where("is_admin", true);
}
I tried lot to search about the problem. I couldn't find any solution. Please help me to understand what i am doing wrong.
I am attaching the code:
UserController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
use Illuminate\Support\Facades\Auth;
class UserController extends Controller
{
public function signup(Request $request){
$this->validate($request,[
'name' => 'required',
'email' => 'required|unique:users',
'password' => 'required'
]);
$user = new User([
'name' => $request->input('name'),
'email' => $request->input('email'),
'password' => bcrypt($request->input('password ')),
]);
$user->save();
return response()->json([
'state' => 'success',
'message' => 'User created.'
],201);
}
public function signin(Request $request){
$credentials = $request->only('email', 'password');
dd(Auth::attempt($credentials));
if (!$token = $this->guard()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
}
And i have routes in api.php
Route::prefix('user')->group(function () {
Route::post('signup', 'UserController#signup');
Route::post('signin', 'UserController#signin');
});
I have
I have this in database
I sent the below json to signup first, but then when i sent to signin i am getting failed.
{
"name":"ironman",
"email":"ironman#yahoo.com",
"password":"avengers"
}
This is a brand new installation of laravel 5.4 (same with 5.5), Using detailt User migration and model came with it.
When i tried to diagnose the problem myself, i found that the password_very is returning false all the time in Auth package.
I am using default password field, hashing it while creating users as other similar questions answered.
I am using php artisan serv.
I am using postman to send this request.
Please help,
This is pulling null from the request:
$request->input('password '); // notice the space
'password' => bcrypt($request->input('password ')),
You probably did not intend to put a space at the end of the input name:
$request->input('password'); // no space
'password' => bcrypt($request->input('password')),
i want return a response json after login. i try use:
public function login(Request $request){
if (Auth::attempt(['email' => $request->email, 'password' => $request->password])) {
return response()->json([
'data' => $request,
'success' => true,
]);
}
}
}
but always, the Auth::attempt, return false.
what am I doing wrong?
or, how i can change the code of the simple auth of lavarel lest redirect, just return a json response.
thanks.