I was able to login and get user token using JWT in laravel.However, while tring to get authenticated user (getAuthUser) by passing that token, I get following error:
"SQLSTATE[42S22]: Column not found: 1054 Unknown column '' in 'where
clause' (SQL: select * from user where `` = 12 limit 1)"
AuthenticationController:
<?php
namespace Modules\Authentication\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use App\Http\Requests;
use JWTAuth;
use JWTAuthException;
use Modules\Settings\Entities\Users;
use Modules\Authentication\Http\Requests\Authentication;
class AuthenticationController extends Controller
{
public function __construct()
{
// $this->user = new Users;
$this->guard = \Auth::guard('api');
}
public function login(Authentication $request){
$credentials = $request->only('username', 'password');
try {
// verify the credentials and create a token for the user
$token = JWTAuth::attempt($credentials);
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);
}
// if no errors are encountered we can return a JWT
return response()->json(compact('token'));
}
public function getAuthUser(Request $request){
$user = JWTAuth::user($request->token);
// dd($user);
return response()->json(['result' => $user]);
}
}
Users Model:
namespace Modules\Settings\Entities;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class Users extends Model implements JWTSubject,Authenticatable{
Protected $table="user";
// protected $primaryKey = 'id';
protected $fillable = ['id','username','password','user_status_type_id','client_id','created_userid'];
protected $hidden = [
'password', 'remember_token',
];
public function user_status(){
return $this->belongsTo('Modules\Settings\Entities\UserStatusType','user_status_type_id');
}
public function user_client(){
return $this->belongsTo('Modules\Settings\Entities\Client','client_id');
}
public function role()
{
return $this->belongsToMany('Modules\Settings\Entities\Role','user_role','user_id','role_type_id');
}
public function getAuthPassword() {
return $this->password;
}
public function getJWTIdentifier() {
return $this->getKey();
}
public function getJWTCustomClaims()
{
return [];
}
public function getAuthIdentifierName(){}
public function getAuthIdentifier(){}
// public function getAuthPassword(){}
public function getRememberToken(){}
public function setRememberToken($value){}
public function getRememberTokenName(){}
}
Route:
Route::group(['middleware' => 'web', 'prefix' => 'api/v1/authentication', 'namespace' => 'Modules\Authentication\Http\Controllers'], function(){
Route::post('auth/login', 'AuthenticationController#login');
// Route::group(['middleware' => 'jwt.auth'], function () {
Route::get('user', 'AuthenticationController#getAuthUser');
// });
});
I am testing it in postman by
GET: ..../api/v1/authentication/user?token={Token}
EDIT:
Now my method for getAuthUser in the controller looks like this:
public function getAuthUser(Request $request){
// $token = JWTAuth::getToken();
// dd($token);
$input = $request->all();
JWTAuth::setToken($input['token']);
// dd($input['token']);
$user = JWTAuth::toUser($input['token']);
// dd($user);
return response()->json(['result' => $user]);
}
and In JWTAuth.php
public function authenticate()
{
// dd($this->getPayload()->get('sub'));
$id = $this->getPayload()->get('sub');
// dd($id);
// dd($this->auth->byId($id));
if (! $this->auth->byId($id)) {
return false;
}
return $this->user();
}
here by doing dd($id), value of id comes but if I try to do dd($this->auth->byId($id)) I get the same error as before.
Try this to retrieve the user instead:
public function getAuthUser()
{
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());
}
// the token is valid and we have found the user via the sub claim
return response()->json(['result' => $user]);
}
Taken from docs, Retreiving the Authenticated user from a token
EDIT:
May not make a difference but just looking at your Model again and I would say that it needs to be
class Users extends Authenticatable implements JWTSubject
rather than
class Users extends Model implements JWTSubject,Authenticatable
Related
The LoginController had this code before:
class LoginController extends Controller
{
public function showLoginForm(){
$roles = Role::all();
return view('auth.login', compact('roles'));
}
public function logout(Request $request){
Auth::logout();
$request->session()->flush();
return redirect('/');
}
public function login()
{
$credentials = $this->validate(request(),[
'email' => 'email|required|string',
'password' => 'required|string',
]);
if (Auth::attempt ($credentials)){//auth attemptdevuelve verdadero o falso en caso de que las credenciales correspondan o no
//Inician cambios RDAN
$user = Auth::user();
if($user->userRole() == 'admin') {
return redirect('main');
} else if($user->userRole() == 'externo') {
return redirect('es/user_form');
} else if($user->userRole() == 'profesor') {
return redirect('profesor_site');
} else if($user->userRole() == 'registrador') {
return redirect('select_lang');
} else {
return back()->withErrors(['email' => 'Incorrect user permissions'])
->withInput(request(['email']));
}
//Terminan cambios RDAN
}else{
return back()->withErrors(['email' => 'Incorrect user permissions'])
->withInput(request(['email'])); }
}
}
Then I changed it for:
class LoginController extends Controller
{
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo = '/main';//RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
}
In the model User I have this for the roles:
public function roles()
{
return $this->belongsToMany(Role::class,'assigned_roles');
}
public function isAdmin(){
return $this->hasRoles(['admin']);
}
public function hasRoles(array $roles)
{
return $this->roles->contains(fn($role, $key) => in_array($role->name, $roles));
}
public function userRole(){
return $this->role->nombre_rol;
}
With the new changes on the LoginController I have no problem with the login, but obviously only redirect to the main view. So, I want to add the redirect view depend on the role, but when I add the public function login() that it had before it returns an error with the function userRole() on the model User. The error is
ErrorException
Trying to get property 'nombre_rol' of non-object
http://localhost/Servescol2.0.2/public/login
You don't need to override the login method. The login method currently will make a call to the authenticated method to see if it returns true truthy value and then return that as the response. You can override the authenticated method and have it return your redirect based on your conditions.
protected function authenticated(Request $request, $user)
{
if ($user->hasRoles(['admin'])) {
return redirect('main');
}
...
}
The error is caused by trying to access an attribute/relationship which doesn't exist, which returns null:
$this->role
The User belongs to many Role so there are multiple roles not a single one.
You could have a method to check for a single role:
public function hasRole($role)
{
return $this->roles->contains('name', $role);
}
Then you could adjust your conditionals:
if ($user->hasRole('admin')) {
Or use the hasRoles method instead:
if ($user->hasRoles(['admin']) {
I am Having this error in my code
Undefined function 'App\Http\Controllers\api\createToken'.intelephense(1010)
i have done all the necessary imports needed to use passport so that i can generate apiTokens but it does not work for me
i will really appreciate it if i can get some one to help me in fixing this error thanks in advance
<?php
namespace App\Http\Controllers\api;
use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class authController extends Controller
{
public function signup(Request $request)
{
//
$rules=[
'name'=>'required|max:55',
'email'=>'required|email|unique:users',
'password'=>'required',
// 'password_confirm'=>'required',
];
// $valid = $request->validate($rules);
$valid = Validator::make($request->all(),$rules);
if($valid->fails()){
return response()->json(
$valid->errors(),400
);
}else{
$user = User::create($request->all());
$accessToken = $user-createToken('authToken')->accessToken;
return response()->json(['user'=>$user,'accessToken'=>$accessToken], 201);
}
}
public function login()
{
//
}
public function logout(Request $request)
{
//
}
public function user(Request $request)
{
$request->user()->token()->revoke(); return response()->json([
'message' => 'Successfully logged out'
]);
}
public function edit($id)
{
//
}
public function update(Request $request, $id)
{
//
}
public function destroy($id)
{
//
}
}
Sorry Guys it was a typo in the code
$user-createToken('authToken')->accessToken;
i left out the '>' character
$user->createToken('authToken')->accessToken;
Laravel version : 7.9.2
PHP version: 7.4
I have code for login with Facebook and Google. It's working absolutely fine on localhost but on live server it doesn't work. Surprising thing is It neither returns any error nor throw any exception.
It simply redirect user back to login page and my URL shows the string #=.
This is live link
https://beta.car-chain.net/login
I need suggestion.
LoginController.php
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite;
use App\User;
use Exception;
class LoginController extends Controller
{
use AuthenticatesUsers;
protected $redirectTo = RouteServiceProvider::HOME;
public function __construct()
{
$this->middleware('guest')->except('logout');
}
protected $providers = [
'github','facebook','google','twitter'
];
public function show()
{
return view('auth.login');
}
public function redirectToProvider($driver)
{
if( ! $this->isProviderAllowed($driver) ) {
return $this->sendFailedResponse("{$driver} is not currently supported");
}
try {
return Socialite::driver($driver)->redirect();
} catch (Exception $e) {
// You should show something simple fail message
return $this->sendFailedResponse($e->getMessage());
}
}
public function handleProviderCallback( $driver )
{
try {
$user = Socialite::driver($driver)->user();
} catch (Exception $e) {
return $this->sendFailedResponse($e->getMessage());
}
// check for email in returned user
return empty( $user->email )
? $this->sendFailedResponse("No email id returned from {$driver} provider.")
: $this->loginOrCreateAccount($user, $driver);
}
protected function sendSuccessResponse()
{
return redirect()->intended('home');
}
protected function sendFailedResponse($msg = null)
{
return redirect()->route('social.login')
->withErrors(['msg' => $msg ?: 'Unable to login, try with another provider to login.']);
}
protected function loginOrCreateAccount($providerUser, $driver)
{
// check for already has account
$user = User::where('email', $providerUser->getEmail())->first();
// if user already found
if( $user ) {
// update the avatar and provider that might have changed
$user->update([
'provider' => $driver,
'provider_id' => $providerUser->id,
'access_token' => $providerUser->token
]);
} else {
if($providerUser->getEmail()){ //Check email exists or not. If exists create a new user
$user = User::create([
'name' => $providerUser->getName(),
'email' => $providerUser->getEmail(),
'provider' => $driver,
'provider_id' => $providerUser->getId(),
'access_token' => $providerUser->token,
'password' => '' // user can use reset password to create a password
]);
}else{
//Show message here what you want to show
}
}
// login the user
Auth::login($user, true);
return $this->sendSuccessResponse();
}
private function isProviderAllowed($driver)
{
return in_array($driver, $this->providers) && config()->has("services.{$driver}");
}
}
Route.php
<?php
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', 'HomeController#index')->name('home');
Route::get('auth/social', 'Auth\LoginController#show')->name('social.login');
Route::get('oauth/{driver}', 'Auth\LoginController#redirectToProvider')->name('social.oauth');
Route::get('oauth/{driver}/callback', 'Auth\LoginController#handleProviderCallback')->name('social.callback');
So i created a controller for authentication with 2 methods (token() / native)_). Im using fractal transformer to return response. The token method works fine for me, but the loginAndroid() returns
"Call to a member function createData() on null" error.
Any help? Thank you.
class AuthController extends RestController
{
protected $transformer = UserTransformers::Class;
public function __construct()
{
$this->middleware('auth:api', ['except' => ['login', 'loginAndroid']]);
}
public function login(Request $request)
{
$credentials = $request->only(['username', 'password']);
if (!$token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
public function loginAndroid(Request $request)
{
$credentials = $request->only(['username', 'password']);
if (Auth::attempt($credentials)) {
//$user = Auth::user()->with(['employees']);
$userdata = User::with(['employees', 'employees.role', 'employees.branch'])->find(Auth::id());
//$success['token'] = $user->createToken('MyApp')->accessToken;
//return response()->json($userdata, 200);
//return $userdata;
$response = $this->generateItem($userdata);
return $this->sendResponse($response, 201);
} else {
return response()->json('gagal', 401);
}
}
}
this is my restcontroller
abstract class RestController extends Controller
{
protected $manager;
protected $transformer;
public function __construct()
{
$this->manager = new Manager();
}
protected function generateItem($model, $transformer = null)
{
if (!is_null($transformer)) {
return new Item($model, new $transformer);
}
return new Item($model, new $this->transformer);
}
protected function generateCollection($model, $transformer = null)
{
if (!is_null($transformer)) {
return new Collection($model, new $transformer);
}
return new Collection($model, new $this->transformer);
}
protected function sendResponse(ResourceInterface $data, $status = 200)
{
return response()->json(
$this->manager->createData($data)->toArray(),
$status
);
}
protected function sendNotFoundResponse($status)
{
return response()->json($status, 404);
}
protected function sendIseResponse($status)
{
return response()->json($status, 500);
}
}
It looks like your sendResponse() method depends on $this->manager. However, $this->manager gets set in RestController::__construct() and you've overridden the __construct() method in your AuthController::__construct(). So, in order to have $this->manager available, you should call the parent constructor from your AuthController, like this:
class AuthController extends RestController
{
protected $transformer = UserTransformers::Class;
public function __construct()
{
parent::__construct(); // call the parent constructor where
// $this->manager gets initialized
$this->middleware('auth:api', ['except' => ['login', 'loginAndroid']]);
}
... etc
I know I'll lose the purpose if I store jwt token in my database but for some reason, i want to store it, how can I do that?
Controller
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Tymon\JWTAuth\JWTAuth;
class AuthController extends Controller
{
/**
* #var \Tymon\JWTAuth\JWTAuth
*/
protected $jwt;
public function __construct(JWTAuth $jwt)
{
$this->jwt = $jwt;
}
public function postLogin(Request $request)
{
$this->validate($request, [
'email' => 'required|email|max:255',
'password' => 'required',
]);
try {
if (! $token = $this->jwt->attempt($request->only('email', 'password'))) {
$create_token = User::where('user_id', $login->user_id)->update(['token' => $token]);
return response()->json(['user_not_found'], 404);
}
else {
$create_token = User::where('user_id', auth()->user()->user_id)->update(['token' => $token]);
}
} catch (\Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
return response()->json(['token_expired'], 500);
} catch (\Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
return response()->json(['token_invalid'], 500);
} catch (\Tymon\JWTAuth\Exceptions\JWTException $e) {
return response()->json(['token_absent' => $e->getMessage()], 500);
}
return response()->json(compact('token'));
}
public function logout(Request $request)
{
$this->jwt->invalidate($this->jwt->getToken());
return response()->json([
'message' => 'User logged off successfully!'
], 200);
}
}
I tried the method above but i got error saying Call to undefined function App\Http\Controllers\auth()
can anyone help me??
Not advised seeing that json web tokens are time sensitive but I did write something similar but for the use of redis. Looking at your code I would advise you create row for tokens and add it to the public $hidden = ['password','token']. When a user is created, you create and save a token to the database.