Custom validation rules - php

I want to create a validation to a form that resets the password.
I have a method that checks whether the new password are the same and have more than 6 characters.
I miss only one thing to check if given the previous password is consistent.
/**
* Reset user password
*
* #return void
*/
public function reset_password(Request $request){
$this->validate($request, [
'old_password' => 'required',
'password' => 'required|min:6|confirmed'
]);
$user = Auth::user();
$user->password = bcrypt($request->password);
$user->save();
return redirect('/user/profile');
}

Try this:
\Hash::check($request->password, $user->password);

The best way to do this is to create new validation rule: Custom Validation Rules.
What I did (not tested):
// Somewhere inside your app service provider boot()
Validation::extend('notSamePassword', function($attribute, $value, $parameters, $validator) {
return !Hash::check($attribute, $parameters[0]);
}
And then in your controller/place where you validate:
$password = 'password hash to validate with';
$this->validate($request, [
'old_password' => 'required',
'password' => 'required|min:6|confirmed|notSamePassword:'.$password
]);

Related

How to access currently logged in user globally in Laravel, Auth::user() returning null

When a user is logged in, session('person_id') is set, but Auth::user() returns null.
This means I have to do this everywhere I need access to properties or methods of the user:
$person = Person::where('id', session('person_id'))->firstOrFail();
What is a better solution for this? Could I set $person in the BaseController then access the user via $this->user when I need it?
I don't want to do a DB query for every request on every page. Using Laravel 8 with PHP 8.
Here are my current login and signup functions:
/**
* Handles user login
*
* #param Request $request
* #return \Illuminate\Http\RedirectResponse
*/
public function login(Request $request)
{
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
if (Auth::attempt($credentials, request('remember'))) {
$request->session()->regenerate();
return redirect()->intended('/account')->with('status', 'Logged in');
}
return back()->withErrors([
'email' => 'The provided credentials do not match our records.',
]);
}
/**
* Saves a new unverified user, sends code to their email and redirects to verify page
*
* #param Request $request
*/
public function signUp(Request $request)
{
// #todo Move to SignUpRequest file
$request->validate([
'email' => 'required|email|unique:people',
'password' => 'required',
]);
$person = new Person;
$person->email = $request->email;
$person->password = Hash::make($request->password);
if (!$person->save()) {
return back()->with('status', 'Failed to save the person to the database');
}
$request->session()->put('person_id', $person->id);
$verification = new Verification;
$verification->person_id = $person->id;
$verification->code = rand(111111, 999999);
$verification->valid_from = Carbon::now();
$verification->expires_at = Carbon::now()->addDay();
if (!$verification->save()) {
return back()->with('status', 'Failed to save the verification to the database');
}
// email stuff
return redirect('/verify')->with('status', 'Successfully created account, please verify to continue');
}
It seems your fighting with framework default authentication you're using another model instead of User
I recommend reading the official documentation
You can take a look at laravel breeze to see how they implemented authentication
if you check the laravel breeze you'll see you missed the Auth::login($user)
public function store(Request $request)
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
event(new Registered($user));
Auth::login($user);
return redirect(RouteServiceProvider::HOME);
}
Laravel ships with a the global helper auth() and you can access the logged user with auth()->user()

Getting user data with Laravel Sanctum

I was using Laravel's built-in api token authentication before but I wanted to provide multiple api tokens for different clients and with Laravel 7.x, I'm trying to migrate to Laravel Sanctum.
API seems authenticates user without any problem but when I try to get user data with Auth::user();, it returns null. Also  Auth::guard('api')->user(); returns null too.
What should I use as Auth guard? Or is it correct way to get user data based on provided token?
Thank you very much....
auth('sanctum')->user()->id
auth('sanctum')->check()
without middleware, you could use these.
First, route through the sanctum auth middleware.
Route::get('/somepage', 'SomeController#MyMethod')->middleware('auth:sanctum');
Then, get the user.
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AuthController extends Controller
{
public function MyMethod(Request $request) {
return $request->user();
}
}
auth()->user() is a global helper, Auth::user() is a support facade, and $request->user() uses http. You can use any of them.
For a quick test, try
Route::get('/test', function() {
return auth()->user();
})->middleware('auth:sanctum');
Be sure to send your token in a header like so:
Authorization: Bearer UserTokenHere
Send token in the Authorization header, below code return the auth user.
Route::middleware('auth:sanctum')->group(function () {
Route::get('/profile/me', function (Request $request) {
return $request->user();
});
});
In case of restful api, suggest you to send Accept header also for checking at authenticate middleware for redirection if not authenticated. By default for restful api it redirect to login form (if any) if user not authenticated.
namespace App\Http\Middleware;
protected function redirectTo($request)
{
if (!$request->expectsJson()) {
return route('login');
}
}
When you are logging in the user, in your login function use something like this
public function login(Request $request)
{
if(Auth::attempt($credentials))
{
$userid = auth()->user()->id;
}
}
Then send this user id to the client and let it store in a secured way on client-side. Then with every request, you can use this user-id to serve data for next requests.
private $status_code= 200; // successfully
public function register(Request $request)
{
// $validator = $this->validator($request->all())->validate();
$validator = Validator::make($request->all(),
[
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255'], // , 'unique:users'
'password' => ['required', 'string', 'min:4'],
]
);
if($validator->fails()) {
return response()->json(["status" => "failed", "message" => "Please Input Valid Data", "errors" => $validator->errors()]);
}
$user_status = User::where("email", $request->email)->first();
if(!is_null($user_status)) {
return response()->json(["status" => "failed", "success" => false, "message" => "Whoops! email already registered"]);
}
$user = $this->create($request->all());
if(!is_null($user)) {
$this->guard()->login($user);
return response()->json(["status" => $this->status_code, "success" => true, "message" => "Registration completed successfully", "data" => $user]);
}else {
return response()->json(["status" => "failed", "success" => false, "message" => "Failed to register"]);
}
}
/**
* 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:4'],
]);
}
/**
* Create a new user instance after a valid registration.
* #author Mohammad Ali Abdullah ..
* #param array $data
* #return \App\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
protected function guard()
{
return Auth::guard();
}
/**
* method public
* #author Mohammad Ali Abdullah
* #date 01-01-2021.
*/
public function login(Request $request)
{
$validator = Validator::make($request->all(),
[
"email" => "required|email",
"password" => "required"
]
);
// check validation email and password ..
if($validator->fails()) {
return response()->json(["status" => "failed", "validation_error" => $validator->errors()]);
}
// check user email validation ..
$email_status = User::where("email", $request->email)->first();
if(!is_null($email_status)) {
// check user password validation ..
// ---- first try -----
// $password_status = User::where("email", $request->email)->where("password", Hash::check($request->password))->first();
// if password is correct ..
// ---- first try -----
// if(!is_null($password_status)) {
if(Hash::check($request->password, $email_status->password)) {
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials)) {
// Authentication passed ..
$authuser = auth()->user();
return response()->json(["status" => $this->status_code, "success" => true, "message" => "You have logged in successfully", "data" => $authuser]);
}
}else {
return response()->json(["status" => "failed", "success" => false, "message" => "Unable to login. Incorrect password."]);
}
}else{
return response()->json(["status" => "failed", "success" => false, "message" => "Email doesnt exist."]);
}
}
public function logout()
{
Auth::logout();
return response()->json(['message' => 'Logged Out'], 200);
}
I see that no answer has been accepted yet. I just had the problem that my sacntum auth did not work. The auth() helper always returned null.
To solve the problem I removed the comment in the kernel.php under the api key. It is about this class \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class. This is because it is commented out by default.
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
After that I had access to the User object with the auth() helper.
The simplest way to to that is to use auth helpers like
$user = auth('sanctum')->user();
Or you can get it by the request object
//SomeController.php
public function exampleMethod(Request $request)
{
$user = $request->user();
}
To get user by sactum token string like
2|bTNlKViqCkCsOJOXWbtNASDKF7SyHwzHOPLNH
Code be like
use Laravel\Sanctum\PersonalAccessToken;
//...
$token = PersonalAccessToken::findToken($sactumToken);
$user = $token->tokenable;
Note: The most way to pass token is from Authorization headers by bearer
Make sure the sanctum middleware is in api
I was in the same boat; migrated to Sanctum and wondered why all of my $request->user() were empty. The solution for me was to throw some middleware onto the stack to modify the request's user() resolver:
namespace App\Http\Middleware;
use Illuminate\Http\Request;
class PromoteSanctumUser
{
/**
* #param Request $request
* #param \Closure $next
*/
public function handle(Request $request, \Closure $next)
{
$sanctumUser = auth('sanctum')->user();
if ($sanctumUser) {
$request->setUserResolver(function() use ($sanctumUser) {
return $sanctumUser;
});
}
return $next($request);
}
}

Why Laravel Auth::attempt method is not working?

I want to do user authentication, method Auth::attempt() is not working, but data is correct (laravel 5.2)
public function postLogin(Request $request)
{
$this->validate($request, [
'name' => 'required|max:255',
'password' => 'required'
]);
$username = $request->input('name');
$password = $request->input('password');
if (Auth::attempt(['name' => $username, 'password' => $password])) {
return redirect('home.get');
} else {
return "FALSE";
}
If you're using the php artisan make:auth. There's a "use AuthenticatesUsers;" the default code will login and check with email and password. Hence, you need to copy the credential function put into LoginController (override the default code). The code below shows how to login using Email or name with password.
protected function credentials(Request $request)
{
$field = filter_var($request->get($this->name()), FILTER_VALIDATE_EMAIL)
? $this->name()
: 'name';
return [
$field => $request->get($this->name()),
'password' => $request->password,
];
}
You need to attempt by email and password by default

dingo api validator rule need to fix

public function store(Request $request) {
$response = array('response' => '', 'success'=>false);
$rules = [
'email' => 'required|email',
'password' => 'required'
];
$validator = \Validator::make($request->all(), $rules);
if($validator->fails()){
$response['response'] = $validator->messages();
return $this->response->error($response, 401);
// or
return $this->response->error($validator, 401);
}else{
User::create($request->all());
}
}
How can I set validator in laravel using dingo API? I tried above code but does not work can't understand where is the right reference to keep track error logs
Please guide.
$rules = [
'username' => 'required',
'password' => 'required'
];
$payload = app('request')->only('username', 'password');
$validator = app('validator')->make($payload, $rules);
if ($validator->fails()) {
throw new Dingo\Api\Exception\StoreResourceFailedException('Invalid username provided.', $validator->errors());
}
You can try this
public function store()
{
$rules = [
'email' => 'required|email',
'password' => 'required'
];
$payload = app('request')->only('username', 'password');
$validator = app('validator')->make($payload, $rules);
if ($validator->fails()) {
throw new Dingo\Api\Exception\StoreResourceFailedException('Could not create new user.', $validator->errors());
}
User::create($request->all());
// send a success response
}
This example is taken from the documentation of Dingo and customized based on your code.
The best way I've found to do validation especially when using Dingo API is to use Form Requests.
When using Dingo API however, you use
use Dingo\Api\Http\FormRequest;
instead of
use App\Http\Requests\Request;
like in normal form requests.
So in your case, you'd have a form request like
<?php
namespace App\Http\Requests;
use Dingo\Api\Http\FormRequest;
class CreateUser extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'email' => 'required|email',
'password' => 'required'
];
}
}
So this keeps validations outside your controller. And your controller function can just be
public function store(Request $request) {
User::create($request->all());
}
If you are not very familiar with Form Requests, this is a great chance to look at it. Cheers.

Laravel update password only if it is set

I am working on a laravel project with user login. The admin can create new users and edit existing users. I have got a password and a passwordConfirm field in the update-user-form. If the admin puts a new password in the form, it should check for validation and update the record in the db. If not, it shouldn't change the password (keep old one), but update the other user data (like the firstname).
If I try to send the form with an empty password and passwordConfirm field, it doesn't validate. I got a validation error, that the password must be a string and at least 6 characters long, but I don't know why. It seems like the first line of my update function will be ignored.
UserController.php
public function update(User $user, UserRequest $request) {
$data = $request->has('password') ? $request->all() : $request->except(['password', 'passwordConfirm']);
$user->update($data);
return redirect('/users');
}
UserRequest.php
public function rules() {
return [
'firstname' => 'required|string|max:255',
'lastname' => 'required|string|max:255',
'email' => 'required|string|email|max:255',
'password' => 'string|min:6',
'passwordConfirm' => 'required_with:password|same:password',
];
}
If you want to validate a field only when it is present then use sometimes validation rule in such cases.
Add sometimes validation to both password & passwordConfirm. Remove the $data line from update();
// UserController.php
public function update(User $user, UserRequest $request) {
$user->update($request->all());
return redirect('/users');
}
// UserRequest.php
public function rules() {
return [
'firstname' => 'required|string|max:255',
'lastname' => 'required|string|max:255',
'email' => 'required|string|email|max:255',
'password' => 'sometimes|required|string|min:6',
'passwordConfirm' => 'sometimes|required_with:password|same:password',
];
}
Reference - https://laravel.com/docs/5.4/validation#conditionally-adding-rules
I always do this in my projects:
//Your UserController file
public function update(User $user, UserRequest $request) {
$user->update($request->all());
return redirect('/users');
}
//Your UserRequest file
public function rules() {
$rules= [
'firstname' => 'required|string|max:255',
'lastname' => 'required|string|max:255',
'email' => 'required|string|email|max:255'
];
if($this->method()=="POST"){
$rules['password']='sometimes|required|string|min:6';
$rules['passwordConfirm']='sometimes|required_with:password|same:password';
}
return $rules;
}
So, as you can see if your method is POST it means that you want to add a new user so its going to ask for password and passwordConfirm but if your method is PATCH or PUT it means you don't need to validate password and passwordConfirm.
Hope it helps
Maybe you should try the following:
// ... more code
// Removes password field if it's null
if (!$request->password) {
unset($request['password']);
}
$request->validate([
// ... other fields,
'password' => 'sometimes|min:6'
// ... other fields,
]);
// ... more code
you should replace "has" with "filled" in your code
$data = $request->filled('password') ? $request->all() : $request->except(['password', 'passwordConfirm']);
and actually it's better if you use the expression like this
$request->request->remove('password_confirmation');
( ! $request->filled('password') ) ? $request->request->remove('password'):"";
( $request->has('password') ) ? $request->merge([ 'password' => Hash::make($request->post()['password']) ]):"";
//then you can use
$user->update($request->all());
Even better, however, you have to use separate request classes for create and update "php artisan make:request" for ex:
UserUpdateRequest.php and UserCreateRequest.php
for UserCreateRequest your rule is
'password' => 'required|confirmed|min:6',
for UserUpdateRequest your rule is
'password' => 'sometimes|nullable|confirmed|min:6',
and your controller head add this line
use App\Http\Requests\UserCreateRequest;
use App\Http\Requests\UserUpdateRequest;
and your update method must change
public function update(UserUpdateRequest $request, $id)
{
//
}
Standard way of doing this
UserRequest.php
first import Rule
use Illuminate\Validation\Rule;
in your rules array:
'password' => [Rule::requiredIf(fn () => $this->route()->method == "POST")]
Example:
public function rules()
{
return [
'name' => 'required',
'email' => ['required', 'email'],
'password' => [Rule::requiredIf(fn () => $this->route()->method == "POST"), 'confirmed'],
];
}
below php 7.4 use this way
'password' => [Rule::requiredIf(function(){
return $this->route()->method == "POST";
})]

Categories