Laravel HTTPS URL random chars? - laravel

I have just set up my custom API on my sub-domain, with an SSL certificate using NGINX. Everything works just fine (get requests etc.) however, whenever I try to authenticate I receive a 401 'Invalid credentials'.
When I die and dumped $request->all(); I figured out I suddenly have an extra GET param called 'q'? For https://api.domain.com/api/v1/login?email=test#test.com&password=test My credentials suddenly became:
q => email=test#test.com
password => test
I am absolutely baffled, does anybody have an idea whats going on? This happened when I activated my SSL cert.
I assume q is for query..
Also, here is my routes file:
Route::get('/', function() {
return 'Welcome to API';
});
/* API Version 1 */
Route::group(['prefix' => 'v1', 'middleware' => 'cors'], function() {
Route::post('/login', 'AuthController#authenticate');
Route::group(['middleware' => ['jwt.auth']], function() {
Route::resource('users', 'UsersController', ['only' => ['index', 'show', 'store']]);
Route::resource('rooms', 'RoomsController', ['only' => ['index', 'show']]);
Route::resource('reservations', 'ReservationsController');
Route::resource('customers', 'CustomersController');
Route::get('rooms/{id}/reservations', 'ReservationsController#getReservationsByRoom');
Route::get('users/{id}/reservations', 'ReservationsController#getReservationsByUser');
Route::get('users/{id}/customers', 'CustomersController#getCustomersByUser');
Route::get('reservations/{id}/customer', 'CustomersController#getCustomerByReservation');
Route::get('me', 'UsersController#getAuthenticatedUser');
Route::get('me/reservations', 'ReservationsController#getReservationsByAuthenticatedUser');
Route::get('me/customers', 'CustomersController#getCustomersByAuthenticatedUser');
});
});
TIA

What version of Laravel?
I have not seen this behavior (rewriting parameters); i'd be very surprised if Laravel was in fact the culprit (and a little disappointed).
If you're using Laravel 5.3 (and maybe 5.2, not sure), you can use $request->getContent() to see the entire request object as seen by Laravel. As the comment below suggests, I agree that it's not Laravel and more likely a problem with your config (especially considering it's when you enabled HTTPS).
Laravel does not handle the security layer (https), your web server does, so from Laravel's POV http === https, and it shouldn't care either way.

I've seen in several cases that Laravel Forcing HTTPS with Mod Rewrite so you might want to take a look into that, maybe it would help.

I've found the solution. It was indeed my NGINX configuration, Laravel would not do such a thing. It is best to have at less rewrites as possible. This was in my default setting of my /etc/nginx/sites-available/default:
location / {
try_files $uri $uri/ /index.php?q=$query_string;
}
Removing the q before query string solves of course my problem.
Thanks for all your insights

Related

Laravel Sanctum returns 404 instead of 401 unauthenticated

I am using laravel/sanctum package for authenticating my api. I have followed all the steps from the documentation but I get 404 not found when using an invalid token for hitting a guarded route instead of 401.
Route::group(['middleware' => ['auth:sanctum']], function () {
Route::post("/upload", [AuthController::class, "uploadFile"]);
Route::post('/me', [AuthController::class, 'me']);
});
few steps to follow
clear cache
test.ca/api/upload - make sure you have 'api' in url (from where you are calling)
add 'Accept application/json' in your request header.

Laravel Echo + Websockets + Private Channels

I know I'm not the first struggling with this. But after some days and going trough a lot of related questions i somehow feel that my case deserves it's own question :).
I have a working websocket solutions with Laravel Websockets (https://beyondco.de/docs/laravel-websockets/getting-started/introduction) and Laravel Echo for public channels.
My client application is a vue-cli app and connects to the server + broadcast messages on public channels work great. The authorization is handled by Laravel Passport. So through sending a Bearer token in the Authorization header the backend application knows if the user is authenticated.
However I'm struggling to get Private channels to work. Trying to authenticate always gives me this error:
Access to XMLHttpRequest at 'https://my-app.test/broadcasting/auth' from origin 'https://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I know this CORS error comes on my way when I have a server issues so I tried debugging the request in Insomnia. However when mimicking the request in Insomnia it gives a response 200 and also what would be expected:
I've been reading several guides and stackoverflow questions but I can't find anything similar.
Going back to it might be a CORS issues but I don't think that is the case. My OPTIONS request returns back just fine.
To be complete I also add some code that might help in debugging.
My BroadcastServiceProvider
public function boot()
{
Broadcast::routes(['middleware' => ['auth:api']]);
require base_path('routes/channels.php');
}
My channels.php
use Illuminate\Support\Facades\Broadcast;
Broadcast::channel('App.User.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
The client
this.echoClient = new Echo({
broadcaster: 'pusher',
key: process.env.VUE_APP_WEBSOCKETS_APP_ID,
wsHost: process.env.VUE_APP_WEBSOCKETS_URL,
wssPort: 6001,
// forceTLS: true,
disableStats: true,
authEndpoint: process.env.VUE_APP_SERVER_URL + '/broadcasting/auth',
auth: {
headers: {
Authorization: "Bearer " + this.$store.state.auth.auth_token
}
}
})
// this one works!!
this.echoClient.channel('App.User')
.listen('UpdatePosts', (e) => {
console.log('event received')
console.log(e)
})
// private channels don't work ... :'(
this.echoClient.private('App.User.' + this.user.id)
.listen('UpdatePosts', function(e) {
console.log(e)
})
for anyone struggling with this issue.
For me the solution was to add the api prefix to the broadcast::auth method.
public function boot()
{
Broadcast::routes(['prefix' => 'api', 'middleware' => ['auth:api']]);
require base_path('routes/channels.php');
}
Offcourse you need to correctly set the api prefix on the client:
authEndpoint: process.env.VUE_APP_SERVER_URL + '/api/broadcasting/auth',
I suppose the difference is that when you prefix api Laravel we specifically tell the server to ditch web Middleware.
I still don't really understand why the request was succesfull from Insomnia since there was no x-csrf header set. Insomnia did send a cookie header. Maybe that's the reason why it was working there.
EDIT
Solution provide by #Tippin on laracasts forum.
To add to the answer, it was a CORS issue after all.
https://github.com/fruitcake/laravel-cors
Prefixing the broadcast route with API does not alter middleware at all, so that is not putting it in the api middleware group. What I do think is happening is you may have the cors package installed and in the allowed paths, you have something like api/*, so by simply adding that prefix, you solved your issue. Otherwise, you can add the default broadcast to the whitelist (assuming you use that package for CORS):
/*
* You can enable CORS for 1 or multiple paths.
* Example: ['api/*']
*/
'paths' => ['api/*', 'broadcasting/auth'],
https://github.com/fruitcake/laravel-cors/blob/master/config/cors.php
I've got the same problem
By using fruitcake/laravel-cors, it was solved.
this is my auth option:
auth : {
headers : {
Authorization: "Bearer " + token,
Accept: "application/json",
}
},

How to authenticate Vue.js / Axios request of an API route in Laravel

I'm in Laravel 5.6. I have all my API routes built out and properly responding to requests from my REST client (Paw). I'm trying to build a simple front end to access those routes.
I'm trying to use Laravel's out-of-the-box features as much as possible, so I'm using Axios to call those routes from a blade template using Vue.js. It works if I disable auth middleware on the test route, but I get 401 errors on the console when auth middleware is enabled for the route.
The problem seems obvious enough... The auth:api guard on my /api routes wants to see an oauth token in the header, but when I log in with the web page it does session authentication. I assume there's a simple way to resolve this without having to spoof an oauth token request in the web frontend, right? Do I need to somehow pass the session token in my request with Axios? And, if so, do I also need to change the auth:api guard in my api routes file?
I solved it! I'm a bit embarrassed because the answer was actually in the Laravel docs, but I will say I tried this before posting the question here and it wasn't working. Perhaps something else was broken at the time.
Per the Laravel docs:
All you need to do is add the CreateFreshApiToken middleware to your
web middleware group in your app/Http/Kernel.php file:
'web' => [
// Other middleware...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
This Passport middleware will attach a laravel_token cookie to your
outgoing responses. This cookie contains an encrypted JWT that
Passport will use to authenticate API requests from your JavaScript
application. Now, you may make requests to your application's API
without explicitly passing an access token...
You will probably want to use Larvel Passport or a JWT auth mechanism for obtain the Authorization token.
Seeing as how you're using axios, add a request interceptor to attach the access token to every request once you successfully authenticate. A simple example:
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// assume your access token is stored in local storage
// (it should really be somewhere more secure but I digress for simplicity)
let token = localStorage.getItem('access_token')
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
}
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
to use the auth:api first you need api_token inside your users table
Schema::table('users', function ($table) {
$table->string('api_token', 80)->after('password')
->unique()
->nullable()
->default(null);
});
also you can create a user for testing as follows
User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
'api_token' => Str::random(60),
]);
in your layout use the following before #yield('content')
<script>
window.Laravel = <?php echo json_encode(['api_token' => (Auth::user())->api_token]); ?>
</script>
now you can use window.laravel.api_token inside your vue js to use it in headers
heres an example
var methods = new Vue({
el: '#tabs_lists',
data: {
config: {
headers: {
Authorization: 'Bearer ' + window.Laravel.api_token,
Accept: 'application/json'
}
},
data: []
},
methods: {
test: function (link) {
axios.get(link, this.config)
.then(response => (this.data = response.data)).catch(function (error) {
// handle error
console.log(error);
});
}
}
}
)

Axios/XMLHttpRequest is sending GET instead of POST in production environment

I am running into a very strange issue. We are putting an app into production and one of the POST request is turning into a POST followed directly by a GET request to the same URL and the POST is never received in the backend (Laravel). In the chrome network tab it just looks like just a GET but with Burpsuite we can see the POST request.
The code responsible
async store() {
// This prints post
console.log(this.method());
await this.form[this.method()]('/api/admin/users/' + (this.isUpdate() ? this.id : ''));
if (!this.isUpdate()) {
this.form.reset();
}
},
The form.post method content
return new Promise((resolve, reject) => {
axios[requestType](url, this.data())
.then(response => {
this.busy = false;
this.onSuccess(response.data);
resolve(response.data);
})
.catch(error => {
this.busy = false;
if (error.response.status == 400) {
return this.displayErrors(error.response.data)
}
this.onFail(error.response.data.errors);
reject(error.response.data);
});
});
This question was also answered by me in the Larachat slack forum, and for others sake here is the answer for the next one with such a problem.
Just a little back story. In the chat we found out that it was receiving a 301 error which is a redirect error.
I had the same error recently when posting to a url on a staging server, it was working fine locally but not on the staging server.
The problem appeared to be a slash at the end of the post url.
So posting to https://example.com/post/to/ will not work.
Removing the / and posting to https://example.com/post/to will work.
Just for info, I had the same thing - axios request was being redirected. For me though, it turned out to be some localization middleware causing the redirect!
I set up an alternative route in the Api routes file (again Laravel as in the question), bypassing that middleware (probably where the route should have gone in the first place!). All good now! Schoolboy error I guess!
I confirm the previous answer. And I had the same problem from local to production ambience.
The call to an endpoint like / api / user / store / might be redirect to / api / user / store with a 301 status code and this call was interpreted like a GET that obviously we cant reach (because it not in our route list). So it don't work.
A solution can be to work on Apache configuration (trim last slash) but I prefer to adapt my Axios call.

JWT Token Refresh

Hello I am using JWT for authentication in my laravel 5 application. The application currently resides on a live server and has several APIs that other apps can connect to.
Currently I am building a mobile app that connects to some of these APIs, as you know with mobile apps when the user logs in once I want them to remain logged in for a very long time.
To do this I found out that I need to refresh the JWT when it's a expired, apparently this will generate a new token and blacklist the old one.
I use that latest version of Tymon/JWT which includes a new
blacklist_grace_period feature. This allows me to set a delay for when the token is refreshed and the old one is blacklisted.
My question now is, having the following in my route file:
Route::group(['domain' => 'admin.website'.(App::environment('production') ? '.com' : '.dev'), 'prefix' => '/api/v1/manager/', 'namespace' => 'App\Http\BackEnd\Controllers', 'middleware' => ['before' => 'jwt.auth', 'after' => 'jwt.refresh', 'acl'], 'is' => 'manager'], function() {
/** Manager Resource Routes */
Route::resource('events', 'EventManagerController');
});
How do I then get the newly generated token and return to the client after a jwt.refresh? Or is that automatically appended to the response of the request?
Take a look at the after-middleware that you are using:
vendor/tymon/jwt-auth/src/Middleware/RefreshToken.php
First it parses the token, then refreshes it:
try {
$newToken = $this->auth->setRequest($request)->parseToken()->refresh();
}
Then it adds it to the response's header:
$response->headers->set('Authorization', 'Bearer '.$newToken);
return $response;
So, yes, this is indeed where the newly refreshed token is added to the response.

Resources