JSON errors of LoginController and RegisterController should be strings not arrays - laravel

I am using Laravel 5.5.14 (php artisan --version). I am using Laravel to create just a REST API.
When I get errors, the shape of it looks like this:
[
'message' => 'The given data was invalid.',
'errors' => [
'email' => ['The email field is required.'],
'password' => ['The password field is required.']
]
]
My
We see inside the errors array, each field (email, password) instead of being strings, is an array of a single string. Can there ever be more then one error is this why it is an array? Even if there is more then one error, I wanted to tell Laravel just report the first error without an array, is this possible?
In my api.php I have this:
Route::post('login', 'Auth\LoginController#login');
Route::post('logout', 'Auth\LoginController#logout');
Route::post('register', 'Auth\RegisterController#register');
My LoginController.php looks like this:
public function __construct()
{
$this->middleware('guest')->except('logout');
}
public function login(Request $request)
{
$this->validateLogin($request);
if ($this->attemptLogin($request)) {
$user = $this->guard()->user()->load('pets');
$user->generateToken();
return $user;
}
return $this->sendFailedLoginResponse($request);
}
public function logout(Request $request)
{
$user = Auth::guard('api')->user();
if ($user) {
$user->api_token = null;
$user->save();
}
return ['ok' => true];
}
And my RegisterController.php looks like this:
public function __construct()
{
$this->middleware('guest');
}
/**
* The user has been registered.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function registered(Request $request, $user)
{
$user->generateToken();
return response()->json($user, 201);
}
/**
* Get a validator for an incoming registration request.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return \App\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}

Override validateLogin to the following or you could give it a different name and call it from your controller function-
public function validateLogin($request){
$rules = [
'user_name' => 'required|string',
'password' => 'required|string',
];
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
$errorResponse = $this->validationErrorsToString($validator->errors());
return response()->json(['message' => 'The given data was invalid.', 'errors' => $errorResponse],400);
}
}
And make another function to transform your response to expected format with this method-
private function validationErrorsToString($errArray) {
$valArr = array();
foreach ($errArray->toArray() as $key => $value) {
$valArr[$key] = $value[0];
}
return $valArr;
}
And don't forget to add this at the start of your controller file-
use Validator;
Hope this helps you :)

From the docs:
The $errors variable will be an instance of Illuminate\Support\MessageBag
An example of displaying errors from the docs:
#if ($errors->any())
<div class="alert alert-danger">
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
</div>
#endif

Related

Return to a View from Controller after an Api call - Laravel 8

So guys,
I have an app that needs to login.
After login and getting the API and token, it has to redirect to a dashboard, but unfortunately, I can't make it to a dashboard view.
I try to find answers on the forum but can't find one that suits my code.
Here is my api.php
Route::post('/login', App\Http\Controllers\api\LoginController::class)->name('login');
my web.php
Route::get('/dashboard', [Controller::class, 'dashboard']);
my LoginController
class LoginController extends Controller
{
/**
* Handle the incoming request.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function __invoke(Request $request)
{
//set validation
$validator = Validator::make($request->all(), [
'email' => 'required',
'password' => 'required'
]);
//if validation fails
if ($validator->fails()) {
return response()->json($validator->errors(), 422);
}
//get credentials from request
$credentials = $request->only('email', 'password');
//if auth failed
if(!$token = auth()->guard('api')->attempt($credentials)) {
return response()->json([
'success' => false,
'message' => 'Email atau Password Anda salah'
], 401);
}
//if auth success
return response()->json([
'success' => true,
'user' => auth()->guard('api')->user(),
'token' => $token
], 200);
}
my AuthController :
class AuthController extends Controller
{
public function login(Request $request){
$email = $request->input("email");
$password = $request->input("password");
$request = Request::create('http://localhost:8000/api/login', 'POST',[
'name'=>$email,
'password'=>$password,
]);
$response = json_decode(Route::dispatch($request)->getContent());
// echo($response->success);
if($response->success == 1 || true){
return redirect()->route('dashboard',["response"=>$response]);
}else{
return redirect()->back();
}
}
}
Controller.php where dashboard route is defined:
public function dashboard()
{
return view('dashboard', [
"title" => "Dashboard",
]);
}
if I'm using this code, the error I get is:
Route [dashboard] not defined.
but if I'm not using return redirect and use return view instead. I can go to my dashboard, but the URL is localhost:8000\auth\login which is not what I want.
is there any suggestion so I can get my view on Dashboard?
Thank you very much.

Auth::user() is null after successful Passport authentication

I am using password for user Authentication, and I after every authentication I assign a secure cookie that stores the passport token. A am being able to successfully authenticate using the Auth::attempt() method, but the Auth::user() is null. Even in the same controller, on the logout() method the user is undefined and I can't even Auth::logout().
Login method:
public function login(Request $request)
{
$request->validate([
'email' => 'required|string|email',
'password' => 'required|string',
]);
$credentials = \request(['email', 'password']);
if (Auth::attempt($credentials)) {
$user = Auth::user();
$token = $user->createToken('Personal Access Token')->accessToken;
$cookie = $this->getSessionCookie($token);
return response()
->json([
'user' => $user,
'token' => $token,
], 200)
->cookie(
$cookie['name'],
$cookie['value'],
$cookie['minutes'],
$cookie['path'],
$cookie['domain'],
$cookie['secure'],
$cookie['httponly'],
$cookie['samesite']
);
} else {
return response()->json([
'error' => 'Invalid Credentials',
], 422);
}
}
Logout method:
public function logout(Request $request)
{
$request->user()->token()->each(function ($token, $key) {
$token->delete();
});
$cookie = \Cookie::forget('auth_token');
Auth::logout();
return response()->json([
'message' => 'Logged out successfully.'
], 200)->withCookie($cookie);
}
Here the Auth::logout() produces Method Illuminate\Auth\RequestGuard::logout does not exist. Otherwise the logout is successful.
My API routes:
Route::group(['prefix' => 'v1'], function() {
// Authentication
Route::post('/login', 'AuthController#login');
Route::post('/register', 'AuthController#register');
Route::post('/password/reset', 'AuthController#sendPasswordResetLink');
Route::post('/password/update', 'AuthController#callResetPassword');
// Articles
Route::get('/articles', 'ArticleController#index');
Route::middleware(['auth.header', 'auth:api'])->group(function () {
// Get Logged in User
Route::get('/user', function (Request $request) {
return $request->user(); // returns the actual logged in user
});
// Articles
Route::post('/articles', 'ArticleController#store');
Route::get('/articles/{id}', 'ArticleController#show');
Route::put('/articles/{id}', 'ArticleController#update');
Route::delete('/articles/{id}', 'ArticleController#destroy');
// Log Out
Route::post('/logout', 'AuthController#logout');
});
});
Example controller where Auth::user() is null:
class ArticleController extends Controller
{
public function index(Request $request)
{
$user = \auth()->user(); // null
$user = Auth::user(); // null
$user = $request->user(); // null
}
}
In index method I know why the user is null since the route is not wrapped in auth:api middleware, but how would I get the auth user in this method even if it's not required.
I am sure I am missing something but I don't know what. I'll be happy to provide more code.
EDIT:
My auth.api middleware:
class AuthenticationHeader
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (!$request->bearerToken()) {
if ($request->hasCookie('auth_token')) {
$token = $request->cookie('auth_token');
$request->headers->add(['Authorization' => 'Bearer ' . $token]);
}
}
return $next($request);
}
}
API Routes
Route::group(['prefix' => 'v1'], function() {
// Authentication
Route::post('/login', 'AuthController#login');
Route::post('/register', 'AuthController#register');
Route::post('/password/reset', 'AuthController#sendPasswordResetLink');
Route::post('/password/update', 'AuthController#callResetPassword');
// Articles
Route::get('/articles', 'ArticleController#index');
Route::middleware(['auth:api'])->group(function () {
// Get Logged in User
Route::get('/user', function (Request $request) {
return $request->user(); // returns the actual logged in user
});
// Articles
Route::post('/articles', 'ArticleController#store');
Route::get('/articles/{id}', 'ArticleController#show');
Route::put('/articles/{id}', 'ArticleController#update');
Route::delete('/articles/{id}', 'ArticleController#destroy');
// Log Out
Route::post('/logout', 'AuthController#logout');
});
});
Auth Controller
<?php
namespace App\Http\Controllers\Api;
use App\Mail\ResetPassword;
use Carbon\Carbon;
use Illuminate\Http\Request;
use App\Http\Controllers\Api\BaseController as BaseController;
use App\User;
use Illuminate\Support\Facades\Auth;
use Validator;
use Illuminate\Support\Facades\Password;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Foundation\Auth\ResetsPasswords;
//use Illuminate\Foundation\Auth\VerifiesEmails;
//use Illuminate\Auth\Events\Verified;
class AuthController extends BaseController
{
use ResetsPasswords;
/**
* Authenticate api
* #param Request
* #return \Illuminate\Http\Response
*/
public function login(Request $request)
{
$validator = Validator::make($request->all(), [
'email' => 'required|email',
'password' => 'required',
]);
if ($validator->fails()) {
return $this->sendError('Validation Error.', $validator->errors(), 400);
}
$email = $request->input('email');
$password = $request->input('password');
if (Auth::attempt(['email' => $email, 'password' => $password])) {
$user = Auth::user();
if ($user->hasVerifiedEmail()) {
$success['token'] = 'Bearer ' . $user->createToken('MyApp')->accessToken;
$success['user'] = $user->only('id', 'name', 'email', 'avatar');
return $this->sendResponse($success, 'User logged in successfully.');
} else {
return $this->sendError('Please verify your Email.', [], 400);
}
}
return $this->sendError('Wrong Credentials.', [], 400);
}
/**
* Register API
* #param Request
* #return \Illuminate\Http\Response
*/
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required',
'email' => 'required|email',
'password' => 'required',
'c_password' => 'required|same:password',
//'g-recaptcha-response' => 'required|captcha',
]);
if ($validator->fails()) {
return $this->sendError('Validation Error.', $validator->errors(), 400);
}
$name = $request->input('name');
$email = $request->input('email');
$password = $request->input('password');
$user = User::where('email', $email)->first();
if ($user) {
return $this->sendError('This email address is already taken. Please try another.', [], 400);
}
$user = User::create([
'name' => $name,
'email' => $email,
'password' => bcrypt($password)
]);
$user->sendApiEmailVerificationNotification();
return $this->sendResponse([], 'Please confirm yourself by clicking on verify user button sent to you on your email.');
}
/**
* Send reset password email API
* #param Request
* #return \Illuminate\Http\Response
*/
public function sendPasswordResetLink(Request $request)
{
$validator = Validator::make($request->all(), [
'email' => 'required|email',
]);
if ($validator->fails()) {
return $this->sendError('Validation Error.', $validator->errors(), 400);
}
$email = $request->input('email');
$response = Password::sendResetLink(['email' => $email], function (Message $message) {
$message->subject($this->getEmailSubject());
});
switch ($response) {
case Password::RESET_LINK_SENT:
return $this->sendResponse([], 'We have e-mailed your password reset link!');
case Password::INVALID_USER:
return $this->sendError('We can\'t find a user with that e-mail address.', [], 400);
}
}
/**
* Reset password action API
* #param Request
* #return \Illuminate\Http\Response
*/
public function callResetPassword(Request $request)
{
$validator = Validator::make($request->all(), [
'token' => 'required',
'email' => 'required|email',
'password' => 'required',
'password_confirmation' => 'required|same:password',
]);
if ($validator->fails()) {
return $this->sendError('Validation Error.', $validator->errors(), 400);
}
// Here we will attempt to reset the user's password. If it is successful we
// will update the password on an actual user model and persist it to the
// database. Otherwise we will parse the error and return the response.
$response = $this->broker()->reset(
$this->credentials($request),
function ($user, $password) {
$this->resetPassword($user, $password);
}
);
if ($response == Password::PASSWORD_RESET) {
return $this->sendResponse([], 'User password has been successfully reset.');
} else {
return $this->sendError($response, [], 400);
}
}
/**
* Logout API
* #param Request
* #return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
if (Auth::check()) {
Auth::user()->oauthAcessTokens()->delete();
return $this->sendResponse([], 'User logged out successfully.');
}
}
}
Base Controller
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class BaseController extends Controller
{
/**
* success response method.
*
* #return \Illuminate\Http\Response
*/
public function sendResponse($result, $message = null)
{
$response = [
'success' => true,
'data' => $result
];
if (!empty($message)) {
$response['message'] = $message;
}
return response()->json($response, 200);
}
/**
* return error response.
*
* #return \Illuminate\Http\Response
*/
public function sendError($error, $errorMessages = [], $code = 422)
{
$response = [
'success' => false,
'message' => $error,
];
if (!empty($errorMessages)) {
$response['data'] = $errorMessages;
}
return response()->json($response, $code);
}
}

Prevent overwriting errors message on a page

I have a page with 2 separated forms, routes and requests.
In this forms I have a field with the same name (for example in each form I have email field).
The problem is when I validating one forms and when form have error, in both forms the error will show.
How can I prevent overwriting errors ?
This is my codes:
Register Request :
class RegisterFormRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'email' => 'required|email|unique:users',
'name' => 'required',
'location' => 'required'
];
}
}
Login Request:
class LoginFormRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'email' => 'required|email',
'password' => 'required'
];
}
}
in blade I use this code above of email field in both forms:
{{$errors->has('email') ? 'has-error' : null }}
When I don't enter email in Login form and submit, the error will show above of email field in Register form too.
Finally I found the solution.
I should add $errorBag variable in Request file:
class RegisterFormRequest extends FormRequest
{
protected $errorBag = 'register';
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'email' => 'required|email|unique:users',
'name' => 'required',
'location' => 'required'
];
}
}
and in blade:
{{$errors->register->has('email') ? 'has-error' : null }}
I have a solution but I am not sure if it is the best way of doing it. The login controller uses the AuthenticatesUsers trait which contains a method sendFailedLoginResponse
By overriding this method in LoginController, you can name the error message bag like this:
protected function sendFailedLoginResponse(Request $request)
{
$errors = [$this->username() => trans('auth.failed')];
if ($request->expectsJson()) {
return response()->json($errors, 422);
}
return redirect()->back()
->withInput($request->only($this->username(), 'remember'))
->withErrors($errors, 'login');
}
Then, in your blade file you can maybe do something like this:
#if (! $errors->login->isEmpty())
<ul>
#foreach ($errors->login->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
#endif

Append errors with custom error

How can i append the $errors->all() array? Like a custom error message... Searched on the internet but i did not find anything.
In my view:
#if (count($errors) > 0)
<div class="alert alert-danger">
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
</div>
#endif
My controller:
<?php
namespace App\Http\Controllers\Admin;
use App\User;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
class UserController extends Controller
{
/**
* #param Request $request
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function login(Request $request)
{
$this->validate($request, [
'email' => 'required|email|exists:users,email',
'password' => 'required',
]);
if ($user = Auth::attempt([
'email' => $request->email,
'password' => $request->password
])) {
/** #var $user User */
if ($user->isAdmin()) {
return redirect()
->route('admin.dashboard');
}
}
// wrong email or password
return redirect('/admin/login');
}
}
First use Validator facade for validation and then
$inputs = $request->all();
$rules = array(
'email' => 'required|email|exists:users,email',
'password' => 'required',
);
$messages = array();
$validator = Validator::make($inputs,$rules,$messages);
$validator->after(function($validator) {
//do some stuff
$validator->errors()->add('error', 'error messgae');
})
custom error message :
return redirect('/admin/login')->withErrors([
'error_key' => 'error_message'
]);
and if you want flush old inputs :
return redirect('/admin/login')->withInput()->withErrors([
'error_key' => 'error_message'
]);
You should write this
<?php
namespace App\Http\Controllers\Admin;
use App\User;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
class UserController extends Controller
{
/**
* #param Request $request
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function login(Request $request)
{
$this->validate($request, [
'email' => 'required|email|exists:users,email',
'password' => 'required',
]);
if ($user = Auth::attempt([
'email' => $request->email,
'password' => $request->password
])) {
/** #var $user User */
if ($user->isAdmin()) {
return redirect()
->route('admin.dashboard');
}else {
return redirect('/admin/login')->>with('errors', 'Sorry!!! something went wrong. please try again.');
}
}else {
return redirect('/admin/login')->>with('errors', 'Sorry!!! something went wrong. please try again.');
}
// wrong email or password
return redirect('/admin/login');
}
}
You can add to errors while using Validator Facade for validation:
$validator = Validator::make(
[
'email' => $request->email,
'password' => $request->password
],
[
'email' => 'required|email|exists:users,email',
'password' => 'required',
]
);
$validator->errors()->add('custom-error', 'my custom error');

laravel 5.2 - create password for user

I want to implement a backend user management but how can i set up a hased password for a user?
my controller:
public function store(Request $request)
{
$this->validate($request, ['email' => 'required', 'name' => 'required', 'password' => 'required', 'surname' => 'required', ]);
$user = new User($request->all());
$user->password=bcrypt($request);
$user->save();
return redirect('dash/users');
}
view
<div class="form-group {{ $errors->has('password') ? 'has-error' : ''}}">
{!! Form::label('password', trans('users.password'), ['class' => 'col-sm-3 control-label']) !!}
<div class="col-sm-6">
{!! Form::text('password', null, ['class' => 'form-control', 'required' => 'required']) !!}
{!! $errors->first('password', '<p class="help-block">:message</p>') !!}
</div>
</div>
fixed now, the function work and my new user are stored in database, but when i try to login with them the loginform say me "nothing record found"
why?
You need to pass the password text to bcrypt instead of whole $request
$user->password=bcrypt('yourpasswordtext');
<?php
namespace App\Http\Controllers\Dash;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Auth;
use App\User;
use App\Report;
use App\Category;
use Illuminate\Http\Request;
use Carbon\Carbon;
use Session;
class UsersController extends Controller
{
/**
* Display a listing of the resource.
*
* #return void
*/
public function index()
{
$users = User::paginate(15);
return view('dash.users.index', compact('users'));
}
/**
* Show the form for creating a new resource.
*
* #return void
*/
public function create()
{
return view('dash.users.create');
}
/**
* Store a newly created resource in storage.
*
* #return void
*/
public function store(Request $request)
{
$this->validate($request, ['email' => 'required', 'name' => 'required', 'password' => 'required', 'surname' => 'required', ]);
$user = new User($request->all());
$user->password = bcrypt($request);
$user->save();
return redirect('dash/users');
}
/**
* Display the specified resource.
*
* #param int $id
*
* #return void
*/
public function show($id)
{
$user = User::findOrFail($id);
return view('dash.users.show', compact('user'));
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
*
* #return void
*/
public function edit($id)
{
$user = User::findOrFail($id);
return view('dash.users.edit', compact('user'));
}
/**
* Update the specified resource in storage.
*
* #param int $id
*
* #return void
*/
public function update($id, Request $request)
{
$this->validate($request, ['email' => 'required', 'name' => 'required', 'password' => 'required', 'surname' => 'required', ]);
$user = User::findOrFail($id);
$user->update($request->all());
Session::flash('flash_message', 'User updated!');
return redirect('dash/users');
}
/**
* Remove the specified resource from storage.
*
* #param int $id
*
* #return void
*/
public function destroy($id)
{
User::destroy($id);
Session::flash('flash_message', 'User deleted!');
return redirect('dash/users');
}
}
update, this is controller
<?php
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/
/* ruote for Admin */
Route::group(['middleware' => ['web']], function () {
Route::resource('dash/reports', 'Dash\\ReportsController');
});
Route::group(['middleware' => ['role:admin']], function () {
Route::resource('dash/categories', 'Dash\\CategoriesController');
});
Route::group(['middleware' => ['role:admin']], function () {
Route::resource('dash/roles', 'Dash\\RolesController');
});
Route::group(['middleware' => ['role:admin']], function () {
Route::resource('dash/permissions', 'Dash\\PermissionsController');
});
Route::group(['middleware' => ['role:admin']], function () {
Route::resource('dash/users', 'Dash\\UsersController');
});
/* another routes */
Route::auth();
Route::get('/provola', 'HomeController#autorizzazione');
Route::get('/home', 'HomeController#index');
Route::get('/', function () {return view('welcome');});
/* injection for user roles
Route::get('/start', 'HomeController#inject');
*/
my routes
For Laravel 5.6+
Password should be hashed using Hash::make($password)

Resources