This is a common issue when feature testing cookies in Laravel, especially with Passport or Sanctum, due to how Laravel encrypts and handles cookies in tests.
The root cause:
When you use Cookie::get() in your controller, it tries to read the cookie from the request. In feature tests, cookies are encrypted unless you specifically set them as unencrypted. If your app expects an encrypted cookie (the default), but your test sends it unencrypted (or vice versa), Cookie::get() will return null.
Solution
1. Use withUnencryptedCookie in your test
Since in your controller you are using Cookie::get(), which expects the cookie to be encrypted (as Laravel does by default), you should use withUnencryptedCookie() in your test. However, you must ensure the value you pass is unencrypted (i.e., the raw value), and Laravel will encrypt it for the request.
But, if you get the cookie from the response ($cookie = $response->getCookie(...)), that cookie is already encrypted. Passing it to withUnencryptedCookie() will result in double encryption, and Cookie::get() won't be able to decrypt it.
Instead, use withCookie() with the raw value:
$cookie = $this->postJson(route('auth.login'), $this->credentials)
->getCookie(Passport::cookie());
$response = $this->withCookie(
$cookie->getName(),
$cookie->getValue()
)->putJson(route('auth.refresh-tokens'));
But if this still doesn't work, try decrypting the cookie value before passing it to withUnencryptedCookie():
use Illuminate\Support\Facades\Crypt;
$cookie = $this->postJson(route('auth.login'), $this->credentials)
->getCookie(Passport::cookie());
$decryptedValue = Crypt::decrypt($cookie->getValue());
$response = $this->withUnencryptedCookie(
$cookie->getName(),
$decryptedValue
)->putJson(route('auth.refresh-tokens'));
2. Ensure you are using the correct cookie name
Double-check that Passport::cookie() returns the same name in both your controller and your test.
3. If you are using HttpOnly or Secure cookies
Laravel's test client can handle these, but if you have custom logic around these flags, ensure they're not interfering with your test environment.
Summary
- If you use
withCookie(), pass the value as-is (encrypted). - If you use
withUnencryptedCookie(), decrypt the value first. - Prefer
withUnencryptedCookie()with the decrypted value for Passport refresh tokens.
Example:
use Illuminate\Support\Facades\Crypt;
$cookie = $this->postJson(route('auth.login'), $this->credentials)
->getCookie(Passport::cookie());
$decryptedValue = Crypt::decrypt($cookie->getValue());
$response = $this->withUnencryptedCookie(
$cookie->getName(),
$decryptedValue
)->putJson(route('auth.refresh-tokens'));
$response->assertOk();
This should ensure that $token in your controller is populated as expected.
References:
Let me know if this resolves your issue!