I have just recently started using Sentry on my Laravel 5.1 application.
I see in there docs, and getting started stuff, a reference to capturing user information.
The example they give of passing this in, looks like this:
Raven.setUserContext({
email: 'foo#example.com'
});
Having followed their setup instructions for Laravel, I see no reference for where this could go or any reference in the documentation for how to set this up in a config file or anything for Laravel.
Any ideas on how I could set this up to send user information? Users will always be logged in when using my application.
I realise this question is three months old, so apologies if you’ve already found the answer.
I had the same requirements (log user as part of exceptions in Sentry) so did a little digging myself.
Raven.setUserContext() seems to be a function specific to the JavaScript SDK. The PHP SDK has a set_user_data($id, $email, $data) method on the Raven client that you can use somewhere in your application (most likely in your exception handler before you actually send the exception to Sentry).
Something like this should work:
public function report(Exception $e)
{
// Will only enter if statement if request has a user
if ($user = request()->user()) {
app('sentry')->set_user_data($user->getAuthIdentifier(), $user->email);
}
app('sentry')->captureException($e);
return parent::report($e);
}
Related
I've been using Laravel from command prompt in Windows 10, but the difficulty of switching between projects has made me switch to using Homestead. However, in a new project I have started there, I can't for the life of me get cookies to persist.
Here is my current code (for debugging this problem only):
use Illuminate\Support\Facades\Cookie;
// ......
public function __construct(Request $request) {
$customer_id = Cookie::get('customer_id');
if(!$customer_id) {
Cookie::queue('customer_id', time(), 3600);
}
dd($customer_id);
}
Expected output: On consecutive page loads, the visitor will see the same unix timestamp they initially opened the page at (I understand this is not a good way of handling it, again, this is just for reproducing the error.)
Reality: Every pageload will produce a different timestamp.
I've looked up as many discussions as I could find. Solutions that I tried:
Using the Route method of declaring cookies
Using good-old PHP setcookie
Using Cookie:make, and Cookie:forever
Adding 'customer_id' in the exceptions among EncryptCookies
Placing the route in the web middleware
Erasing php artisan cache, restarting vagrant
Making the session folder editable through chmod
Yet still, after applying all the above, the cookie is still gone after every page load.
Since I had no prior problem like this through Xampp's PHP, I have to assume there is a (hopefully) trivial and obvious problem with Vagrant that I don't yet know. Any advice is appreciated!
Queued cookies are only sent with responses, so be sure that your controller function does return one.
use Illuminate\Support\Facades\Cookie;
// ......
public function __construct(Request $request) {
$customer_id = Cookie::get('customer_id');
if(!$customer_id) {
Cookie::queue('customer_id', time(), 3600);
}
}
public function foo() {
...
return response('some text');
}
Also, if using some kind of api you have to add a middleware to include the cookies on the response. See Laravel 5.4 - Cookie Queue
I am using a rest api to store/retrieve my data which is stored in a postgres database. The api is not laravel, its an external service!
Now i want to create a website with laravel (framework version 7.3.0) and i'm stuck on how to implement the api calls correctly.
For example: i want to have a custom user provider with which users can log-in on the website. But the validation of the provided credentials is done by the api not by laravel.
How do i do that?
Just make a Registration controller and a Login Controller by "php artisan make:controller ControllerName" and write Authentication logics there.
In previous versions of Laravel you had a command like "php artisan make:auth" that will make everything needed to do these operations. But in Laravel 7.0 you need to install a package called laravel/ui.
Run "composer required laravel/ui" to install that package
Then run "php artisan ui bootstrap --auth"
and now, you are able to run "php artisan make:auth"
This command will make whole Registration (Signup) and Login system for you.
and in orer to work with REST, you may need to know REST (Http) verbs. Learn about GET, POST, PUT, PATH, DELETE requests and how to make those request with PHP and Laravel collection methods. Learn about JSON parsing, encoding, and decoding. Then you can work with REST easily. and work without any template codes from other packages.
Thank you so much. I hope this answer give you some new information/thought. Thanks again.
Edit:
This might not be the best way. But this is what I did at that time. I tried curl and guzzle to build the request with session cookie and everything in the header to make it look like a request from a web browser. Couldn't make it work.
I used the web socket's channel id for the browser I want the changes to happen and concatenated it with the other things, then encrypted it with encrypt($string). After that, I used the encrypted string to generate a QR code.
Mobile app (which was already logged in as an authenticated used) scanned it and made a post request with that QR string and other data. Passport took care of the authentication part of this request. After decrypting the QR string I had the web socket's channel id.
Then I broadcasted in that channel with proper event and data. Caught that broadcast in the browser and reloaded that page with JavaScript.
/*... processing other data ...*/
$broadcastService = new BroadcastService();
$broadcastService->trigger($channelId, $eventName, encrypt($$data));
/*... returned response to the mobile app...*/
My BroadcastService :
namespace App\Services;
use Illuminate\Support\Facades\Log;
use Pusher\Pusher;
use Pusher\PusherException;
class BroadcastService {
public $broadcast = null;
public function __construct() {
$config = config('broadcasting.connections.pusher');
try {
$this->broadcast = new Pusher($config['key'], $config['secret'], $config['app_id'], $config['options']);
} catch (PusherException $e) {
Log::info($e->getMessage());
}
}
public function trigger($channel, $event, $data) {
$this->broadcast->trigger($channel, $event, $data);
}
}
In my view :
<script src="{{asset('assets/js/pusher.js')}}"></script>
<script src="{{asset('assets/js/app.js')}}" ></script>
<script>
<?php
use Illuminate\Support\Facades\Cookie;
$channel = 'Channel id';
?>
Echo.channel('{{$channel}}')
.listen('.myEvent' , data => {
// processing data
window.location.reload();
});
</script>
I used Laravel Echo for this.
Again this is not the best way to do it. This is something that just worked for me for that particular feature.
There may be a lot of better ways to do it. If someone knows a better approach, please let me know.
As of my understanding, you are want to implement user creation and authentication over REST. And then retrieve data from the database. Correct me if I'm wrong.
And I'm guessing you already know how to communicate over API using token. You are just stuck with how to implement it with laravel.
You can use Laravel Passport for the authentication part. It has really good documentation.
Also, make use of this medium article. It will help you to go over the step by step process.
I'm trying to add login with social media
I'm using Laravel 7 and socialite for login.
It was working at first but after the first time I'm getting following error:
Laravel\Socialite\Two\InvalidStateException
vendor/laravel/socialite/src/Two/AbstractProvider.php:210
I have read this question Laravel Socialite: InvalidStateException but I couldn't solve the problem.
I would appreciate any help
In my case the same error was caused by adding a reserved word parameter "state" to the ->with() method. I did:
return Socialite::driver('twitch')
->with(['state' => 'randomstate'])
->redirect();
Removing that helped.
I had the same issue, but only when running the application (Laravel 8) on the production host.
The problem was the enforcement of a restrictive Resource Isolation Policy from the provider, which discarded all input parameters for requests having a sec-fetch-site:cross-site header (which is the case of an OAuth2 redirect).
To verify if this is your case, it is enough to check the contents of the Request coming to your endpoint function: if the following code prints an empty array, you probably have to check your web server configurations.
public function socialCallback(Request $request) {
// Those two lines are just for debug, to be removed
print_r($request->all());
exit();
Socialite::driver('google')->user();
}
Here, more informations about sec-fetch-site.
The Problem in a Nutshell
I'm looking for a way to remove VerifyCsrfToken from the global middleware pipeline from within a package without the user having to modify App\Http\Middleware\VerifyCsrfToken. Is this possible?
The Use Case
I'm developing a package that would make it easy to securely add push-to-deploy functionality to any Laravel project. I'm starting with Github. Github uses webhooks to notify 3rd party apps about events, such as pushes or releases. In other words, I would register a URL like http://myapp.com/deploy at Github, and Github will send a POST request to that URL with a payload containing details about the event whenever it happens, and I could use that event to trigger a new deployment. Obviously, I don't want to trigger a deployment on the off chance that some random (or perhaps malicious) agent other than the Github service hits that URL. As such, Github has a process for securing your webhooks. This involves registering a secret key with Github that they will use to send a special, securely hashed header along with the request that you can use to verify it.
My approach to making this secure involves:
Random Unique URL/Route and Secret Key
First, I automatically generate two random, unique strings, that are stored in the .env file and used to create a secret key route within my app. In the .env file this looks like:
AUTODEPLOY_SECRET=BHBfCiC0bjIDCAGH2I54JACwKNrC2dqn
AUTODEPLOY_ROUTE=UG2Yu8QzHY6KbxvLNxcRs0HVy9lQnKsx
The config for this package creates two keys, auto-deploy.secret and auto-deploy.route that I can access when registering the route so that it never gets published in any repo:
Route::post(config('auto-deploy.route'),'MyController#index');
I can then go to Github and register my webook like this:
In this way, both the deployment URL and the key used to authenticate the request will remain secret, and prevent a malicious agent from triggering random deployments on the site.
Global Middleware for Authenticating Webhook Requests
The next part of the approach involves creating a piece of global middleware for the Laravel app that would catch and authenticate the webhook requests. I am able to make sure that my middleware gets executed near the beginning of the queue by using an approach demonstrated in this Laracasts discussion thread. In the ServiceProvider for my package, I can prepend a new global middleware class as follows:
public function boot(Illuminate\Contracts\Http\Kernel $kernel)
{
// register the middleware
$kernel->prependMiddleware(Middleware\VerifyWebhookRequest::class);
// load my route
include __DIR__.'/routes.php';
}
My Route looks like:
Route::post(
config('auto-deploy.route'), [
'as' => 'autodeployroute',
'uses' => 'MyPackage\AutoDeploy\Controllers\DeployController#index',
]
);
And then my middleware would implement a handle() method that looks something like:
public function handle($request, Closure $next)
{
if ($request->path() === config('auto-deploy.route')) {
if ($request->secure()) {
// handle authenticating webhook request
if (/* webhook request is authentic */) {
// continue on to controller
return $next($request);
} else {
// abort if not authenticated
abort(403);
}
} else {
// request NOT submitted via HTTPS
abort(403);
}
}
// Passthrough if it's not our secret route
return $next($request);
}
This function works right up until the continue on to controller bit.
The Problem in Detail
Of course the problem here is that since this is a POST request, and there is no session() and no way to get a CSRF token in advance, the global VerifyCsrfToken middleware generates a TokenMismatchException and aborts. I have read through numerous forum threads, and dug through the source code, but I can't find any clean and easy way to disable the VerifyCsrfToken middleware for this one request. I have tried several workarounds, but I don't like them for various reasons.
Workaround Attempt #1: Have user modify VerifyCsrfToken middleware
The documented and supported method for solving this problem is to add the URL to the $except array in the App\Http\Middleware\VerifyCsrfToken class, e.g.
// The URIs that should be excluded from CSRF verification
protected $except = [
'UG2Yu8QzHY6KbxvLNxcRs0HVy9lQnKsx',
];
The problem with this, obviously, is that when this code gets checked into the repo, it will be visible to anyone who happens to look. To get around this I tried:
protected $except = [
config('auto-deploy.route'),
];
But PHP didn't like it. I also tried using the route name here:
protected $except = [
'autodeployroute',
];
But this doesn't work either. It has to be the actual URL. The thing that actually does work is to override the constructor:
protected $except = [];
public function __construct(\Illuminate\Contracts\Encryption\Encrypter $encrypter)
{
parent::__construct($encrypter);
$this->except[] = config('auto-deploy.route');
}
But this would have to be part of the installation instructions, and would be an unusual install step for a Laravel package. I have a feeling this is the solution I'll end up adopting, as I guess it's not really that difficult to ask users to do this. And it has the upside of at least possibly making them conscious that the package they're about to install circumvents some of Laravel's built in security.
Workaround Attempt #2: catch the TokenMismatchException
The next thing I tried was to see if I could just catch the exception, then ignore it and move on, i.e.:
public function handle($request, Closure $next)
{
if ($request->secure() && $request->path() === config('auto-deploy.route')) {
if ($request->secure()) {
// handle authenticating webhook request
if (/* webhook request is authentic */) {
// try to continue on to controller
try {
// this will eventually trigger the CSRF verification
$response = $next($request);
} catch (TokenMismatchException $e) {
// but, maybe we can just ignore it and move on...
return $response;
}
} else {
// abort if not authenticated
abort(403);
}
} else {
// request NOT submitted via HTTPS
abort(403);
}
}
// Passthrough if it's not our secret route
return $next($request);
}
Yeah, go ahead and laugh at me now. Silly wabbit, that's not how try/catch works! Of course $response is undefined within the catch block. And If I try doing $next($request) in the catch block, it just bangs up against the TokenMismatchException again.
Workaround Attempt #3: Run ALL of my code in the middleware
Of course, I could just forget about using a Controller for the deploy logic and trigger everything from the middleware's handle() method. The request lifecycle would end there, and I would never let the rest of the middleware propagate. I can't help feeling that there's something inelegant about that, and that it departs from the overall design patterns upon which Laravel is built so much that it would end up making maintenance and collaboration difficult moving forward. At least I know it would work.
Workaround Attempt #4: Modify the Pipeline
Philip Brown has an excellent tutorial describing the Pipeline pattern and how it gets implemented in Laravel. Laravel's middleware uses this pattern. I thought maybe, just maybe, there was a way to get access to the Pipeline object that queues up the middleware packages, loop through them, and remove the CSRF one for my route. Best I can tell, there are ways to add new elements to the pipeline, but no way to find out what's in it or to modify it in any way. If you know of a way, please let me know!!!
Workaround Attempt #5: Use the WithoutMiddleware trait
I haven't investigated this one quite as thoroughly, yet, but it appears that this trait was added recently to allow testing routes without having to worry about middleware. It's clearly NOT meant for production, and disabling the middleware would mean that I'd have to come up with a whole new solution for figuring out how to get my package to do its thing. I decided this was not the way to go.
Workaround Attempt #6: Give up. Just use Forge or Envoyer
Why reinvent the wheel? Why not just pay for one or both of these service that already supports push-to-deploy rather than go to the trouble of rolling my own package? Well, for one, I only pay $5/month for my server, so somehow the economics of paying another $5 or $10 per month for one of these services doesn't feel right. I'm a teacher who builds apps to support my teaching. None of them generate revenue, and although I could probably afford it, this kinda thing adds up over time.
Discussion
Okay, so I've spent the better part of two solid days banging my head against this problem, which is what brought me here looking for help. Do you have a solution? If you've read this far, perhaps you'll indulge a couple of closing thoughts.
Thought #1: Bravo to the Laravel guys for taking security seriously!
I'm really impressed with how difficult it is to write a package that circumvents the built-in security mechanisms. I'm not talking about "circumvention" in the I'm-trying-to-do-something-bad way, but in the sense that I'm trying to write a legitimate package that would save me and lots of other people time, but would, in effect, be asking them to "trust me" with the security of their applications by potentially opening them up to malicious deployment triggers. This should be tough to get right, and it is.
Thought #2: Maybe I shouldn't be doing this
Frequently if something is hard or impossible to implement in code, that is by design. Maybe it's Bad Design™ on my part to want to automate the entire installation process for this package. Maybe this is the code telling me, "Don't do that!" What do you think?
In summary, here are two questions:
Do you know a way to do this that I haven't thought of?
Is this bad design? Should I not do it?
Thanks for reading, and thank you for your thoughtful answers.
P.S. Before someone says it, I know this might be a duplicate, but I provided much more detail than the other poster, and he never found a solution, either.
I know it is not good practice to use the Reflection API in production code, but this is the only solution i could think of where no additional configuration is needed. This is more like a proof of concept and I would not use it in production code.
I think a better and more stable solution is to have the user update his middleware to work with your package.
tl;dr - you can place this in your packages boot code:
// Just remove CSRF middleware when we hit the deploy route
if(request()->is(config('auto-deploy.route')))
{
// Create a reflection object of the app instance
$appReflector = new ReflectionObject(app());
// When dumping the App instance, it turns out that the
// global middleware is registered at:
// Application
// -> instances
// -> Illuminate\Contracts\Http\Kernel
// -> ... Somewhere in the 'middleware' array
//
// The 'instance' property of the App object is not accessible
// by default, so we have to make it accessible in order to
// get and set its value.
$instancesProperty = $appReflector->getProperty('instances');
$instancesProperty->setAccessible(true);
$instances = $instancesProperty->getValue(app());
$kernel = $instances['Illuminate\Contracts\Http\Kernel'];
// Now we got the Kernel instance.
// Again, we have to set the accessibility of the instance.
$kernelReflector = new ReflectionObject($kernel);
$middlewareProperty = $kernelReflector->getProperty('middleware');
$middlewareProperty->setAccessible(true);
$middlewareArray = $middlewareProperty->getValue($kernel);
// The $middlewareArray contains all global middleware.
// We search for the CSRF entry and remove it if it exists.
foreach ($middlewareArray as $i => $middleware)
{
if ($middleware == 'App\Http\Middleware\VerifyCsrfToken')
{
unset($middlewareArray[ $i ]);
break;
}
}
// The last thing we have to do is to update the altered
// middleware array on the Kernel instance.
$middlewareProperty->setValue($kernel, $middlewareArray);
}
I haven't tested this with Laravel 5.1 - for 5.2 it works.
So you could create a Route::group where you can explicitly say which middleware you want to use.
For example in your ServiceProvider you could do something like this:
\Route::group([
'middleware' => ['only-middleware-you-need']
], function () {
require __DIR__ . '/routes.php';
});
So just exclude VerifyCsrfToken middleware, and put what you need.
I have this controller
class APIV1CustomerController extends BaseController{
public function getIndex(){
return "super new look";
}
public function postRegister(){
return "you have entered "+Input::get('fullName')+" as fullName parameter and "+Input::get('mobileNumber')+" as mobileNumber parameter";
}
}
I am trying to call the register function from IOS, I have some problems with the returing data. Thus, I need to debug that function.
when I return just simple test like return "test", it works well from IOS. but when I return the code I showed to you, some problem accrues, I can solve the problem myself from the IOS side but I need to debug the postRegister to check some issues.
when I was using J2EE web service, I can simple print some data to the console in my eClipse that is why I am asking if there is any way to do that in laravel.
Thanks
I am using php storm IDE
This might be useful: https://github.com/mlanin/laravel-api-debugger
It's a laravel package specifically built for assisting with APIs.
this might be irrelevant, but i learned a lot from this article when debugging api and jquery back then. http://www.openlogic.com/wazi/bid/188084/jQuery-and-Ajax-Error-Detection-and-Handling.
the first step is to see if there's any functions in your IOS which will notify you when you have an error after the http call, so that you know you actually are getting an error, ex. 404, 500 etc. Because if you are getting 200, most likely you don't need more advanced debug tool other than print.
server log is another friend which is very helpful in these white screen case, it'll tell you the exact error when 500 error happens. Ex. last error I got on my server was telling me a class name is not found, although it runs fine in my local.