I try to add new validation rule (Laravel 5.2) for google recaptcha and this rule not working. Can you explain me why? And how to fix it?
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Validator;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Validator::extend('recaptcha', function($attribute, $value, $parameters, $validator) {
return false;
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}
-
class AuthController extends Controller
{
/**
* 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, [
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:6',
'g-recaptcha-response' => 'recaptcha|required',
]);
}
Require rule working fine, recaptcha not.
I try to do dump-autoload - no results.
Thank you very much :)
change recpatcha|required to captcha|required
assuming you're using https://github.com/anhskohbo/no-captcha
Related
I am trying to update an old (5.2) laravel installation to the newest one (9.2) and most is working except the authentication part.
I already installed laravel/ui, I have an authentication middleware, route and controller but for some reason I get:
Invalid route action: [App\Http\Controllers\Auth\AuthController].
My Authenticate.php middleware:
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* #param \Illuminate\Http\Request $request
* #return string|null
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
}
My route in web.php :
Route::get('/login', 'App\Http\Controllers\Auth\AuthController')->name('login');
And my AuthController:
<?php
namespace App\Http\Controllers\Auth;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Topsite\Dataroom\Models\LogEntry;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\RegistersUsers;
class AuthController extends Controller
{
/*
|--------------------------------------------------------------------------
| Registration & Login Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users, as well as the
| authentication of existing users. By default, this controller uses
| a simple trait to add these behaviors. Why don't you explore it?
|
*/
use RegistersUsers, ThrottlesLogins;
/**
* Where to redirect users after login / registration.
*
* #var string
*/
protected $redirectTo = '/';
/**
* The custom login view.
*
* #var string
*/
protected $loginView = 'pages.login';
/**
* Create a new authentication controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest', ['except' => 'logout']);
}
/**
* 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|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:6',
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
/**
* Send the response after the user was authenticated.
*
* #param \Illuminate\Http\Request $request
* #param bool $throttles
* #return \Illuminate\Http\Response
*/
protected function handleUserWasAuthenticated(Request $request, $throttles)
{
if ($throttles) {
$this->clearLoginAttempts($request);
}
if (method_exists($this, 'authenticated')) {
return $this->authenticated($request, Auth::guard($this->getGuard())->user());
}
LogEntry::create([
'account_id' => Auth::id(),
'message' => 'Ingelogd'
]);
return redirect()->intended($this->redirectPath());
}
}
I thought maybe I need to set a method in the route so I changed the route to:
Route::get('/login', 'App\Http\Controllers\Auth\AuthController#handleUserWasAuthenticated')->name('login');
But this gives:
Too few arguments to function App\Http\Controllers\Auth\AuthController::handleUserWasAuthenticated(), 1 passed in C:\xampp\htdocs\dataroom.website.nl.test\vendor\laravel\framework\src\Illuminate\Routing\Controller.php on line 54 and exactly 2 expected
I think the $throttles variable is empty but what needs to be passed there? Or maybe I am thinking wrong alltogether and the fix is something else?
I have a boolean column in my users table for active. But I can't figure out how to have the column checked during login and only allow login if active has a value of 1. I wish I had examples of code I've tried, but I am literally stuck. I am using Laravel 8 and Breeze. I am assuming that the check would happen inside the AuthenticatedSessionController file in the store function. Below is the file.
AuthenticatedSessionController.php
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Http\Requests\Auth\LoginRequest;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AuthenticatedSessionController extends Controller
{
/**
* Display the login view.
*
* #return \Illuminate\View\View
*/
public function create()
{
return view('auth.login');
}
/**
* Handle an incoming authentication request.
*
* #param \App\Http\Requests\Auth\LoginRequest $request
* #return \Illuminate\Http\RedirectResponse
*/
public function store(LoginRequest $request)
{
$request->authenticate();
$request->session()->regenerate();
return redirect()->intended(RouteServiceProvider::HOME);
}
/**
* Destroy an authenticated session.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\RedirectResponse
*/
public function destroy(Request $request)
{
Auth::guard('web')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}
}
Please look at authenticate() function located at app/Http/Requests/LoginRequest.php
/**
* Attempt to authenticate the request's credentials.
*
* #return void
*
* #throws \Illuminate\Validation\ValidationException
*/
public function authenticate()
{
$this->ensureIsNotRateLimited();
//array_merge( $request->only($this->username(), 'password'), ['is_active' => 1 ])
//if (! Auth::attempt($this->only('email', 'password'), $this->filled('remember'))) {
if (! Auth::attempt(array_merge( $this->only('email', 'password'), ['is_active' => 1 ]), $this->filled('remember'))) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'email' => __('auth.failed'),
]);
}
RateLimiter::clear($this->throttleKey());
}
You have to use AuthenticatesUsers where you doing POST login request
AuthenticatesUsers is Laravel's default authentication trait which provide the Login Related methods.
You have to place below method in your LoginController to override default method and add your column to check if user is active or not.
protected function credentials(Request $request)
{
return array_merge($request->only($this->username(), 'password'), ['active' => 1]);
}
I have using Laravel 6.x as a backend with an external (different domain) Vue frontend and do not have register user functionality. The way I register users is by importing batches of users using the Maatwebsite/Laravel-Excel package - which works great.
So when each user is created a job is created by sending each user an email verification link, which when they login for the first time they will need to change their password and simultaneously their email gets marked as verified - which also should work fine.
The problem is that with the already created factory of users, who have their email_verified_at field filled - and the newly imported users - I cannot login as the custom EnsureEmailApiIsVerified middleware does not have access to the $request->user(). I figured out that I can specify the auth guard of 'api' such as $request->user('api') which then can pick up the user, but only of their Bearer token (using Laravel Passport) is sent with the request.
This does make sense as how else would the system know who the request user is without some identifier such as the token. But then how does the standard 'implements MustVerifyEmail' on the User model and subsequent standard 'EnsureEmailIsVerified' middelware on the web routes pick up the $request->user()?
It would stand to reason that either both (standard and my custom) middleware should have access to the $request->user() or both should not.
Now I have had to modify and bring out quite a few framework controllers into my App\Http directory but I have copied them almost verbatim just changing a few things to ensure it works with my API routes - because setting the default guard to 'api' instead of 'web' in config/auth.php had not used it as a default throughout my controllers as thought to be.
So here are the steps I followed:
Created a custom middleware and attached it to the entire 'api' middleware group in App\Http\Kernel.php
/**
* The application's route middleware groups.
*
* #var array
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class
],
'api' => [
'throttle:60,1',
'bindings',
'verifiedapi',
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* #var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'role' => \App\Http\Middleware\HasRole::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'verifiedapi' => \App\Http\Middleware\EnsureApiEmailIsVerified::class,
];
Then here is that custom 'EnsureApiEmailIsVerified' middleware:
<?php
namespace App\Http\Middleware;
use Closure;
use App\Http\Controllers\API\Auth\MustVerifyApiEmail;
class EnsureApiEmailIsVerified
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (! $request->user('api') ||
($request->user('api') instanceof MustVerifyApiEmail &&
! $request->user('api')->hasVerifiedEmail())) {
return abort(403, 'Your email has not yet been verified.');
}
return $next($request);
}
}
You will see that references an instance of my custom 'MustVerifyApiEmail' which is a trait that is used on the User Model, with the only diversion from the standard trait being the public function 'sendApiEmailVerificationNotification' as such:
<?php
namespace App\Http\Controllers\API\Auth;
use App\Notifications\VerifyApiEmail;
trait MustVerifyApiEmail
{
/**
* Determine if the user has verified their email address.
*
* #return bool
*/
public function hasVerifiedEmail()
{
return ! is_null($this->email_verified_at);
}
/**
* Mark the given user's email as verified.
*
* #return bool
*/
public function markEmailAsVerified()
{
return $this->forceFill([
'email_verified_at' => $this->freshTimestamp(),
])->save();
}
/**
* Send the email verification notification.
*
* #return void
*/
public function sendApiEmailVerificationNotification()
{
$this->notify(new VerifyApiEmail);
}
/**
* Get the email address that should be used for verification.
*
* #return string
*/
public function getEmailForVerification()
{
return $this->email;
}
}
This new 'sendApiEmailVerificationNotification()' notifies the $request-user('api') with a custom VerifyApiEmail Notification, as such:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Config;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
class VerifyApiEmail implements ShouldQueue
{
use Queueable;
/**
* The callback that should be used to build the mail message.
*
* #var \Closure|null
*/
public static $toMailCallback;
/**
* Create a new notification instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Get the notification's delivery channels.
*
* #param mixed $notifiable
* #return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Build the mail representation of the notification.
*
* #param mixed $notifiable
* #return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
$verificationUrl = $this->verificationUrl($notifiable);
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
}
return (new MailMessage)
->subject(Lang::get('Verify Email Address'))
->line(Lang::get('Please click the button below to verify your email address.'))
->action(Lang::get('Verify Email Address'), $verificationUrl)
->line(Lang::get('If you did not create an account, no further action is required.'));
}
/**
* Get the verification URL for the given notifiable.
*
* #param mixed $notifiable
* #return string
*/
protected function verificationUrl($notifiable)
{
return URL::temporarySignedRoute(
'verification.api.verify',
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
[
'id' => $notifiable->getKey(),
'hash' => sha1($notifiable->getEmailForVerification()),
]
);
}
/**
* Set a callback that should be used when building the notification mail message.
*
* #param \Closure $callback
* #return void
*/
public static function toMailUsing($callback)
{
static::$toMailCallback = $callback;
}
}
The new api routes are as follows:
Route::namespace('API\Auth')->group(function () {
Route::post('login', 'PassportController#login');
Route::post('refresh', 'PassportController#refresh');
Route::post('logout', 'PassportController#logout');
Route::get('email/verify/{id}/{hash}', 'VerificationApiController#verify')->name('verification.api.verify');
Route::get('email/resend', 'VerificationApiController#resend')->name('api.verification.resend');
});
And the VerificationApiController is as follows:
<?php
namespace App\Http\Controllers\API\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\Verified;
use Illuminate\Auth\Access\AuthorizationException;
class VerificationApiController extends Controller
{
/**
* Show the email verification notice.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function show(Request $request)
{
//
}
/**
* Mark the authenticated user's email address as verified.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*
* #throws \Illuminate\Auth\Access\AuthorizationException
*/
public function verify(Request $request)
{
if (! hash_equals((string) $request->route('id'), (string) $request->user('api')->getKey())) {
throw new AuthorizationException;
}
if (! hash_equals((string) $request->route('hash'), sha1($request->user('api')->getEmailForVerification()))) {
throw new AuthorizationException;
}
if ($request->user('api')->hasVerifiedEmail()) {
return response()->json(['error' => 'Email already verified'], 422);
}
if ($request->user('api')->markEmailAsVerified()) {
event(new Verified($request->user('api')));
}
return response()->json(['success' => 'Email verified!']);
}
/**
* Resend the email verification notification.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function resend(Request $request)
{
$request->user('api')->sendApiEmailVerificationNotification();
return response()->json(['success' => 'Email verification has been resent!']);
}
}
I also noticed that onn the User Model it extends User as Authenticatable which then uses the standard MustVerifyEmail' - so I brought that out of the framework as well and changed the usage to the new MustVerifyApiEmail - like so:
<?php
namespace App\Http\Controllers\API\Auth;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use App\Http\Controllers\API\Auth\MustVerifyApiEmail;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class User extends Model implements
AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword, MustVerifyApiEmail;
}
My User model then looks like this at the top:
<?php
namespace App;
use Illuminate\Support\Str;
use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use App\Http\Controllers\API\Auth\User as Authenticatable;
use App\Http\Controllers\API\Auth\MustVerifyApiEmailInterface;
class User extends Authenticatable implements MustVerifyApiEmailInterface
{
use HasApiTokens, Notifiable;
...
As you can see it's quite a bit of customization - but it should all work in theory and I am getting no errors that I can use. Here are the errors that I get:
When I login with a user who's email is verified or even not verified, I get the error that the user's email has not been verified - but only because it doesn't pick up the $request->user('api'). When I try to throw an error in the middleware itself before returning the request dumping the $request->user('api') it gives me null
So my question is, with the standard middleware of 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, - how does this pick up the $request->user()?
Is there something I am missing or am I going about this the wrong way? It seems that when the user logs in it doesn't log them in and then run the middleware - so there is no $request->user('api') - maybe because I am using Passport, but I would think that what should happen is that the middleware needs to run after it has authenticated the user then it would have access to the $request->user('api')
ANY GUIDANCE WOULD BE EXTREMELY APPRECIATED!
Hopefully you have long since solved this one, but since I came across this same problem earlier I thought I'd share my solution. I wanted to use verify on login of the api, but realised that this would be invoked before the user was logged in or authenticated which means there is no user on the request.
In my case I made changes to my custom EnsureEmailsVerified like this:
<?php
namespace App\Http\Middleware;
use App\User;
use Closure;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class EnsureApiEmailIsVerified
{
/**
* Handle an incoming request.
*
* #param Request $request
* #param \Closure $next
* #return JsonResponse
*/
public function handle($request, Closure $next)
{
$user = $request->user() ?? User::where('email', $request->get('username'))->first();
if (! $user ||
($user instanceof MustVerifyEmail &&
! $user->hasVerifiedEmail())) {
return response()->json(['error' => [
'message' => __('errors.email_not_verified'),
'status_code' => 401,
]], 401);
}
return $next($request);
}
}
And likewise I made a custom VerifiesEmails trait and changed the verify method to find the user based on the id in the email link because I know this will be going to new, non-logged in users:
public function verify(Request $request)
{
$user = User::find($request->get('id'));
if (! hash_equals((string) $request->get('id'), (string) $user->getKey())) {
throw new AuthorizationException;
}
if (! hash_equals((string) $request->get('hash'), sha1($user->getEmailForVerification()))) {
throw new AuthorizationException;
}
if ($user->hasVerifiedEmail()) {
return new Response('', 204);
}
if ($user->markEmailAsVerified()) {
event(new Verified($user));
}
if ($response = $this->verified($request)) {
return $response;
}
return new Response('', 204);
}
this CreatePost.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CreatePost 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 [
'title' => 'required|string',
'content' => 'required',
];
}
}
anyone please help me ?? Class App\Http\Request\CreatePost does not exist
thank
you need to import the model on top
for example
use App\Supplier;
I would like to count how many times a user has been logged in using the Events in Laravel.
I have this Event defined:
<?php namespace App\Handlers\Events;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldBeQueued;
use App\User;
use Illuminate\Support\Facades\Log;
class AuthLoginEventHandler {
/**
* Create the event handler.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* #param User $user
* #param $remember
* #return void
*/
public function handle(User $user, $remember)
{
Log::info('Logged in');
$user->login_counter = 1;
$user->save();
$user->increment('login_counter');
}
}
and in the EventServiceProvider I have the following:
<?php namespace App\Providers;
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
class EventServiceProvider extends ServiceProvider {
/**
* The event handler mappings for the application.
*
* #var array
*/
protected $listen = [
'auth.login' => [
'App\Handlers\Events\AuthLoginEventHandler'
]
];
/**
* Register any other events for your application.
*
* #param \Illuminate\Contracts\Events\Dispatcher $events
* #return void
*/
public function boot(DispatcherContract $events)
{
parent::boot($events);
//
}
}
Update:
I have override the postLogin() from trait to the AuthController as:
<?php namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\Registrar;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
class AuthController extends Controller {
use AuthenticatesAndRegistersUsers;
/**
* Create a new authentication controller instance.
*
* #param \Illuminate\Contracts\Auth\Guard $auth
* #param \Illuminate\Contracts\Auth\Registrar $registrar
* #return void
*/
public function __construct(Guard $auth, Registrar $registrar)
{
$this->auth = $auth;
$this->registrar = $registrar;
$this->middleware('guest', ['except' => 'getLogout']);
}
public function postLogin(Request $request)
{
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
$credentials = $request->only('email', 'password');
if ($this->auth->attempt($credentials, $request->has('remember')))
{
Log::info('Logged in User from AuthController');
return redirect()->intended($this->redirectPath());
}
return redirect($this->loginPath())
->withInput($request->only('email', 'remember'))
->withErrors([
'email' => $this->getFailedLoginMessage(),
]);
}
}
This is not working on my Laravel 5.0, I dont know why, is there any other alternative to count the logins of user other than using Events?
I am checking the logs and there is nothing comming in, when I try Log::info('Logged in'); within the boot method, it comes to logs.
First thing, because you have added something up into Provider, you need to clear the previous compiled source (which is cached to speed up loading time).
$ php artisan clear-compiled
Secondly, your code has counting problem,
$user->login_counter = 1; <-- set count to 1 ??
$user->save(); <--- then SAVE?
$user->increment('login_counter'); <--- INCREMENT for what?
Instead, it should be like this
$user->login_counter += 1;
$user->save();
Make sure in the migration you have the default (initial) value for login_counter
$table->integer('login_counter')->unsigned()->default(0);
Then it just works!