Laravel check if all route resourrce params are set - php

I am making a API, i want to check if the posted params (by curl) are equal to de resource params. If not i want to return a json with errors.
How do I get it done to check all route resource parameters?
Route::group(['prefix' => 'api' , 'middleware' => 'auth:api'], function () {
Route::resource('note' , 'NoteController');
});

You can handle the MethodNotAllowedHttpException in Exception/Handler.php in method render
public function render($request, Exception $e)
{
// check if MethodNotAllowedHttpException exception type using status code 405 or instanceof
if ($e->getStatusCode() == 405) {
// custom your response. could be json with 404 or redirect to another page
return response()->json(['error' => true, 'message' => 'not found'], 404);
}
return parent::render($request, $e);
}
more explanation here

The answer is:
Routes.php
Route::put('note', 'NoteController#update');
Controller:
$id = null and make a check id check
public function update(Request $request, $id = null){
if($id != null){
//do stuff
}

Related

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 ?

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

Return 403 if route is not allowed

I have Route group with middleware (I use Zizaco/entrust package):
Route::group(['as' => 'admin.', 'prefix' => 'admin', 'middleware' => ['role:admin']], function (){
...
});
When I try to enter http://mysite/admin while not authenticated I get exception
Symfony \ Component \ HttpKernel \ Exception \ HttpException
No message
But I want to return 403.
I tried to do this:
Route::fallback(function(){
abort(403);
});
but it didn't helped.
Edit 1: here we have exception handle in Laravel 5.5.28
public function abort($code, $message = '', array $headers = [])
{
if ($code == 404) {
throw new NotFoundHttpException($message);
}
throw new HttpException($code, $message, null, $headers);
}
As you can see, there is no 403 handle.
you have to modify handle method of role middleware
public function handle($request, Closure $next, $roles)
{
if (!is_array($roles)) {
$roles = explode(self::DELIMITER, $roles);
}
if ($this->auth->guest() || !$request->user()->hasRole($roles)) {
abort(403); // notice here it returns 403
}
return $next($request);
}

Unable to authenticate with invalid token error in dingo/api with jwt-auth

I'm using dingo/api (that has built-in support for jwt-auth) to make an API.
Suppose this is my routes :
$api->group(['prefix' => 'auth', 'namespace' => 'Auth'], function ($api) {
$api->post('checkPhone', 'LoginController#checkPhone');
//Protected Endpoints
$api->group(['middleware' => 'api.auth'], function ($api) {
$api->post('sendCode', 'LoginController#sendCode');
$api->post('verifyCode', 'LoginController#verifyCode');
});
});
checkPhone method that has task of authorize and creating token is like :
public function checkPhone (Request $request)
{
$phone_number = $request->get('phone_number');
if (User::where('phone_number', $phone_number)->exists()) {
$user = User::where('phone_number', $phone_number)->first();
$user->injectToken();
return $this->response->item($user, new UserTransformer);
} else {
return $this->response->error('Not Found Phone Number', 404);
}
}
And injectToken() method on User Model is :
public function injectToken ()
{
$this->token = JWTAuth::fromUser($this);
return $this;
}
Token creation works fine.
But When I send it to a protected Endpoint, always Unable to authenticate with invalid token occures.
The protected Endpoint action method is :
public function verifyCode (Request $request)
{
$phone_number = $request->get('phone_number');
$user_code = $request->get('user_code');
$user = User::wherePhoneNumber($phone_number)->first();
if ($user) {
$lastCode = $user->codes()->latest()->first();
if (Carbon::now() > $lastCode->expire_time) {
return $this->response->error('Code Is Expired', 500);
} else {
$code = $lastCode->code;
if ($user_code == $code) {
$user->update(['status' => true]);
return ['success' => true];
} else {
return $this->response->error('Wrong Code', 500);
}
}
} else {
return $this->response->error('User Not Found', 404);
}
}
I used PostMan as API client and send generated tokens as a header like this :
Authorization:Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI5ODkxMzk2MTYyNDYiLCJpc3MiOiJodHRwOlwvXC9hcGkucGFycy1hcHAuZGV2XC92MVwvYXV0aFwvY2hlY2tQaG9uZSIsImlhdCI6MTQ3NzEyMTI0MCwiZXhwIjoxNDc3MTI0ODQwLCJuYmYiOjE0NzcxMjEyNDAsImp0aSI6IjNiMjJlMjUxMTk4NzZmMzdjYWE5OThhM2JiZWI2YWM2In0.EEj32BoH0URg2Drwc22_CU8ll--puQT3Q1NNHC0LWW4
I Can not find solution after many search on the web and related repositories.
What is Problem in your opinion?
Update :
I found that not found error is for constructor of loginController that laravel offers :
public function __construct ()
{
$this->middleware('guest', ['except' => 'logout']);
}
because when I commented $this->middleware('guest', ['except' => 'logout']); all things worked.
But if I remove this line is correct?
How should be this line for APIs?
updating my config/api.php to this did the trick
// config/api.php
...
'auth' => [
'jwt' => 'Dingo\Api\Auth\Provider\JWT'
],
...
As I mentioned earlier as an Update note problem was that I used checkPhone and verifyCode in LoginController that has a check for guest in it's constructor.
And because guest middleware refers to \App\Http\Middleware\RedirectIfAuthenticated::class and that redirects logged in user to a /home directory and I did not created that, so 404 error occured.
Now just I moved those methods to a UserController without any middleware in it's constructor.
Always worth reading through the source to see whats happening. Answer: The is expecting the identifier of the auth provider in order to retrieve the user.
/**
* Authenticate request with a JWT.
*
* #param \Illuminate\Http\Request $request
* #param \Dingo\Api\Routing\Route $route
*
* #return mixed
*/
public function authenticate(Request $request, Route $route)
{
$token = $this->getToken($request);
try {
if (! $user = $this->auth->setToken($token)->authenticate()) {
throw new UnauthorizedHttpException('JWTAuth', 'Unable to authenticate with invalid token.');
}
} catch (JWTException $exception) {
throw new UnauthorizedHttpException('JWTAuth', $exception->getMessage(), $exception);
}
return $user;
}

Categories