How to rewrite a method for using with AJAX? - php

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

Related

Laravel JWTAuth::attempt suddenly returning False

I'm having a problem that came out of nowhere. I created a login function using JWTAuth and it was working pretty well, the JWTAuth::attempt returns the token that I needed. But after I added the Auto-Hash password function in the User Model, the JWTAuth::attempt always returns false.
I also added softDeletes in the user migration. What causes the JWTAuth::attempt keeps returning false? Because I didn't modify anything except the User Model and the User Migration. How can I fix this problem?
Here is my codes:
Auto-Hash Password Function (User.php Model)
public function setPasswordAttribute($password)
{
$this->attributes['password'] = Hash::make($password);
}
Login Function (AuthController.php Controller)
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
try {
$token = JWTAuth::attempt($credentials);
return response()->json(['status' => 'success','token' => $token], 200);
} catch(Exception $e){
return response()->json(['error' => $e], 401);
}
}
You may use for checking credentials.
$credentials = $request->only('email', 'password');
if (false === ($token = JWTAuth::attempt($credentials))) {
return response()->json(['status' => 'error'], 400);
}
return response()->json(['status' => 'success', 'token' => $token], 200);
Also, just use auto-hash inside your controller or in your service. Maybe your setPasswordAttribute function has some side effects.

Is there any way to change response of auth:api , A laravel api_token

I am creating API with Default api-authentication
I am using laravel 6.x
Its run when i generate on user register and pass generated token with request.
But
when i pass a wrong token, Then it shows a Login page HTML, i want to show some custom JSON response instead of HTML
Also is there any way to check that passed token is same with passed user id or not. Because user can pass different user id with token.
My api route file as below
Route::middleware('auth:api')->post('/listUser', 'ApiController#listUser');
I have manage my points as below
For Point 1
when i pass a wrong token, Then it shows a Login page HTML, i want to show some custom JSON response instead of HTML
I made change in App/Exceptions/handler.php
Modify render function as below
public function render($request, Exception $exception)
{
if ($exception instanceof NotFoundHttpException) {
if ($request->is('api/*')) {
return response()->json(['error' => 'Not Found'], 404);
}
//return response()->view('404', [], 404);
}
return parent::render($request, $exception);
}
It workrs well because i have an api based routes
My api route look likes
// Request with Authentication v1
Route::group(['prefix' => 'v1', 'namespace' => 'Api\v1', 'middleware' => ['api','auth:api'] ], function () {
Route::post('/myProfile', 'ApiController#myProfile');
});
// Request without Authentication v1
Route::group(['prefix' => 'v1', 'namespace' => 'Api\v1', 'middleware' => 'api'], function () {
Route::post('/register', 'ApiController#register');
});
For Point 2
Also is there any way to check that passed token is same with passed user id or not. Because user can pass different user id with token.
For that i have created a function checkValidations in ApiController and check user id is associated with particular token or not as below:
In that function i check in way that
Check for all validation passed from called method
Match token associated with user id then return success
else return invalid token response
Function Code
public function checkValidations($required = [], $request = [])
{
$validator = Validator::make($request->all(), $required);
if ($validator->fails()) {
$this->response[] = array(
'status' => 'false',
'response_msg' => implode(",",$validator->messages()->all()),
);
return array('response' => $this->response);
} else if(isset($request['api_token']) && auth('api')->user()->id ==
$request['id']) {
return 'success';
} else {
$this->response[] = array(
'status' => 'false',
'response_msg' => 'Invalid token',
);
return array('response' => $this->response);
}
}
And call that checkValidations from any function and can reuse it as
public function myProfile(Request $request)
{
$validation = [
'id' => 'bail|required|exists:users',
'api_token' => 'bail|required|min:60|max:60'
];
if( $this->checkValidations($validation, $request) == 'success'){
$this->response[] = array(
'status' => 'true',
'response_msg' => 'Success',
'data' => auth('api')->user()
);
}
return array('response' => $this->response);
}
May be there is many other best way to manage that points, but i didn't found, so i manage in above ways.
You can configure a custom response in the Authenticate middleware. e.g.
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($guard === 'api') {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('login');
}
}
return $next($request);
}
You can do this by extending the TokenGuard, with your custom logic. Or you can create a new Middleware, which asserts that user authenticated by API matches the passed user ID.
I just verified the kind of exception if is related with authentication and then the URL( as API guard use '/api' just verify it) and fire the response.
if($exception instanceof \Illuminate\Auth\AuthenticationException){
if($request->is('api/*')){
return response()->json([
'success' => false,
'message' => 'User not logged'
]);
}
}
I made the below change in app/Exceptions/Handler.php.
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Not Authorized'], 404);
}
return redirect()->guest(route('login'));
}
Add use Illuminate\Auth\AuthenticationException in the document. Also, do not forget to add X-Requested-With:XMLHttpRequest to your request header. (Or Headers in postman)
return redirect()->guest(route('login')); is to redirect you to login page when you are not using the APIs.

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 ?

On Axios request laravel auth attemp session part not working

am making a login screen using bootstrap model and when I request using axisos call to server everything working fine but when the response came and I refresh page laravel session part not working
here is my code in controller :
public function __construct()
{
$this->middleware('guest')->except('logout');
}
public function showLoginForm()
{
return view('login');
}
//use AuthenticatesUsers;
public function login(Request $request)
{
$validator = Validator::make($request->all(), [
'phone' => 'required|numeric|',
'password' => 'required|min:6',
]);
if ($validator->fails()) {
// return redirect()->back()->with('errors',$validator->errors())->withInput($request->only('phone', 'remember'));
return response()->json(['success'=>false,'error'=>$validator->errors()], 403);
}
// Attempt to log the user in
if (Auth::guard('web')->attempt(['phone' => '+91'.$request->phone, 'password' => $request->password], $request->remember)) {
// if successful, then redirect to their intended location
return response()->json(['success'=>true,'message'=>'login successfully'], 200);
}
// if unsuccessful, then redirect back to the login with the form data
//dd('auth fail am here');
//return redirect()->back()->withErrors(['Invalid Phone Or Password'])->withInput($request->only('phone', 'remember'));
return response()->json(['success'=>false,'error'=>'Invalid Phone Or Password'], 401);
}
/**
* Where to redirect users after login.
*
* #var string
*/
// protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* #return void
*/
public function logout()
{
Auth::guard('web')->logout();
return redirect('/login');
}
}
this is my axios request:
axios.post(process.env.MIX_BASEURL+'/login', vm.loginDetails)
.then(response => {
event.target.disabled = true;
console.log(response);
// console.log(response.config.method)
if (response.status == 200){
alert('login success')
vm.loginsuccess = true
$("#phone").addClass("is-valid");
$("#password").addClass("is-valid");
vm.successlogin=response.data.message
toastr["success"](vm.successlogin);
if(response.status===200){
let intended=response.data.intended;
alert(intended);
//return;
window.location.href = intended;
this.closeModal();
}
}
})
.catch(error => {
console.log('am here in error');
console.log(error.response);
var errors = error.response
console.log(error.response)
///IF EMPTY FIELDS FOUND///
if (errors.status === 403) {
//alert('empty fields Forbidden')
//alert(errors.data.error.phone[0])
if (errors.data) {
if (errors.data.error.phone) {
vm.errorsPhone = true
$("#phone").addClass("is-invalid");
vm.PhoneError = _.isArray(errors.data.error.phone) ? errors.data.error.phone[0] : errors.data.error.phone
//alert(vm.PhoneError)
toastr["error"](vm.PhoneError);
}
if (errors.data.error.password) {
vm.errorsPassword = true
$("#password").addClass("is-invalid");
vm.passwordError = _.isArray(errors.data.error.password) ? errors.data.error.password[0] : errors.data.error.password
//alert(vm.passwordError)
toastr["error"](vm.passwordError);
}
}
}
if (errors.status === 401) {
//alert('invalid login details')
vm.errorslogin = true
vm.loginerror=errors.data.error
//alert(vm.loginerror)
toastr["error"](vm.loginerror);
}
});
}
**what i have try :**when i click on login my data is gone auth attemp successfull i got 200 and my msg as i set in response then i call to location reload (my first try and fail) it reload page for new data but session data not coming in my blade template

Laravel JWT Auth get user on Login

Is it possible with https://github.com/tymondesigns/jwt-auth
to get the current user? Because right now I can only generate a token (when a user sign in).
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
try {
if (! $token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (Tymon\JWTAuth\Exceptions\JWTException $e) {
return response()->json(['error' => 'could_not_create_token'], 500);
}
return response()->json(compact('token'));
}
You don't need Auth::user();
You can use the toUser method of JWTAuth. Just pass the token as parameter and you will get back the user info:
$user = JWTAuth::toUser($token);
return response()->json(compact('token', 'user'));
For more info, this is the toUser method:
/**
* Find a user using the user identifier in the subject claim.
*
* #param bool|string $token
*
* #return mixed
*/
public function toUser($token = false)
{
$payload = $this->getPayload($token);
if (! $user = $this->user->getBy($this->identifier, $payload['sub'])) {
return false;
}
return $user;
}
You can get logged user data.
$credentials = $request->only('code', 'password', 'mobile');
try {
// verify the credentials and create a token for the user
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);
}
$currentUser = Auth::user();
print_r($currentUser);exit;
You can also use JWTAuth::user() method.
The user() method call is returned in the toUser() method, which itself is an alias for authenticate() method which authenticates a user via a token. If the user is already authenticated, there is no need to authenticate them again (which toUser() does), instead user() method can be used to get the authenticated user.
// $token = JWTAuth::attempt($credentials) was successful...
$user = JWTAuth::user();
return response()->json(compact('token', 'user'));
It works fine for me (laravel 5.7)
U must post token to me function and will return user
use Tymon\JWTAuth\Facades\JWTAuth;
public function me(Request $request)
{
$user = JWTAuth::user();
if (count((array)$user) > 0) {
return response()->json(['status' => 'success', 'user' => $user]);
} else {
return response()->json(['status' => 'fail'], 401);
}
}
try this (it works fine with laravel 5.6,5.7):
use Tymon\JWTAuth\Facades\JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
// code to generate token for user with email testuser#gmail.com
$user=User::where('email','=','testuser#gmail.com')->first();
if (!$userToken=JWTAuth::fromUser($user)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
return response()->json(compact('userToken'));
just need to return inside the specified route middleware
The route that you defined api route
Route::group(['middleware' => 'jwt.auth'], function () {
Route::post('/user', function (Request $request) {
try {
$user = \Illuminate\Support\Facades\Auth::user();
return $user;
} catch (\Tymon\JWTAuth\Exceptions\UserNotDefinedException $e) {
return '$user';
}
});
});
You can get the current user related to token using :
$user = JWTAuth::setToken($token)->toUser();
In Laravel 7.2 if none of the above works use like this to retrive the user:
$credentials = request(['email', 'password']);
if (! $token = auth('api')->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
$user = auth('api')->user();
dd($user);
Solution for who are all struggle validate the user and token and then guard
Problems that i face
Jwt does not return any user even i put currect token
return null
$user = JWTAuth::user(); //getting null
return false
$user = JWTAuth::parseToken()->authenticate();
return false
auth()->user()
Solution to check
Before Going to solution middleware and your route guard is matter so keep in mind
Guard setting config/auth.php
'guards' => [
'website_admin' => [
'driver' => 'jwt',
'provider' => 'website_admin',
],
]
Provider
'providers' => [
'super_admin' => [
'driver' => 'eloquent',
'model' => App\Website_super_admin_auth::class,
],
]
Middleware (must)
<?php
namespace App\Http\Middleware;
use Closure;
use JWTAuth;
use Exception;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
class JwtMiddleware extends BaseMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
try {
$user = JWTAuth::parseToken()->authenticate();
} catch (Exception $e) {
if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException){
return response()->json(['status' => 'Token is Invalid']);
}else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException){
return response()->json(['status' => 'Token is Expired']);
}else{
return response()->json(['status' => 'Authorization Token not found']);
}
}
return $next($request);
}
}
Route(should specify middleware with guard)
Route::group(['prefix' => 'super_ad', 'middleware' => ['jwt.verify','auth:super_admin']], function()
{
Route::get('/test', function (){
// $user = JWTAuth::parseToken()->authenticate(); <-------check user here
$user=auth()->user();
// return $user;
});
})
$user = JWTAuth::setToken($token)->toUser();
Auth::login($user, $remember = true);
public function user(Request $request){
/// get user details from token
$user = JWTAuth::toUser($this->bearerToken($request));
// get payloads in the token
$payload = JWTAuth::getPayload($this->bearerToken($request));
}
public function bearerToken($request)
{
$header = $request->header('Authorization', '');
if (Str::startsWith($header, 'Bearer ')) {
return Str::substr($header, 7);
}
}

Categories