Overriding Laravel's method - php

Given the route:
Route::get('verify/{id}/{hash}', 'Auth\VerificationController#verify');
It uses the Laravel's default verify method from
auth-backend/VerifiesEmails.php
The default verify method looks like bellow:
public function verify(Request $request)
{
if (! hash_equals((string) $request->route('id'), (string) $request->user()->getKey())) {
throw new AuthorizationException;
}
if (! hash_equals((string) $request->route('hash'), sha1($request->user()->getEmailForVerification()))) {
throw new AuthorizationException;
}
if ($request->user()->hasVerifiedEmail()) {
return $request->wantsJson()
? new Response('', 204)
: redirect($this->redirectPath());
}
if ($request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}
if ($response = $this->verified($request)) {
return $response;
}
return $request->wantsJson()
? new Response('', 204)
: redirect($this->redirectPath())->with('verified', true);
}
I would like to change only the last block of the code in the verify method from
return $request->wantsJson()
? new Response('', 204)
: redirect($this->redirectPath())->with('verified', true);
to
return $request->wantsJson()
? new Response('', 204)
: redirect($this->redirectPath())->with([
'verified' => true,
'userNotification' => [
'message' => 'Wellcome to my website',
'title' => 'Hello World',
],
]);
I know I can override the whole verify method in the VerificationController, which is not ideal to copy and paste the whole block of code for a small change.
My question is How can override only the last block of code as mentioned above?

Right before the final return there is this block:
if ($response = $this->verified($request)) {
return $response;
}
So in your VerificationController you can override just the verified method which is meant for that.
If you look into its source you will see it:
source
So in your local VerificationController add:
protected function verified(Request $request)
{
return $request->wantsJson()
? new Response('', 204)
: redirect($this->redirectPath())->with([
'verified' => true,
'userNotification' => [
'message' => 'Wellcome to my website',
'title' => 'Hello World',
],
]);
}

Related

How to show password reset link in response

I have the code below which sends the password reset link and it works fine. What I want to do is show password reset link or token in response. (Yes, I know it's dangerous.)
$status = Password::sendResetLink(
$request->only('email'),
function ($user, $token) {
(\DB::table('password_resets')
->updateOrInsert(
['email' => $user->email],
[
'token' => md5($token)
]
))
? $user->sendPasswordResetNotification(md5($token))
: null;
}
);
if ($request->expectsJson()) {
if ($status === Password::RESET_LINK_SENT) {
return response()->json(['message' => __($status)], 200);
} else {
return response()->json(['message' => __($status)], 422);
}
}
return $status === Password::RESET_LINK_SENT
? back()->with(['status' => __($status)])
: back()->withErrors(['email' => __($status)]);
}
I changed the code as below but I am getting 500 Internal Server Error. What's wrong?
$status = Password::sendResetLink(
$request->only('email'),
function ($user, $token) use (&$token) {
(\DB::table('password_resets')
->updateOrInsert(
['email' => $user->email],
[
'token' => md5($token)
]
))
? $user->sendPasswordResetNotification(md5($token))
: null;
}
);
if ($request->expectsJson()) {
if ($status === Password::RESET_LINK_SENT) {
return response()->json(['message' => __($status), 'link' => md5($token)], 200);
} else {
return response()->json(['message' => __($status)], 422);
}
}
return $status === Password::RESET_LINK_SENT
? back()->with(['status' => __($status)])
: back()->withErrors(['email' => __($status)]);
}
One way would be like this:
$token = DB::table('password_resets')->latest('created_at')->first()->token;
You get the last record inserted in the table (by sorting created_at column) which is the latest password reset and you can get the token from that.
return response()->json(['message' => __($status), 'link' => $token], 200);
And you getting the 500 Server Error could be from the addition of use (&$token) in the second code. use is the way to use the variables out of the function scope in the function and you don't have any variable named $token (Don't confuse with the $token in the function). And you're using the use to use a function variable outside of its scope which won't work.
Also you could set APP_DEBUG to true (NOT IN PRODUCTION MODE) to see the reason for 500 error.

verify-email route does not pass "auth" middleware

The email verification process not working.
I send email correctly. When I click on link to verify the email, I have been redirected to login page.
It seems the route does not pass auth middleware.
This is how I defined the route:
Route::middleware('auth')->group(function () {
Route::get('email/verify/{id}/{hash}', [App\Http\Controllers\Auth\VerificationController::class, 'verify'])
->name('verification.verify');
});
This is the function in the controller (it's the standard laravel function, I did not edited it):
public function verify(Request $request)
{
if (! hash_equals((string) $request->route('id'), (string) $request->user()->getKey())) {
throw new AuthorizationException;
}
if (! hash_equals((string) $request->route('hash'), sha1($request->user()->getEmailForVerification()))) {
throw new AuthorizationException;
}
if ($request->user()->hasVerifiedEmail()) {
return $request->wantsJson()
? new JsonResponse([], 204)
: redirect($this->redirectPath());
}
if ($request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}
if ($response = $this->verified($request)) {
return $response;
}
return $request->wantsJson()
? new JsonResponse([], 204)
: redirect($this->redirectPath())->with('verified', true);
}
The auth middleware is the default middleware come with laravel.

slim/twig-view Won't Work With Additional Route Middleware

I've been using slim/twig-view, documented here: https://notes.enovision.net/slim/composer-package-documentation/slim_twig-view. The recommended way to handle a route, render a view is:
$app->get('/hello/{name}', function ($request, $response, $args) {
$view = Twig::fromRequest($request);
return $view->render($response, 'profile.html', [
'name' => $args['name']
]);
})
Problem is, if you try to add any route based middleware to run afterwards, it fails
$app->get('/hello/{name}', function ($request, $response, $args) {
$view = Twig::fromRequest($request);
return $view->render($response, 'profile.html', [
'name' => $args['name']
]);
})->add(function(Request $request, RequestHandler $handler){
$response = $handler->handle($request);
return $response;
});
With an error like this:
Type: TypeError
Code: 0
Message: Return value of Slim\Handlers\Strategies\RequestResponse::__invoke() must implement interface Psr\Http\Message\ResponseInterface, int returned
File: /var/www/html/vendor/slim/slim/Slim/Handlers/Strategies/RequestResponse.php
Line: 43
I see this is because I'm not returning a response from the route handler, so I rewrite the route handler to return the $response:
$view = Twig::fromRequest($request);
$view->render($response, 'profile.html', [
'name' => $args['name']
]);
return $response;
Still the same error.
I can work around this, but it's a greenfield project and it would be nice to have access to route middleware. Any ideas?

Return JSON response instead of 401 Blade file

I am using AuthBasic for API authentication in a Laravel project,
I have this problem: when the API request authentication is invalid instead of displaying the JSON response it returns the 401 default blade view template.
Here is the code:
app\Http\Middleware\AuthBasic.php
public function handle($request, Closure $next)
{
if (Auth::onceBasic()) {
return response()->json(["message", "Authentication Required!"], 401);
} else {
return $next($request);
}
}
Found the Solution:
app\Exceptions\Handler.php
public function render($request, Exception $exception)
{
if ($request->is('api/*') || $request->wantsJson())
{
$json = [
'success' => false,
'error' => [
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
],
];
return response()->json($json, 401);
}
return parent::render($request, $exception);
}
Remove the 401 or change it to 200 from this line:
return response()->json(["message", "Authentication Required!"], 401);
See the reference, the second parameter is defining the http code to send the browser. [401] in you case.
https://laravel.com/api/5.7/Illuminate/Routing/ResponseFactory.html#method_json
This will fix your problem, probably!
public function handle($request, Closure $next)
{
$result = Auth::onceBasic();
if($result === 401)
return response()->json(["message", "Authentication Required!"]);
else
return $next($request);
}
So here is a half Solution for this problem:
vendor\laravel\framework\src\Illuminate\Auth\SessionGuard.php
public function onceBasic($field = 'email', $extraConditions = [])
{
$credentials = $this->basicCredentials($this->getRequest(), $field);
if (! $this->once(array_merge($credentials, $extraConditions))) {
//return $this->failedBasicResponse();
return response()->json(["Message" => "Authentication Required!"], 401);
}
}
So Instead of returning the Failed Basic Response it will return the JSON Message, but I don't want to make changes in Laravel Core Files, because in case of update they will get lost !
So Any Idea ?

How to rewrite a method for using with AJAX?

I have a trait which handle password restoring logic:
public function reset(Request $request)
{
$this->validate($request, $this->rules(), $this->validationErrorMessages());
$response = $this->broker()->reset(
$this->credentials($request), function ($user, $password) {
$this->resetPassword($user, $password);
}
);
return $response == Password::PASSWORD_RESET
? $this->sendResetResponse($response)
: $this->sendResetFailedResponse($request, $response);
}
protected function rules()
{
return [
'token' => 'required',
'email' => 'required|email',
'password' => 'required|confirmed|min:6',
];
}
protected function sendResetFailedResponse(Request $request, $response)
{
return redirect()->back()
->withInput($request->only('email'))
->withErrors(['email' => trans($response)]);
}
I want to use it with AJAX calls. How should I rewrite sendResetFailedResponse()?
When I use this logic without AJAX and if validation fails on rules() I simply get an error response with 422 status code. But if validation fails while on checking token validity (reset()) - there are no errors with status code in return.
My AJAX is like
axios.post('/password/reset', {
//data to send
})
.then((response) => {
...
})
.catch((error) => {
//I can catch errors which are returning from rules() fail
//I want to catch non-valid token error here too
});
I tried to override
protected function sendResetFailedResponse(Request $request, $response)
{
return response(['email' => trans($response)]);
}
but this code returns token error after AJAX .catch()
I just do this in the reset method and it works pretty good.
public function reset(Request $request)
{
$this->validate($request, $this->rules(), $this->validationErrorMessages());
// 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 ($request->ajax()){
if ($response == Password::PASSWORD_RESET) {
return response()->json(['message' => 'Success'],200);
} else {
return response()->json(['error' => 'Please try again'], 422);
}
}
// If the password was successfully reset, we will redirect the user back to
// the application's home authenticated view. If there is an error we can
// redirect them back to where they came from with their error message.
return $response == Password::PASSWORD_RESET
? $this->sendResetResponse($response)
: $this->sendResetFailedResponse($request, $response);
}
Hope this helps

Categories