I am creating an api using Laravel 4.1. I am having problem with authentication and custom errors. I want to check first if the user is authenticated and then show error message. For example localhost:8080/trips/1 is not a valid a resource; if I go to that url it giving me 404 not found error even though I am not authenticated. My point is to check the authentication first then the errors. I am using laravel http basic authentication. Here is my filter code:
Route::filter('api.auth', function()
{
if (!Request::getUser())
{
App::abort(401, 'A valid API key is required');
}
$user = User::where('api_key', '=', Request::getUser())->first();
if (!$user)
{
App::abort(401);
}
Auth::login($user);
});
Here is my custom errors:
App::error(function(Symfony\Component\HttpKernel\Exception\HttpException $e, $code)
{
$headers = $e->getHeaders();
switch ($code)
{
case 401:
$default_message = 'Invalid API key';
$headers['WWW-Authenticate'] = 'Basic realm="REST API"';
break;
case 403:
$default_message = 'Insufficient privileges to perform this action';
break;
case 404:
$default_message = 'The requested resource was not found';
break;
default:
$default_message = 'An error was encountered';
}
return Response::json(array(
'error' => $e->getMessage() ?: $default_message,
), $code, $headers);
});
Here is my routes:
Route::group(array('before' => 'api.auth'), function()
{
Route::resource('trips', 'TripController', array(
'except' => array('create', 'edit')
));
});
The error code is executing before the filters thats why I am getting 404 error instead of getting 401. Is there any way to execute filter first then the error ?
I got this issue too and the way I worked around it (though I was not a fan at all) was doing the following inside the group(s) that don't contain a route sending requests to a controller's index method:
Route::any('/', function(){});
Granted, this shouldn't be how it works, the filter should trigger no matter what.
Related
My API has been built with Laravel 5.4 Passport, authorisation and issuing access tokens is working properly, but when dealing with a resource like below using Insomnia or Postman:
Authorization: Bearer [encrypted access token goes here which has only "manage-users" scope]
Content-Type: application/json
Accept: application/json
I send above request to this url:
http://apiendpoint.loc/api/users
which has been restricted access to this recourse to tokens which has this scopes
manage-users, test-scope-1
Route::get('/users', [
'as' => 'users',
'uses' => 'UsersController#index'
])->middleware(['auth:api', 'scopes:manage-users,test-scope-1']);
scopes have been defined in the:
AuthServiceProvider
Passport::tokensCan([
'manage-users' => 'testing',
'test-scope-1' => 'test scope',
'test-scope-2' => 'another test scope',
]);
protected $routeMiddleware = [
...,
...,
...,
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class
];
The token used to authorize this request has "manage-users" scope only, so I expected to get json response with unauthorized access 401 in addition to the required scope to access this resource which is "test-scope-1".
although I got a HttpException "Invalid scope(s) provided." as HTML response not json
Edit
Auth-Scaffolding is not installed.
After a lot of digging, I found a way to to work around the issue earlier in the exception handler like below:
public function render($request, Exception $exception)
{
// If the request wants JSON (AJAX doesn't always want JSON)
if ($request->wantsJson()) {
if($exception instanceof MissingScopeException){
// Define the response
$response = [
'errors' => 'Sorry, something went wrong.'
];
// If the app is in debug mode
if (config('app.debug')) {
// Add the exception class name, message and stack trace to response
//$response['exception'] = get_class($exception); // Reflection might be better here
$response['message'] = $exception->getMessage();
//$response['trace'] = $exception->getTrace();
}
// Default response of 401
$status = 403;//forbidden
// If this exception is an instance of HttpException
if ($this->isHttpException($exception)) {
// Grab the HTTP status code from the Exception
$status = $exception->getStatusCode();
}
// Return a JSON response with the response array and status code
return response()->json($response, $status);
}
}
return parent::render($request, $exception);
}
so I'll be able to catch the error early and return a json object as a response.
I am building a REST API with Laravel, and I have a filter that checks for a TOKEN:
Route::filter('api.auth', function() {
$token = Request::header('X-CSRF-Token') ? Request::header('X-CSRF-Token') : '';
if (empty($token)) {
return Response::json(
['message' => 'A valid API key is required!'],
401
);
};
$user = User::where('token', '=', $token);
if ($user->count()) {
$user = $user->first();
Auth::login($user);
} else {
return Response::json(
['message' => 'Your token has expired!'],
401
);
};
});
If everything is ok, the filter will log in the user with uth::login($user);
How can I log him for only 1 request?
Since this filter is going to be checked on every request, I think it would be better to log the user out each time.
I have seen this in Laravel's docs, not sure how to apply it:
if (Auth::once($credentials))
{
//
}
Could I have a callback in my response? where I could log the user out?
/*
Get all products.
*/
public function getProducts() {
$products = Auth::user()->products;
return Response::json($products, 200);
}
Any ideas?
If you haven't user's password use this:
if(Auth::onceUsingId($userId)) {
// do something here
}
If I correctly understand the question then I would say that, just replace following
Auth::login($user);
with this (To log the user in only for current request):
Auth::once(['email' => $user->email, 'password' => $user->password]);
If you log in a user only for once then you don't have to manually logo out the user, the user will be asked again for to log in on next request.
I am creating a facebook application in Laravel 4, the problem is it is giving me following error while running as a facebook application
Symfony \ Component \ HttpKernel \ Exception \ NotFoundHttpException
but the same thing is working fine out of facebook.I followed this tutorial
http://maxoffsky.com/code-blog/integrating-facebook-login-into-laravel-application/
Following is my routes.php
Route::get('home', 'HomeController#showWelcome');
Route::get('/', function() {
$facebook = new Facebook(Config::get('facebook'));
$params = array(
'redirect_uri' => url('/login/fb/callback'),
'scope' => 'email,publish_stream',
);
return Redirect::to($facebook->getLoginUrl($params));
});
Route::get('login/fb/callback', function() {
$code = Input::get('code');
if (strlen($code) == 0) return Redirect::to('/')->with('message', 'There was an error communicating with Facebook');
$facebook = new Facebook(Config::get('facebook'));
$uid = $facebook->getUser();
if ($uid == 0) return Redirect::to('/')->with('message', 'There was an error');
$me = $facebook->api('/me');
return Redirect::to('home')->with('user', $me);
});
Edit: I have checked chrome console and getting this error
Refused to display 'https://www.facebook.com/dialog/oauth?client_id=327652603940310&redirect_ur…7736c22f906b948d7eddc6a2ad0&sdk=php-sdk-3.2.3&scope=email%2Cpublish_stream' in a frame because it set 'X-Frame-Options' to 'DENY'.
Put this somewhere inside bootstrap/start.php:
$app->forgetMiddleware('Illuminate\Http\FrameGuard');
You can read this post:
http://forumsarchive.laravel.io/viewtopic.php?pid=65620
Try changing your callback to be Route::post not Route::get. If my memory serves me correctly, Facebook places a POST request, not a GET request.
<?php
Route::get('login/fb/callback', function() {
$code = Input::get('code');
if (strlen($code) == 0) {
return Redirect::to('/')->with('message', 'There was an error communicating with Facebook');
}
$facebook = new Facebook(Config::get('facebook'));
$uid = $facebook->getUser();
if ($uid == 0) {
return Redirect::to('/')->with('message', 'There was an error');
}
$me = $facebook->api('/me');
return Redirect::to('home')->with('user', $me);
});
After looking at the link that you sent, the error actually tells you what is wrong.
REQUEST_URI /
REQUEST_METHOD POST
Loading that page is making a POST request to /, and like I suggested above, you'll need to change the route for the index to Route::post.
I am able to use the Event::Override function successfully with the 404 event but not the 500 event. I simply wish the event to redirect to the front page with a flash message, as I am able to do fine with 404 events.
Is there something else I need to do to get the 500 events also redirecting to my front page? see code below in routes.php file:
Event::listen('404', function() {
return Response::error('404');
});
Event::listen('500', function() {
return Response::error('500');
});
Event::override('404', function() {
Session::flash('error', '<strong>Error 404 - Not Found.</strong> The page you are looking for is either <u>invalid</u> or <u>may no longer exist</u>. You are now back at our home page.');
return Redirect::to_route('home');
});
Event::override('500', function() {
Session::flash('error', '<strong>Error 500 - Internal Server Error.</strong> Something went wrong on our servers, apologies for that. Please contact us if this persists.');
return Redirect::to_route('home');
});
any ideas?
I've been able to work around this by updating the 500 error file directly in the \application\views\error folder.
One of my mobile application need a error as json format. I got it's solution from laravel forum . For catching http error
App::error( function(Symfony\Component\HttpKernel\Exception\HttpException $exception) {
$code = $exception->getStatusCode();
// you can define custome message if you need
return Response::json( array(
'message' => $exception->getMessage() ,
'code' => $code ,
) , $code);
});
Catch unhandled like db error, ..
App::error(function(Exception $exception , $code )
{
return Response::json( array(
'message' => $exception->getMessage() ,
'code' => $code ,
), $code );
});
I'm new to REST API and CURL and I've been referring to http://net.tutsplus.com/tutorials/php/working-with-restful-services-in-codeigniter-2/
If I change the error code from 403 to 200 I get the output:
'Error occured'.
If I leave the errror code as 403 I get output:
'Something has gone wrong'.
From doing some reading it seems I'm correct to give an error code of some sort but how do I pass back some more details on the error? How should I be responding?
My API
public function login_put() {
$valid = $this->membership_model->validate_username($this->put('username'));
if($valid !== TRUE){
$this->response(array('status' => 'error', 'msg' => 'error_details'), 403);
}
}
MY CURL tester
function curl_put()
{
$this->load->library('curl');
$this->curl->create('http://localhost/api/login/format/json');
$this->curl->put(array(
'username' => 'my_username'
));
$result = json_decode($this->curl->execute());
if( isset($result->status) && $result->status == 'error' ) {
echo 'Error occured';
} else {
echo 'Something has gone wrong';
}
}
Looks like you're missing a reference in your curl url. If the file where login_put is called login.php, your url should look like:
http://localhost/api/login/login/format/json
First, we have your domain, then the reference to the API. The first login referrs to the controller within the api folder and the second refers to the function name you defined (login_put)