Laravel - Mixed Content just on post request - laravel

Message error:
Mixed Content: The page at 'https://example/contracts/create' was
loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint
'http://example:443/api/v1/contracts'. This request has been blocked;
the content must be served over HTTPS.
I saw some similar posts, but till now it didn't work.(my .env is with https, and this is not local)
I got this error just on this post, the rest of them are with https, and only when I have error validation.
On local it's working fine.
Route::middleware('auth:api')->prefix('contracts')->group(function () {
Route::get('{contract?}', 'ContractsController#getContracts');} //in api.php
post("/contracts/" + `${this.customer_id}`, newContract) // in VueJs

You can add to app\Provider\AppServiceProvider
/**
* Register any application services.
*
* #return void
*/
public function register()
{
if (!app()->isLocal()) {
URL::forceScheme('https');
}
}

Related

Laravel - Log Guzzle requests to file

While working on a project I found third party API's are working from Postman, but doesn't work from Guzzle Client.
Debugging a Guzzle request could be difficult, so is there any way that can log all requests made by Guzzle client can be seen?
TLDR;
There’s an easy way to log all Guzzle requests by passing second parameter to Client and then it will log all the requests. But that’s ugly way if you have many methods using Guzzle Client to send request to third party server. I’ve done it using Laravel’s Service Container.
Long way via Laravel’s Service Container
When I used Guzzle client in my project and used handler to log all requests it looks good. But later on there were many methods in many different classes so I have to write logger logic every where. Then I thought why don’t to leverage Laravel’s Service Container and bind an object once and use it everywhere.
Here’s how I did it. In your AppServiceContainer.php’s boot method we will add all our code. And then in Controllers we will use our Client object.
Add this use statements on top of the AppServiceContainer.php file.
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\MessageFormatter;
use GuzzleHttp\Middleware;
use Illuminate\Support\ServiceProvider;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
Add below code to your AppServiceContainer.php’s boot method
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
$this->app->bind('GuzzleClient', function () {
$messageFormats = [
'REQUEST: {method} - {uri} - HTTP/{version} - {req_headers} - {req_body}',
'RESPONSE: {code} - {res_body}',
];
$stack = HandlerStack::create();
collect($messageFormats)->each(function ($messageFormat) use ($stack) {
// We'll use unshift instead of push, to add the middleware to the bottom of the stack, not the top
$stack->unshift(
Middleware::log(
with(new Logger('guzzle-log'))->pushHandler(
new RotatingFileHandler(storage_path('logs/guzzle-log.log'))
),
new MessageFormatter($messageFormat)
)
);
});
return function ($config) use ($stack){
return new Client(array_merge($config, ['handler' => $stack]));
};
});
}
Explanation
If you have noticed above code, In first line of boot method we are telling Laravel that we want to register this code as a GuzzleClient in your Service Container.
In last return statement we are returning a function that will accept one argument $config. We used this function as a proxy so that we can pass an argument to it and that can be used in Client Object.
return function ($config) use ($stack){
return new Client(array_merge($config, ['handler' => $stack]));
};
Rest of the code is building Guzzle’s handler object to Log all requests to a file called guzzle-log.log using Logger object of Monolog library. If you have daily logs enabled, a date will be appended to file name like guzzle-log-2019-08-11.log.
Usage
We have binded our object to Service Container, now it’s time to use this container everywhere in our code, and make it looks clean.
For demo purpose I’ve used it directly in routes/web.php file. You can use anywhere.
Route::get('/', function () {
$client = app('GuzzleClient')(['base_uri' => 'http://httpbin.org/']);
$request = $client->get('get',[
'query' => ['foo'=>'bar', 'baz' => 'baz2'] ,
'headers' => [ 'accept' => 'application/json']
]);
$response = json_decode((string) $request->getBody());
return response()->json($response);
});
As you can see I’m making an object $client using app() helper. Also you can pass any valid arguments array that Guzzle client supports as a second parameter. Here I’ve passed base_uri.
Source: http://shyammakwana.me/laravel/laravel-log-guzzle-requests-to-file-using-service-container.html
The accepted answer works nicely by using Service Providers. Another option would be to attach GuzzleHttp\Middleware's log to wherever you use Illuminate\Support\Facades\Http. An example which logs the request, the response, and any errors if found would be to use Middleware::log:
<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
use GuzzleHttp\MessageFormatter;
use GuzzleHttp\Middleware;
/**
* Class TestService
* #package App
*/
class TestService
{
private function getAccessToken()
{
try {
$response = Http::asForm()->withMiddleware(Middleware::log(with(new Logger('guzzle-log'))->pushHandler(
new RotatingFileHandler(storage_path('logs/guzzle-log.log'))
), new MessageFormatter(MessageFormatter::DEBUG)))->post("https://test.com/oauth/v2/token", [
'grant_type' => 'client_credentials',
]);
$response->throw();
} catch (\Throwable $th) {
$accessToken = false;
}
return $accessToken;
}
}
This will write log records to logs/guzzle-log-{currentDate}.log file. The format of the log record I used in this example is MessageFormatter::DEBUG which is ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}" which nicely outputs the request, response, and any errors. an example of a log file would be:
[2020-08-07T07:13:23.712124+00:00] guzzle-log.INFO: >>>>>>>> POST /oauth/v2/token HTTP/1.1 Content-Length: 29 User-Agent: GuzzleHttp/7 Host: xxxx:4000 Content-Type: application/x-www-form-urlencoded grant_type=client_credentials <<<<<<<< HTTP/1.1 200 OK X-DNS-Prefetch-Control: off X-Frame-Options: SAMEORIGIN Strict-Transport-Security: max-age=15552000; includeSubDomains X-Download-Options: noopen X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block Access-Control-Allow-Origin: * Content-Type: application/json; charset=utf-8 Content-Length: 113 ETag: W/"71-DyA+KEnetTKfUlb0lznokGTt0qk" Date: Fri, 07 Aug 2020 07:13:23 GMT Connection: keep-alive {"data":{"token_type":"Bearer","access_token":"XYZ","expires_in":"7776000"}} -------- NULL [] []
Note: The downside to this option is that you will have to attach withMiddleware(Middleware::log(...)) to anywhere you are using Illuminate\Support\Facades\Http.

request() not secure in Laravel with load balancer and TrustedProxies

Why is request()->isSecure() returning false when visiting a HTTPS-url? I am using Laravel Forge and I have a load balancer. Logging the request I get this data:
request()->url(), // "http://xx.xx"
request()->isSecure(), // false
request()->getClientIps(), // XX.XX.XX.XX
In TrustProxies (which is added in the $middleware array in Http/Kernel.php):
protected $proxies = [
'XX.XX.XX.XX', // exactly the same as in the logged data above
];
In my AppServiceProvider's boot() method:
if (env('APP_ENV') == 'production') {
\URL::forceScheme('https');
}
Edit:
I am also using Cloudflare, and I have added all the Cloudflare proxies to $proxies as well. Check the logs, I see these headers in the request:
X-Forwarded-Proto: http
Cf-Visitor: {"scheme":"https"}
Referer: https://xx.xx/someurl
While You are using Cloudflare You should to use "Full SSL/TLS encryption mode".

OctoberCMS with JWTAuth API authentication plugin- I am getting {"error":"token_not_provided"}

I am authenticating users with JWTAuth OctoberCMS API plugin. When I register user or login with postman, the request return a token. However when I try to access authenticated routes like:
Route::get('api/v1/todos',
'Wafush\Maswali\Controllers\Todos#index')->middleware('\Tymon\JWTAuth\Middleware\GetUserFromToken');
I am getting {"error":"token_not_provided"} exception yet the user is authenticated.
Again when I attempt to return signed in user object like:
$user = JWTAuth::authenticate();
return $user;
I get the following error:
A token is required
C:\xampp\htdocs\myapp\plugins\vdomah\jwtauth\vendor\tymon\jwt-auth\src\JWTAuth.php line 299
Type
Undefined
Exception
Tymon\JWTAuth\Exceptions\JWTException
{
if ($token) {
return $this->setToken($token);
} elseif ($this->token) {
return $this;
} else {
throw new JWTException('A token is required', 400);
}
}
/**
* Set the request instance.
*
What I am missing. Kindly guide. Its like the token is not getting set.
I got to your question because I was facing the same problem and I've just solved it by adding below code to my .htaccess
# Authorization header
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]

Another Laravel Token Mismatch Exception issue

I have an application which uses AJAX quite a bit on a Laravel 5.3 application. I have some pages that are behind authentication and some that are not. The ones that are inside of authentication are working fine. The one that is outside (public facing) are giving me a the infamous TokenMismatchException in VerifyCsrfToken.php line 68. In order to attach the token to the AJAX header, I am using this...
$.ajaxSetup({
cache: false,
async: true,
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
And it is working because when I make a request, I can see this...
...but the tokens are not matching. When I go to the framework file Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class and do a dd() on the session token and the token that was passed, they do not match.
Things I have tried:
Clearing Cache (views, config, cache)
Changing session driver from
file to Redis
Using csrf_field() instead of AJAX headers
I cannot figure out why this is not working. Any other ideas?
If you look at this code of laravel Github Link
/**
* Determine if the session and input CSRF tokens match.
*
* #param \Illuminate\Http\Request $request
* #return bool
*/
protected function tokensMatch($request)
{
$sessionToken = $request->session()->token();
$token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
if (! $token && $header = $request->header('X-XSRF-TOKEN')) {
$token = $this->encrypter->decrypt($header);
}
if (! is_string($sessionToken) || ! is_string($token)) {
return false;
}
return hash_equals($sessionToken, $token);
}
It checks for the X-CSRF-TOKEN and also tries to check for X-XSRF-TOKEN. You can also try to send the _token from the ajax. I hope this helps you.
And, I finally figured it out. I am using BrowserSync for livereload, which proxies all my requests to localhost:3000/*. When I was testing the public side, I was visiting it through the original domain name and not proxied through browsersync's localhost:3000 so that was causing session issues.
Basically, if you have BrowserSync running and you try in use your site not through browsersync, you can get token mismatch errors.

Django Angular Authentication CSRF cached template

I am getting status code 403 when I try to log in after successfully being logged in and logged out.
Client side is written in Angular and server side is in Django.
This goes as follows:
Client requests url '/' fetches main HTML template with all required static files ( angular, bootstrap, jQuery sources and angular sources defined by me) with
<div ng-view></div> tag into which further templates will be inserted.
Via $location service is redirected to url '/#/login'
This rule from $routeProvider is executed once '/#/login' is hit:
$routeProvider.when('/login', {
templateUrl: 'login.html'
});
'login.html' is served by django view and form for logging in is rendered to the user
User logs in successfully providing proper credentials
Then user logs out, by clicking on a button, which fires '$http.get(
'/logout/'
);' and then is redirected to url '/#/login'
Here is the problem. When user fills in credential form and sends 'POST' request, 403 is returned. I thought that it is, because this routing is done only by angular and since 'login.html' template has already been requested it has been catched and can be served without hitting backend, but after logging out currently possesed CSRF cookie is stale, so that's why I am getting 403. So I tried to remove that template:
logout: function(){
var forceLoginTemplateRequest = function(){
if( $templateCache.get('login.html') !== 'undefined'){
$templateCache.remove('login.html');
}
};
var responsePromise = $http.get(
urls.logout
);
responsePromise.success(forceLoginTemplateRequest);
return responsePromise;
}
After doing that I could see client side requesting 'login.html' template always after logging out, so I thought I could provide CSRF cookie when serving that template from backend:
#urls.py
urlpatterns = patterns(
'',
...
url(r'^$', serve_home_template),
url(r'^login.html$', serve_login_template),
url(r'^login/', login_view, name='login'),
url(r'^logout/', logout_view, name='logout'),
...
)
#views.py
#ensure_csrf_cookie
def serve_login_template(request):
return render(request, "login.html")
#ensure_csrf_cookie
def serve_home_template(request):
return render(request, 'home.html')
But still it doesn't work and I am getting 403 when trying to log in after logging out. The only way I managed it to work is to simply refresh the page so that every single file, whether template or source file is requested again from the backend and CSRF cookie is updated with them.
Here is my app's run section for making sure CSRF cookie is sent with every request:
mainModule.run(['$http','$cookies', '$location', '$rootScope', 'AuthService', '$templateCache',
function($http, $cookies, $location, $rootScope, AuthService, $templateCache) {
$http.defaults.headers.common['X-CSRFToken'] = $cookies.csrftoken;
$rootScope.$on( "$routeChangeStart", function(event, next, current) {
if ( !(AuthService.isLoggedIn() == "true")){
$location.path('/login');
}
});
}]);
This could be a cache problem. Try to add the never_cache decorator to all your views:
from django.views.decorators.cache import never_cache
...
#ensure_csrf_cookie
#never_cache
def serve_login_template(request):
return render(request, "login.html")
...
I solved this problem by setting X-CSRFTOKEN header in $routeChangeStart event.
I don't exactly know how module.run phase works, but it seems that when certain event defined within it occurs everything what is defined outside this event's handler body isn't executed.
mainModule.run(['$http','$cookies', '$location', '$rootScope', 'AuthService',
function($http, $cookies, $location, $rootScope, AuthService) {
$http.defaults.headers.common['X-CSRFToken'] = $cookies.csrftoken;
$rootScope.$on( "$routeChangeStart", function(event, next, current) {
// Added this line
$http.defaults.headers.common['X-CSRFToken'] = $cookies.csrftoken;
if ( !(AuthService.isLoggedIn() == "true")){
$location.path('/login');
}
});
}]);
This works together with removing 'login.html' template from $templateCache.
Instead of removing templates on client side with $templateCache service it is also possible to set your server to serve templates and set following headers:
Cache-Control: no-cache, no-store, must-revalidate
Pragma : no-cache
Expires : 0
Another way of dealing with this problem is to simply force page refresh, however I don't like this approach, since this is not pro-single-page-app approach. :)
One solution could be to read the current, fresh csrftoken directly from the cookie and then update the stale cookie using javascript.
var fresh_token = document.cookie.match('csrftoken=([a-zA-Z0-9]{32})

Resources