I have a Laravel 9 api only installation. Login/Logout and any other api routes are working as they should. I'm trying to set a httpOnly cookie on login for use with my ReactJS frontend but it is getting rejected for invalid domain.
The cookie will be used for a refresh token for persistent login. I have it all working in postman but isn't working in the browser with my React frontend. I get both access token and refresh token(that only works on the refresh routes) and things are timing out as expected. My refresh token route deletes the old tokens and refreshes them.
Currently I have have the front end and the backend domains, lets call them
api.myapp.com -> API
my-app.com:3000 -> React Dev Server
Here is my login function:
public function login(Request $request) {
$fields = $request->validate([
'email' => 'required|email',
'password' => 'required',
'remember_me' => 'boolean',
]);
// Check email
$user = User::where('email', $fields['email'])->first();
// Check Password
if(!$user || !Hash::check($fields['password'], $user->password)){
return response([
'message' => 'Invalid Login'
], 401);
}
// Delete old tokens
$user->tokens()->delete();
// Create new tokens
$token = $user->createAuthToken('api')->plainTextToken;
$refresh = $user->createRefreshToken('api')->plainTextToken;
// Create Cookie
$cookie = Cookie::create('myapp-token')
->withValue($refresh)
->withExpires(strtotime("+6 months"))
->withSecure(true)
->withHttpOnly(true)
->withDomain("my-app.com")
->withSameSite("none");
$response = [
'user' => $user,
'token' => $token,
];
// Return user, token and set refresh cookie
return response($response, 201)->cookie($cookie);
}
The cookie is being passed in the header correctly, but isn't actually being set in the browser and I'm getting the error "Cookie 'my app-token' has been rejected for invalid domain. Using the Cookie::create function was the only way I could get it to set sameSite to none assuming I needed that.
The cookie gets handled with middleware and passes it back in as a bearer token for authorization.
public function handle(Request $request, Closure $next)
{
$cookie_name = env('AUTH_COOKIE_NAME');
if (!$request->bearerToken()) {
if ($request->hasCookie($cookie_name)) {
$token = $request->cookie($cookie_name);
$request->headers->add([
'Authorization' => 'Bearer ' . $token
]);
}
}
return $next($request);
}
Does this error come from SANCTUM_STATEFUL_DOMAINS or SESSION_DOMAIN? Or something else entirely? And what does it need to be set to? My API domain for my frontend domain? I feel like I have tried every combination I have found but nothing has worked yet, so I must be missing something.
I've tried both ways to add the cookie, both with and without queue
use Symfony\Component\HttpFoundation\Cookie;
use Illuminate\Support\Facades\Cookie;
With facades and queue It is giving me a csrf-cookie mismatch error. Which I know I can use the sanctum/csrf-cookie route to get that cookie if I need to go that route. If I understand correctly that isn't needed for api only routes.
Kernal.php middleware groups for api
'api' => [
\App\Http\Middleware\AddAuthTokenHeader::class,
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
],
I've been reading everything I can and lots of trial and error but I keep getting stuck. Once the cookie is set correctly I assume Axios on my frontend will pass the cookie on to my api just as my bearer tokens have.
Any direction or help would be greatly appreciated. I can provide any additional code from the other files that helps.