I've read a lot from this forum and watched a lot of tutorial videos on how to connect separate React/Vue SPA to Laravel API with Sanctum Auth but none of the solutions worked for me. This is for my school project.
So here's what I did so far.
I created 2 folders, one for api and one for frontend. I installed Laravel on the api folder and installed React app on the frontend folder. Both of these are Git initialized and have their own Github repositories. Also, both of them are deployed to Heroku.
API
Repository:
https://github.com/luchmewep/jarcalc_api
Website:
https://jarcalc-api.herokuapp.com
Front-end
Repository:
https://github.com/luchmewep/jarcalc_front
Website:
https://jarcalculator.herokuapp.com
On local, everything runs fine. I can set error messages to email and password fields on the front-end so that means I have received and sent the laravel_session and XSRF_TOKEN cookies. I have also displayed the authenticated user's information on a dummy dashboard so everything works fine on local.
On the internet, both my apps run but won't communicate with each other. In the official documentation, they must at least be on the same domain and in this case, they are subdomains of the same domain which is .herokuapp.com.
Here are my environment variables for each Heroku apps.
API
SANCTUM_STATEFUL_DOMAINS = jarcalculator.herokuapp.com
(I've tried adding "SESSION_DRIVER=cookie" and "SESSION_DOMAIN=.herokuapp.com" but still not working!)
Update
Found out that axios is not carrying XSRF-TOKEN when trying to POST request for /login. It is automatically carried on local testing.
Here is the relevant code:
api.tsx
import axios from "axios";
export default axios.create({
baseURL: `${process.env.REACT_APP_API_URL}`,
withCredentials: true,
});
Login.tsx
...
const handleSubmit = (e: any) => {
e.preventDefault();
let login = { email: email.value, password: password.value };
api.get("/sanctum/csrf-cookie").then((res) => {
api.post("/login", login).then((res) => {
/**
* goes here if login succeeds...
*/
console.log("Login Success");
...
})
.catch((e) => {
console.log("Login failed...")
});
})
.catch((e) => {
console.log("CSRF failed...");
});
};
UPDATE
".herokuapp.com is included in the Mozilla Foundation’s Public Suffix List. This list is used in recent versions of several browsers, such as Firefox, Chrome and Opera, to limit how broadly a cookie may be scoped. In other words, in browsers that support the functionality, applications in the herokuapp.com domain are prevented from setting cookies for *.herokuapp.com." https://devcenter.heroku.com/articles/cookies-and-herokuapp-com
Explanation: Although the API and frontend both have .herokuapp.com, that does not make them on the same domain. It is explained on Heroku's article above. This means that all requests between *.herokuapp.com are considered cross-site instead of same-site.
SOLUTION
Since laravel_session cookie is being carried by axios, the only problem left is the xsrf-token cookie. To solve the problem, one must buy a domain name and set the subdomain name for each. In my case, my React frontend is now at www.jarcalculator.me while my Laravel backend is now at api.jarcalculator.me. Since they are now same-site regardless of where they are deployed (React moved to Github pages while Laravel at Heroku), the cookie can be set automatically.