Subdomain routing not working in Laravel 9 and Inertia - laravel

I'm working on a Larave, Inertiajs and Vue project where I want every product to open on a route like this https://{user:username}.example.test/{product:slug}
So basically I need my website to run on example.test and any product to show on username.example.test/productSlug
I had an issue where I'm getting CORS errors and I wrote a thread here Laravel Inertia apps doesn't support subdomain redirects - No 'Access-Control-Allow-Origin'
Unfortunately I didn't get the help I needed here but after hours of searching I found this on stackoverflow which have helped me to finally succeed on making the redirect to the correct URL works but the problem is that It doesn't really work.
To explain this:
The controller store action finishes what It's supposed to do and then redirects me to a working page of my product without cors errors, the only problem is that it shows in the browser as example.test/productSlug while it's supposed to be username.example.test/productSlug. When I refresh the page when I'm on that incorrect url It gets me page not found error which make sense because the actual correct route contains my desired page.
When I tracked the request on firefox I could see that the host is correct (username.example.test) but the url I'm reaching is 'example.test', also when I'm redirected to that incorrect route I could see my product and everything works just fine but it's not a working url because when I refresh It gives me page not found and when I manually write the correct url username.example.test/productSlug It directs me to a working page of my product.
I hope you're not confused.
This is the redirect line in my ProductsController#store:
public function store(StoreProductRequest $request)
{
// Code
return redirect()->route('products.show', [$user, $product]);
}
This is how my route looks like:
Route::domain('{user:username}.' . env('APP_URL'))->group(function () {
Route::get('{product:slug}', [ProductController::class, 'show'])->name('products.show');
});
And this is how I show the product:
public function show(User $user, Product $product)
{
return Inertia::render('Products/Show', [
'user' => $user,
'product' => $product,
'thumbnails' => $product->productimages
]);
}
I made those changes to cors.php:
'paths' => ['api/*', '*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => ['x-inertia'],
'max_age' => 0,
'supports_credentials' => false,

After days of research I'm sure now that it is impossible to route redirect to a subdomain route from root domain without full page refresh. Any change to the host will require the page to reload which is out of Laravel and JetStream scope and out of InertiaJS hands. As far as I understand that this is for security purposes. See more here https://github.com/inertiajs/inertia-laravel/issues/431

Related

Laravel Inertia apps doesn't support subdomain redirects - No 'Access-Control-Allow-Origin'

I'm working on a Laravel 9, InertiaJS and vue project where a user can submit a new product through products/create route and this sends to ProductController#store which does all what it's supposed to do and then supposedly redirect the user to the newly created product on a subdomain route like this http://username.example.test/my-new-product
The issue I'm having is that I keep getting the below error and the redirect doesn't work:
Access to XMLHttpRequest at 'http://user.example.test/my-first-product' (redirected from 'http://example.test/products/create') from origin 'http://example.test' 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.
The code works as follows:
1- The form:
const form = useForm("createProduct", {
webshop_id: null,
title: null,
price: null,
type: null,
currency: null,
description: null,
thumbnail: null,
});
const submit = () => {
form.post(route("products.create"), {
onSuccess: () => form.reset(),
});
};
2- Products create router:
Route::middleware([
'auth:sanctum',
config('jetstream.auth_session'),
'verified',
])->group(function () {
Route::post('products/create', [ProductController::class, 'store'])->name('products.create');
});
3- The controller - Storing the new product with user details and redirecting (which doesn't work) to show the product
public function store(StoreProductRequest $request)
{
// Code
return redirect()->route('products.show', [$user, $product]);
}
4- The show route and controller#show
Route::domain('{user:username}.' . env('APP_URL'))->group(function () {
Route::get('{product:slug}', [ProductController::class, 'show'])->name('products.show');
});
public function show(User $user, Product $product)
{
return Inertia::render('Products/Show', [
'user' => $user,
'product' => $product,
'thumbnails' => $product->productimages
]);
}
I saw #reinink explain in this Issue that we can use now Inertia::location
The only way I could get this to work is returning this is the ProductController#store:
return Inertia::location('http://' . $user->username . '.example.test/' . $validated['slug']);
But It causes a full page refresh which defeats the whole concept of having a single page application and I will have to remember changing 'http' to 'https' and domain to the real domain when I'm going to put the app in production. I was hoping that I was doing something wrong but after hours of searching for a solution I began to think that Laravel Inertia apps doesn't support subdomain redirects
I found a solution that solved for me the CORS errors from this Stackoverflow answer
Making those changes to cors.php solved that for me:
'paths' => ['api/*', '*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => ['x-inertia'],
'max_age' => 0,
'supports_credentials' => false,
but I'm facing an issue where it redirects me to a url that ignores the subdomain completely. Find the new issue here please

Laravel Sanctum Vue SPA CORS issue

I've setup a VUE frontend which connects and authenticates perfectly well with a Laravel backend, using sanctum. I'm able to login and successfully retrieve the logged in user by visiting /api/user. I believe I've followed all the config steps stated in the documentation.
Here is sanctum.php config for stateful domains:
'stateful' => explode(
',',
env(
'SANCTUM_STATEFUL_DOMAINS',
'localhost,localhost:3000,localhost:8080,127.0.0.1,127.0.0.1:8080,::1'
)
), //this is what is in my env SANCTUM_STATEFUL_DOMAINS=app.foo-bar.test:8080
//my laravel backend is running on foo-bar.test
Here is my session.php config:
'domain' => env('SESSION_DOMAIN', null), //in my .env I have: SESSION_DOMAIN=.foo-bar.test
I have the sanctum middleware setup in http/kernel.php, and I'm using auth:sanctum in my routes
Here is my cors.php config:
'paths' => ['api/*', 'sanctum/csrf-cookie', 'login', 'logout'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
and in my vue frontend, I've configured axios to use the following
axios.defaults.baseURL = 'http://foo-bar.test';
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
axios.defaults.withCredentials = true;
As I mentioned, I'm able to successfully login, and even visit /api/user. All works fine,
But when I make a post request to: api/users/multiple,
I get the CORS error: Access to XMLHttpRequest at 'http://foo-bar.test/api/users/multiple' from origin 'http://app.foo-bar.test:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I'm out of ideas, as it seems I've done all the necessary config. Perhaps I'm missing something ? Any help is appreciated.
Kind regards
Add a middleware
php artisan make:middleware Cors
In app/Http/kernel.php paste this in $middleware
protected $middleware = [
\App\Http\Middleware\Cors::class,
....
];
In Middleware/Cors.php now add
public function handle(Request $request, Closure $next)
{
return $next($request)->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods','GET, POST, PUT, PATCH, DELETE,
OPTIONS')
->header('Access-Control-Allow-Headers','Origin, X-Requested-With, Content-
Type, Accept, Authorization');
}
What I came to realise was that this was not actually a CORS issue. If it was a CORS issue, I would not have been able to login, or visit the API that returns the currently logged in user.
What actually happens is that, chrome sometimes fails to report 500 errors on the API and rather returns CORS errors. When I tried making the API call on Firefox, I was able to actually see the 500 error.
Once I resolved the 500 error, the CORS error also stopped appearing in chrome.
What I've seen from this is that the network request error reporting on Firefox is more reliable than that on chrome

Laravel socialite dynamic redirectUrl not working in multi-tenant app

i have a multi-tenant app and i am trying to add Facebook linking in it i have tried the process using laravel socialite but i have a problem that when i use dynamic redirect url like so
return Socialite::driver('facebook')
->with([
'redirect_uri' => "https://" . $dynamichost . "/social/facebook/callback",
])
->redirect();
or this way
return Socialite::driver('facebook')->redirectUrl('https://' . $dynamichost .
'/social/facebook/callback')->redirect();
facebook returns error url mismatch. Note i also have set a value for redirect_url in .env
then i have services values like this
'facebook' => [
'client_id' => env('FACEBOOK_APP_ID'),
'client_secret' => env('FACEBOOK_APP_SECRET'),
'redirect' => env('CALLBACK_URL_FACEBOOK'),
'default_graph_ve`enter code here`rsion' => 'v3.3',
],
my guess is socialite somehow set the redirect url equal to the value which is coming from .env and when i change it dynamically it still thinks that url will be like the value of .env and i have tested this scenario the request get success response if keep the redirect url static.
Any suggestions how can i overcome this. thanks.
if you want to use custom redirect-uri dynamic :
use Socialite;
use Laravel\Socialite\Two\FacebookProvider;
$socialite = Socialite::buildProvider(
FacebookProvider::class, [
'client_id' => 'your_id',
'client_secret' => 'your_secret',
'redirect' => 'url',
'default_graph_version' => 'v3.3', //not sure if this needed as far as i know, in socialite the version is defined
]
)->redirect();
well it was dumb mistake i had to match the redirect url in callback handling method. its fixed now thanks.

Return additional column if user is authorized - API

I have an Laravel-base API which handles both client and admin endpoints (there are two sites like domain.com and admin.domain.com).
My auth is based on cookie, which domain is <.domain.com>. As you can see, this cookie is acceptable for both domains.
I use Eloquent Api Resources for transformation data layer. Is my when() route check here safe and right?
public function toArray($request)
{
return [
'name' => $this->name,
'created_at' => (string)$this->created_at,
'status' => $this->when($request->route()->getName() === 'api.admin.users.index', $this->status)
];
}
Before I used $this->when(Auth::check(), ...), but because my auth cookie is acceptable for client site too, unneeded data might be fetched.
My route:
Route::group(['prefix' => 'admin', 'as' => 'api.admin.', 'middleware' => 'auth:api'], function () {
Route::resource('users', ...);
});
If user is not authorized, he wouldn't get data because of middleware. At the same time, authorized used (who has non-expired cookie) wouldn't get unneded data while being on client site.
Thank you!
I think your approach is fine. The route name is something internal and the user cannot tinker with it. You could improve it by using \Route::is('api.admin.*') though. It would then work for all of your admin API routes.

Laravel: middleware to determine which controller to use

I am building a site which loads all pages and content via javascript while also manipulating the browser address bar (giving the illusion of a normal, navigable site with each page at its own URL). As a fallback, and for the benefit of search engines, the pages must also be able to be loaded normally at their respective URLs.
To do this, I need to let Laravel know if the page data is being requested via an ajax call or normal HTTP request. This- I presume- would be a situation where I would use Middleware. I want to be able to process the pages using two different controllers; one for ajax, one for HTTP.
ie:
if (Request::ajax()){
forward request to ajax page controller
}else {
forward request to standard page controller
}
Is this possible to handle with middleware? All examples I can find seem to assume that the controller is already a given.
I use the routes.php file instead of middleware. I believe middleware is after the route has been determined.
if(Request::ajax() || Request::json()){
Route::get('items', [
'as' => 'api.posts.index' ,
'uses' => 'Api\ItemsController#index'
]);
} else {
Route::get('items', [
'as' => 'posts.index' ,
'uses' => 'ItemsController#index'
]);
}
I do it this way because I like to separate out the urls for json versus web.
Route::get('items', [
'as' => 'posts.index' ,
'uses' => 'ItemsController#index'
]);
/**
* JSON API
*
*/
Route::group([
'prefix' => 'api/v1',
'as' => 'api.',
'namespace' => 'Api'
], function () {
Route::get('items', [
'as' => 'posts.index' ,
'uses' => 'ItemsController#index'
]);
}
Either way your controllers would live here.
App/Http/Controllers/Api/ItemsController.php
App/Http/Controllers/ItemsController.php
EDIT
I read the comment form GONG and the RouteServiceProvider would also work for this, but you would still have two distinct urls. You would have to manage another routes file, but whatever works for you.

Resources