I'm building an API in Laravel 5.4, using Laravel Passport 3 for authentication. All of my API methods return a set of values that are always returned, success, errors (if there are any errors) etc.
I've changed the response of a \Illuminate\Auth\AuthenticationException throws, to fit with the rest of my app, however I'm not sure how to change the response of various token grant responses, without doing something horrible like editing the vendor files.
I think you can use middleware to change your response.
From laravel documentation:
Before & After Middleware
Whether a middleware runs before or after a
request depends on the middleware itself.
You can capture the response and re-format the response.
You can use laravel's setContent method to set the content in response. Check here.
What you are trying to do here is not supported by the library, so whatever you do will be hacky and will probably break the compatibility with future versions of laravel/passport.
In my opinion, you can only choose between those 2 options:
Instead of declaring passport routes (Passport::routes()) you can declare equivalent routes to your custom methods. Those method internally calls Passport classes and methods, handling passport returning values before returning them to the user. It requires a lot of digging into passport code but, at the same time, if you only add some fields (success or error) you should be able to update your code without too much effort when updating the library.
Fork laravel/passport and modify it to suit you needs. This solution in not as messy as the first, but a merge with new versions of passport in the future will probably be hard.
Of course, both are not great solutions. Keeping the standard passport responses or use a more suitable library are better options: I assume they are not feasible if you are asking.
Another way - create proxy routes for your purposes.
Route::post('custom-auth/token', function (Request $request) {
$proxy = Request::create('oauth/token', 'POST', $request->request->input());
$response = app()->handle($proxy);
return responseCallback($response);
});
Where responseCallback your custom response modificator function.
Related
I am making APIs in Laravel version 8.x for a third party system. For this, I have created few APIs and all routes are mentioned in routes/api.php
In our system, there are few APIs that can be accessible via GET request, that returns data without any authentication and parameters. Look at the routes below in api.php:
//routes/api.php
Route::group(['namespace' => 'App\Http\Controllers\WebServices'], function() {
Route::get('event_types', 'EventWs#getEventTypes');
Route::get('event_type/{event_type_id}', 'EventWs#getEventTypeById');
});
Look at the both routes above. The route having segment is working fine with GET request, but the route without any segment is not calling. This is just a simple GET request without any segment or query string.
I am working on Laravel from past few months, but this issue is strange to me. May be there could be some kind of middleware that prevent GET (without segment or query string) route execution. But I am not able to find reason or that unknown barrier.
Have anyone idea about why this happening or I am missing something?
Please Note that I am using Laravel 8.x
I've been using your advice and View::sharing all of my important data to all views. However, there is one issue I have encountered.
This code:
if(!Auth::guest()){
$user=Auth::user()->id;
}
else $user=0;
$temp=DB::select('query');
View::share('cartnumber', count($temp));
View::share('cartitems', $temp);
doesn't work when put in AppServiceProvider. Or better, it always sets $user=0, even if I am logged in. I thought it is because AppServiceProvider's boot function executes before the site checks if someone is logged in.
I then tried to use a BaseController with a construct function but that doesn't work either. The only solution that seems to work correctly is putting the code in every single Controller for every view! That actually works, which kind of confirms my theory.
But is there anywhere I can put this code without having to copy/paste it in every single Controller? Thanks in advance!
You'd likely want to put this code later in the request life cycle to guarantee an auth user because as others have mentioned middleware/session code has not occured during this part of the framework booting up. You could use a service class to call in all your controllers to avoid the copy pasting. Or If you'd like to achieve this using code in your service provider you could use a View Composer instead of a share this allows you to define a callback/or class that will be called right before the view is returned
view()->composer(['/uri-that-needs-data'], function ($view) {
if (Auth::check()) {
$cart = DB::query(...)->get();
$view->with('cartitems', $cart);
}
});
Check out https://laravel.com/docs/5.7/views#view-composers for more details.
Auth::user() will be empty until the session middleware has run.
The reason you can't access the user inside your service provider is because that code is run during the "bootstrapping" phase of the application lifecycle, when it's doing things like loading filesystem or cache drivers, long before the request is sent through response handlers (including middleware).
Once the application has been bootstrapped and all service providers
have been registered, the Request will be handed off to the router
for dispatching. The router will dispatch the request to a route or
controller, as well as run any route specific middleware.
Source: https://laravel.com/docs/5.7/lifecycle
If you don't want to copy/paste that code everywhere, then one place to put it is in custom route middleware. You can list it after the auth middleware to guarantee a logged-in user.
Edit: View composers are another really good option, as suggested by #surgiie. The reason these can be set up inside a service provider (unlike your example) is because the view composer registers a callback, but doesn't execute it until a much later stage in the application lifecycle.
I'm using Laravel to build and api based admin control application and I'm using
Route:resource instead of regular GET and POST methods.
Just realized my hosting provider DOESN'T ALLOW PUT and DELETE now I need to so now I have to use POST and GET methods.
this is what i have
Route::resource('contacts', 'Admin\\ContactInfoController',['only' => ['create', 'store', 'update']]);
Route::get('claims/statuses', 'Admin\\ClaimsController#statusCodes');
Route::get('claims/costcenters', 'Admin\\ClaimsDetailsController#getCostCentres');
Route::get('claims/{id}/details', 'Admin\\ClaimsController#details');
Route::get('claims/{id}/messages', 'Admin\\ClaimsController#messages');
Route::resource('claims', 'Admin\\ClaimsController',['only' => ['index','store','update','destroy','edit']]);
Route::resource('claims/details', 'Admin\\ClaimsDetailsController',['only' => ['store','update','destroy']]);
What approach might be best in converting my routes from PUT and DELETE to POST and GET?
I don't think it's possible that your hosting provider doesn't allow to put or delete request. If you created API it could be the case but in other cases (you created normal page) you send forms using POST method with hidden field _method set to HTTP verb, so if only your provider supports POST method it will work without a problem. You can read Form method spoofing section about this.
Contact your hosting provided to allow put or delete request, it's part of web development and this is quite limiting.
I was fiddling with sails.js and passport. Seems they are mend to be used together.
I have made the user models available in de db. And I am able to use the authentication by bcryptjs. This works, but dearly want to like to automatically authenticate every call, and mostly socket.io blueprint calls.
Still, I am searching for an elegant method to enable the sails.js way of integrating passport.
There are many examples, but seem a bit out-dated, not sure.
Important is the sockets. They need to be authenticated.
For e.g. every io.socket CRUD method, would be nice.
Kind regards
I found a link (http://www.bearfruit.org/2014/07/21/tutorial-easy-authentication-for-sails-js-apps/) that's pointing me in the right direction, but still not clear how this is reflected in for instance controllers
What I am trying to manage, is to have the custom api calls authenticated before they are called (configured in routes.js or by means of blueprints).
e.g:
OrderController:
module.exports = {
placeOrder: function (req, res) {
if (true === req.isSocket) {
// Pseudo code:
if (passport.authenticated(['user','admin'])) {
Order.save();
}
// end pseudo code
}
}
}
How should/is the above method secured, and I am I able to use for instance user-roles here?
I know two good solution for you question:
sails-auth
sails-generate-auth
Both implements passport, I recommend the first one, because it creates a layer to handle all authentication difficulties in model and services
My current setup is AngularJS + Django 1.5 and I have completely thrown away the use of Django's template engine (ie. the backend is pretty much an API server).
Since I am not using the csrf_token template tag, Django, in turn, does not set and send the csrftoken cookie in response. As instructed by the official docs, the ensure_csrf_cookie() decorator should be used to force the decorated view to send the csrftoken cookie.
I have applied the ensure_csrf_cookie() decorator to the view, which serves the first GET request that my web client calls at bootstrapping. With that, my web client gets a hold of the CSRF token and henceforth is allowed to call unsafe methods (ex. POST) to the server.
The above setup works fine only if the CSRF token remains the same until the browsing session ends.
Question: Does Django's CSRF token get updated during the course of a browsing session? If 'yes', does that mean I would need to apply the ensure_csrf_cookie() decorator to all the views I have?
1) Does Django's CSRF token get updated during the course of a browsing session?
Looks like the CSRF token is unique per session, but it is based in my observations, I have no "official" source. With Angular.js I use the following code without problems:
angular.module('app', ...)
.config(function($httpProvider) {
var cookies = document.cookie.split(';');
var csrftoken = _.find(cookies, function(v) {
return v.trim().indexOf('csrftoken=') == 0;
});
if(csrftoken) {
$httpProvider.defaults.headers.common['X-CSRFToken'] = csrftoken.split('=')[1];
}
})
Since I serve the HTML from Django, by the time Angular bootstraps the cookie is already there.
2) If 'yes', does that mean I would need to apply the ensure_csrf_cookie() decorator to all the views I have?
You can try CORS instead if CSRF. Otto Yiu maintains the django-cors-headers package, which is known to work correctly with REST framework APIs.
Some (untested) ideas to apply ensure_csrf_cookie():
monkey-patch APIView
create a CSRFCookie mixin and add it to your views
apply ensure_csrf_cookie() to your base classes
Giving support to the #Paulo Scardine ideas of applying the ensure_csrf_cookie() (which I consider valid, and useful), I would like to add a new one possible solution to it, if you definitely have to ensure_csrf_cookie() in all your views. You could write a custom middleware, and implement the logic that is there inside the ensure_csrf_cookie. Something like this:
On your app.middleware.py:
from django.middleware.csrf import get_token
class EnsureCsrfCookie(object):
def process_request(self, request):
# Forces process_response to send the cookie
get_token(request)
and of courses on your settings file add the middleware to the MIDDLEWARE_CLASSES:
MIDDLEWARE_CLASSES = (
.,
.,
.,
'app.middleware.EnsureCsrfCookie',
.,
.,
.,
)
It is just one idea more to face this problem. I hope it can be useful for somebody in the future.