I have to user models in my eloquent:
User
OfficeUser
OfficeUser is in defined in the JWT config as standard model.
Now I have written a Middleware for authenticate each of them
authUser:
public function handle($request, Closure $next)
{
Config::set('auth.providers.users.model', \App\User::class);
try {
if (! $user = JWTAuth::parseToken()->authenticate()) {
return response()->json(['user_not_found'], 404);
}
} catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
return response()->json(['token_expired'], $e->getStatusCode());
} catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
return response()->json(['token_invalid'], $e->getStatusCode());
} catch (Tymon\JWTAuth\Exceptions\JWTException $e) {
return response()->json(['token_absent'], $e->getStatusCode());
}
return $next($request);
}
authOfficeUser
public function handle($request, Closure $next)
{
try {
if (! $user = JWTAuth::parseToken()->authenticate()) {
return response()->json(['user_not_found'], 404);
}
} catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
return response()->json(['token_expired'], $e->getStatusCode());
} catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
return response()->json(['token_invalid'], $e->getStatusCode());
} catch (Tymon\JWTAuth\Exceptions\JWTException $e) {
return response()->json(['token_absent'], $e->getStatusCode());
}
return $next($request);
}
Additionally I have a login function for each of them:
LoginUser
if ($user){
if (Hash::check($request->password, $user->password)) {
// grab credentials from the request
$credentials = $request->only('email', 'password');
try {
// attempt to verify the credentials and create a token for the user
Config::set('auth.providers.users.model', \App\User::class);
if (! $token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (JWTException $e) {
// something went wrong whilst attempting to encode the token
return response()->json(['error' => 'could_not_create_token'], 500);
}
LoginOfficeUser
if ($user){
if (Hash::check($request->password, $user->password)) {
// grab credentials from the request
$credentials = $request->only('email', 'password');
try {
// attempt to verify the credentials and create a token for the user
Config::set('auth.providers.users.model', \App\OfficeUser::class);
if (! $token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (JWTException $e) {
// something went wrong whilst attempting to encode the token
return response()->json(['error' => 'could_not_create_token'], 500);
}
Unfortunately when I login and try to call a route behind the authUser Middleware I get an "user_not_found"
Does anybody have an idea why this happens?
OfficeUser authentication works fine
Posting for anyone who finds this questions
Although it's not recommended to have two user tables, but I had a similar requirement of setting up JWT with one of our clients. This is how I solved the issue.
No need to make any changes to the providers in `config/auth.php'
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
]
In your authentication controller, dynamically modify the model used by the providers by setting
\Config::set('auth.providers.users.model', \App\Trainer::class);
Example code
In authenticate() method
if ($credentials['user_type'] == 'consultant') {
\Config::set('auth.providers.users.model', \App\Trainer::class);
} else {
\Config::set('auth.providers.users.model', \App\User::class);
}
//Find the user
//Create the token
if ($user) {
$customClaims = ['user_type' => $credentials['user_type']];
$token = JWTAuth::fromUser($user,$customClaims);
} else {
return response()->json(['error' => 'invalid_credentials'], 401);
}
You will have to do the same while parsing the token to authenticate the user as well. Example code
In getAuthenticatedUser() method
$payload = JWTAuth::parseToken()->getPayload();
$user_type = $payload->get('user_type');
if($user_type === 'consultant'){
\Config::set('auth.providers.users.model', \App\Trainer::class);
}else{
\Config::set('auth.providers.users.model', \App\User::class);
}
if (!$user = JWTAuth::parseToken()->authenticate()) {
return response()->json(['user_not_found'], 404);
}
You can change the __construct function in each of your controllers as follows. So that jwt know which model to authenticate.
OfficeUserController
function __construct()
{
Config::set('jwt.user', OfficeUser::class);
Config::set('auth.providers', ['users' => [
'driver' => 'eloquent',
'model' => OfficeUser::class,
]]);
}
Related
I have a pretty complex Laravel application who creates a one to one polymorphic relationship in a middleware if it does not exists. The fact is that I am unable to retrieve the relationship in the same request (create in middleware, then pass it and retrieve it), I have an error 500 when I try to do this. BUT, if I a make an other request I can retrieve it... When I look into my database, I have the userable_id and type defined, I have no idea where this can probably occur.
My middleware look like this:
public function handle(Request $request, Closure $next): mixed
{
if (!Storage::disk('cecurity')->exists(config('coffre_fort.ssl_certificate.name')) ||
!config('coffre_fort.reverse_proxy.username') ||
!config('coffre_fort.reverse_proxy.password') ||
!config('coffre_fort.encryption_key')) {
return response()->json([
'error' => 'Cecurity is not configured correctly.',
], 500);
} elseif (!Auth::user()) {
return response()->json([
'error' => 'You are not authenticated.',
], 401);
}
if (!Auth::user()->cecurityAccount) {
try {
$userType = Auth::user()::class;
/** #phpstan-ignore-next-line */
if ($userType == "App\Models\Admin") {
$this->cecurityRepository->createAdminUser(Auth::user());
} else {
// It enter in this function for creating relationship
$this->cecurityRepository->createFullCustomer(Auth::user());
}
} catch (Exception $e) {
return response()->json([
'error' => $e->getMessage(),
], 500);
}
} elseif (!$this->cecurityRepository->checkConnection()) {
$this->cecurityRepository->connect(
Auth::user()->cecurityAccount->cecurity_user_id,
openssl_decrypt(
Auth::user()->cecurityAccount->password,
'AES-256-CBC',
config('coffre_fort.encryption_key'),
0,
(int) Auth::user()->cecurityAccount->encryption_iv
),
Auth::user()->cecurityAccount->coffre_id
);
}
return $next($request);
}
Then it creates the relationship as this (in the createFulCustomer() function):
$user = new Cecurity;
$user->userable_id = $customer->id_customer;
$user->userable_type = Customer::class;
And then pass the middleware to go to listFiles() function in a controller:
public function listFiles(ListFilesRequest $request): mixed
{
try {
return $this->cecurityRepository->listFiles(
$request->get('nbRowByPage'),
$request->get('pageIndex'),
);
} catch (\Exception $e) {
return response()->json([
'message' => $e->getMessage()
], 500);
}
}
Just after the middleware has passed, my database is completed (Cecurity table related):
I receive this error when I try to login with wrong credentials :
<!doctype html>
<html class="theme-light">
<!--
TypeError: Argument 2 passed to Symfony\Component\HttpFoundation\JsonResponse::__construct() must be of the type integer, array given, called in /home/stylmyvz/cars.styleshopeg.com/vendor/laravel/framework/src/Illuminate/Http/JsonResponse.php on line 31 in file /home/stylmyvz/cars.styleshopeg.com/vendor/symfony/http-foundation/JsonResponse.php on line 42
#0 /home/stylmyvz/cars.styleshopeg.com/vendor/laravel/framework/src/Illuminate/Http/JsonResponse.php(31): Symfony\Component\HttpFoundation\JsonResponse->__construct(Array, Array, Array)
#1 /home/stylmyvz/cars.styleshopeg.com/vendor/laravel/framework/src/Illuminate/Routing/ResponseFactory.php(99): Illuminate\Http\JsonResponse->__construct(Array, Array, Array, 0)
#2 /home/stylmyvz/cars.styleshopeg.com/app/Http/Controllers/APILoginController.php(31): Illuminate\Routing\ResponseFactory->json(Array, Array)
#3 [internal function]: App\Http\Controllers\APILoginController->login(Object(Illuminate\Http\Request)
I want to receive JSON response like this in Postman :
{
"error":"invalid username or password"
}
Here is Login Controller and I instruct it to give me "invalid username or password"
Can anyone tell me what is wrong with it
class APILoginController extends Controller
{
//
public function login(Request $request){
$validator = Validator::make($request -> all(),[
'email' => 'required|string|email|max:255',
'password' =>'required'
]);
if($validator->fails()){
return response()->json($validator->errors());
}
$credentials = $request->only('email', 'password');
try {
if(! $token = JWTAuth::attempt($credentials))
{
return response()->json(['error'=>'invalid username or password'], [401]);
}
} catch (JWTException $e) {
//throw $th;
return response()->json(['error'=>'could not create token'], [500]);
}
return response()->json(compact('token'));
}
}
Contents of api.php file:
Route::post('user/register','APIRegisterController#register');
Route::post('user/login','APILoginController#login');
Route::middleware('jwt.auth')->get('/users', function (Request $request) {
return auth()->user();
});
Route::middleware('jwt.auth')->group( function(){
Route::resource('/cars', 'API\CarController');
} );
I found the problem it was in the error message number I passed it as an array
return response()->json(['error'=>'invalid username or password'], [401]);
and I should remove the brackets and pass it like int
return response()->json(['error'=>'invalid username or password'], 401);
Please Change this try & catch both..
try {
if(! $token = JWTAuth::attempt($credentials))
{
return response()->json(['error'=>'invalid username or password'], 401);
}
} catch (JWTException $e) {
//throw $th;
return response()->json(['error'=>'could not create token'], 500);
}
This is how I do it in Laravel....
return Response::json(['message' => $value,'other_info'=>$vlue2],201);
Or using a helper function:
return response()->json(['message' => $value,'other_info'=>$vlue2], 201);
I am using jwt-auth for my Laravel 5.7 app. Currently, I'm allowing the client to enter email and password as user credentials.
However, I also want to let the client to enter their username in place of their email. So they have 2 choices: email or username.
How can I do that in my code?
My UserController#authenticate
public function authenticate(Request $request)
{
$credentials = $request->only('email', 'password');
try {
if (!$token = JWTAuth::attempt($credentials)) {
return response()->json([
'status' => 401,
'message' => 'invalid_credentials',
], 401);
}
} catch(JWTException $e) {
return response()->json([
'status' => 500,
'message' => 'token_creation_failed',
], 500);
}
return response()->json(compact('token'));
}
Thanks in advance
In your AuthController, add this to the login method;
public function login()
{
$loginField = request()->input('login');
$credentials = null;
if ($loginField !== null) {
$loginType = filter_var($loginField, FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
request()->merge([ $loginType => $loginField ]);
$credentials = request([ $loginType, 'password' ]);
} else {
return $this->response->errorBadRequest('What do you think you\'re doing?');
}
if (! $token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
This is how I handled mine where user can choose either email or phone to login.
public function login(Request $request)
{
//validate incoming request
$this->validate($request, [
'email_phone' => 'required|string',
'password' => 'required|string',
]);
try {
$login_type = filter_var( $request->email_phone, FILTER_VALIDATE_EMAIL ) ? 'email' : 'phone';
// return $login_type;
$credentials = [$login_type => $request->email_phone, 'password'=>$request->password];
if (! $token = Auth::attempt($credentials)) {
return response()->json($this->customResponse("failed", "Unauthorized"), 401);
}
return $this->respondWithToken($token);
} catch(JWTException $e) {
return response()->json($this->customResponse("failed", "An error occured, please contact support.", $user), 500);
}
}
I don’t used react-router, just simply used react with blade. Like
following:
articleManage.blade.php
#extends('layout')
#section('content')
<div id="articleManage"></div>
#stop
I try to use jwt authentication, and it works.
Here is my jwt auth middleware:
authJWT.php
try {
$token = JWTAuth::parseToken();
if (! $token->authenticate()) {
return response()->json(['user_not_found'], 404);
}
} catch (Exception $e) {
if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException){
return response()->json(['error'=>'Token is Invalid 1']);
}else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException){
return response()->json(['error'=>'Token is Expired 2']);
}else{
return response()->json(['error'=>'Something is wrong 3']);
}
}
return $next($request);
Now I am stuck with redirection on middleware, cause it won’t work when I change it like this:
try {
$token = JWTAuth::parseToken();
if (! $token->authenticate()) {
return response()->json(['user_not_found'], 404);
}
} catch (Exception $e) {
……
return redirect('/');
}
return $next($request);
And this is the error message I got:
TypeError: Cannot read property 'map' of undefined
at ArticleManage.render (app.js:54105)
at app.js:50033
at measureLifeCyclePerf (app.js:49313)
at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (app.js:50032)
at ReactCompositeComponentWrapper._renderValidatedComponent (app.js:50059)
at ReactCompositeComponentWrapper._updateRenderedComponent (app.js:49983)
at ReactCompositeComponentWrapper._performComponentUpdate (app.js:49961)
at ReactCompositeComponentWrapper.updateComponent (app.js:49882)
at ReactCompositeComponentWrapper.performUpdateIfNecessary (app.js:49798)
at Object.performUpdateIfNecessary (app.js:2916)
Any idea how to redirect when token error?
10/17: this is my program flow
articleManage.js
let url = 'load-article';
let config = {
headers: {'Authorization': 'Bearer '+localStorage.getItem('token')}
};
axios.get(url,config).then(response=>{
this.setState({'articles':response.data.articles});
}).catch(function (error) {
console.log(error);
});
web.php
Route::get('/load-article', 'API\ArticleController#read')->middleware('jwt-auth');
ArticleController#read
public function read(Request $request){
$header = $request->header('Authorization');
$articles = Article::All();
return response()->json(['articles' => $articles]);
}
now I change middleware, cause I want to try redirection, then I get error.
public function handle($request, Closure $next)
{
// try {
// $token = JWTAuth::parseToken();
// if (! $token->authenticate()) {
// return response()->json(['user_not_found'], 404);
// }
// } catch (Exception $e) {
// if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException){
// return response()->json(['error'=>'Token is Invalid 1']);
// }else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException){
// return response()->json(['error'=>'Token is Expired 2']);
// }else{
// return response()->json(['error'=>'Something is wrong 3']);
// }
// }
// return $next($request);
return redirect('/');
}
Shot in the dark here, since you didn't post the whole articleManager component. But, make sure you're passing some sort of failure status code header on all error responses. Otherwise, Axios will read response()->json(['error'=>'Token is Invalid 1']) as a success and attempt to set state. Since response.data.articles is undefined, that's why you're getting the error. So change your middleware in authJWT.php to something like:
} catch (Exception $e) {
if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException) {
return response()->json(['error'=>'Token is Invalid 1'], 500);
} else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException) {
return response()->json(['error'=>'Token is Expired 2'], 500);
} else {
return response()->json(['error'=>'Something is wrong 3'], 500);
}
}
Is it possible with https://github.com/tymondesigns/jwt-auth
to get the current user? Because right now I can only generate a token (when a user sign in).
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
try {
if (! $token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (Tymon\JWTAuth\Exceptions\JWTException $e) {
return response()->json(['error' => 'could_not_create_token'], 500);
}
return response()->json(compact('token'));
}
You don't need Auth::user();
You can use the toUser method of JWTAuth. Just pass the token as parameter and you will get back the user info:
$user = JWTAuth::toUser($token);
return response()->json(compact('token', 'user'));
For more info, this is the toUser method:
/**
* Find a user using the user identifier in the subject claim.
*
* #param bool|string $token
*
* #return mixed
*/
public function toUser($token = false)
{
$payload = $this->getPayload($token);
if (! $user = $this->user->getBy($this->identifier, $payload['sub'])) {
return false;
}
return $user;
}
You can get logged user data.
$credentials = $request->only('code', 'password', 'mobile');
try {
// verify the credentials and create a token for the user
if (! $token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (JWTException $e) {
// something went wrong
return response()->json(['error' => 'could_not_create_token'], 500);
}
$currentUser = Auth::user();
print_r($currentUser);exit;
You can also use JWTAuth::user() method.
The user() method call is returned in the toUser() method, which itself is an alias for authenticate() method which authenticates a user via a token. If the user is already authenticated, there is no need to authenticate them again (which toUser() does), instead user() method can be used to get the authenticated user.
// $token = JWTAuth::attempt($credentials) was successful...
$user = JWTAuth::user();
return response()->json(compact('token', 'user'));
It works fine for me (laravel 5.7)
U must post token to me function and will return user
use Tymon\JWTAuth\Facades\JWTAuth;
public function me(Request $request)
{
$user = JWTAuth::user();
if (count((array)$user) > 0) {
return response()->json(['status' => 'success', 'user' => $user]);
} else {
return response()->json(['status' => 'fail'], 401);
}
}
try this (it works fine with laravel 5.6,5.7):
use Tymon\JWTAuth\Facades\JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
// code to generate token for user with email testuser#gmail.com
$user=User::where('email','=','testuser#gmail.com')->first();
if (!$userToken=JWTAuth::fromUser($user)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
return response()->json(compact('userToken'));
just need to return inside the specified route middleware
The route that you defined api route
Route::group(['middleware' => 'jwt.auth'], function () {
Route::post('/user', function (Request $request) {
try {
$user = \Illuminate\Support\Facades\Auth::user();
return $user;
} catch (\Tymon\JWTAuth\Exceptions\UserNotDefinedException $e) {
return '$user';
}
});
});
You can get the current user related to token using :
$user = JWTAuth::setToken($token)->toUser();
In Laravel 7.2 if none of the above works use like this to retrive the user:
$credentials = request(['email', 'password']);
if (! $token = auth('api')->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
$user = auth('api')->user();
dd($user);
Solution for who are all struggle validate the user and token and then guard
Problems that i face
Jwt does not return any user even i put currect token
return null
$user = JWTAuth::user(); //getting null
return false
$user = JWTAuth::parseToken()->authenticate();
return false
auth()->user()
Solution to check
Before Going to solution middleware and your route guard is matter so keep in mind
Guard setting config/auth.php
'guards' => [
'website_admin' => [
'driver' => 'jwt',
'provider' => 'website_admin',
],
]
Provider
'providers' => [
'super_admin' => [
'driver' => 'eloquent',
'model' => App\Website_super_admin_auth::class,
],
]
Middleware (must)
<?php
namespace App\Http\Middleware;
use Closure;
use JWTAuth;
use Exception;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
class JwtMiddleware extends BaseMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
try {
$user = JWTAuth::parseToken()->authenticate();
} catch (Exception $e) {
if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException){
return response()->json(['status' => 'Token is Invalid']);
}else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException){
return response()->json(['status' => 'Token is Expired']);
}else{
return response()->json(['status' => 'Authorization Token not found']);
}
}
return $next($request);
}
}
Route(should specify middleware with guard)
Route::group(['prefix' => 'super_ad', 'middleware' => ['jwt.verify','auth:super_admin']], function()
{
Route::get('/test', function (){
// $user = JWTAuth::parseToken()->authenticate(); <-------check user here
$user=auth()->user();
// return $user;
});
})
$user = JWTAuth::setToken($token)->toUser();
Auth::login($user, $remember = true);
public function user(Request $request){
/// get user details from token
$user = JWTAuth::toUser($this->bearerToken($request));
// get payloads in the token
$payload = JWTAuth::getPayload($this->bearerToken($request));
}
public function bearerToken($request)
{
$header = $request->header('Authorization', '');
if (Str::startsWith($header, 'Bearer ')) {
return Str::substr($header, 7);
}
}