How to set a cookie on response in Laravel Lumen 8 - php

I'm currently building a REST API with Laravel Lumen 8. I want to set a cookie if the user logged in successfully. I saw that in the Lumen 5.1 docs there was a section that showed how to send a cookie with the response (https://lumen.laravel.com/docs/5.1/responses#attaching-cookies-to-responses). But in the documentation for version 8 this section is missing. I also looked into the Laravel 8 docs (https://laravel.com/docs/8.x/responses#attaching-cookies-to-responses) and tried the following things in my routes/web.php file:
Attempt 1
$router->get('/test', function () {
return response('Hello World')->cookie(
'name', 'value', 60
);
});
But then I get the following error:
Argument 1 passed to
Symfony\Component\HttpFoundation\ResponseHeaderBag::setCookie() must
be an instance of Symfony\Component\HttpFoundation\Cookie, string
given
Attempt 2
use Illuminate\Support\Facades\Cookie;
$router->get('/test', function () {
Cookie::queue('name', 'value', 60);
return response('Hello World');
});
Error message: Target class [cookie] does not exist.
Attempt 3
$router->get('/test', function () {
$cookie = cookie('name', 'value', 60);
return response('Hello World')->cookie($cookie);
});
Error message: Call to undefined function cookie()
Attempt 4
use Symfony\Component\HttpFoundation\Cookie;
$router->get('/test', function () {
return response(null)->withCookie(new Cookie('name', 'value'));
});
This solution works, but if i set the third parameter like this new Cookie('name', 'value', 60), I don't get an error message but the cookie doesn't get set anymore.
And I'm also a bit sceptical because I never saw this in any official docs but only in this stack overflow question: Set cookie on response in lumen 5.6.
These weren't the only things I tried but nothing worked so far. Setting a cookie should be such an easy thing but I just can't achieve it. I'm pretty new to Laravel/Lumen, has it something to do with the new Version 8? Or what else am I doing wrong?

I've had the same issue, this is not pretty but it fixed it for me.
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Response;
...
$response = new Response();
$response->headers->setCookie(Cookie::create('foo', 'bar'));
$response->send(); // <- this guy

In case you are using the jwt-auth library by Sean Tymon for JSON Web Token Authentication, this Thread may help you: https://github.com/tymondesigns/jwt-auth/issues/1594#issuecomment-395575980
Cited from the thread:
The root of the culprit I guess is that Lumen by design no longer does
cookies which I find a bit of a flaw in the light of all the blogs and
OWASP suggestions of not storing a JWT in localstorage but rather in a
httponly cookie to prevent XSS and deal with CSRF accordingly. So, the
jwt-auth doesn't include the cookie parser with the
LumenServiceProvider which is what you register in app.php as a
service provider:
$app->register(Tymon\JWTAuth\Providers\LumenServiceProvider::class);
So when you add
use Tymon\JWTAuth\Http\Parser\Cookies;
to the top of jwt-auth\src\Providers\LumenServiceProvider.php
and add
new Cookies($this->config('decrypt_cookies'))
into the array at the very end of the file
$this->app['tymon.jwt.parser']->setChain([<br>
new AuthHeaders,
new QueryString,
new InputSource,
new LumenRouteParams,
new Cookies($this->config('decrypt_cookies')),
]);
then you should be able use the cookie authentication in Lumen as
well.

Related

Laravel remembers original response during http tests

Given the following pest test:
it('allows admins to create courses', function () {
$admin = User::factory()->admin()->create();
actingAs($admin);
$this->get('/courses')->assertDontSee('WebTechnologies');
$this->followingRedirects()->post('/courses', [
'course-name' => 'WebTechnologies',
])->assertStatus(200)->assertSee('WebTechnologies');
});
The above should fully work; however, the second request post('/courses')...
fails saying that:
Failed asserting that <...> contains "WebTechnologies".
If I remove the first request:
it('allows admins to create courses', function () {
$admin = User::factory()->admin()->create();
actingAs($admin);
$this->followingRedirects()->post('/courses', [
'course-name' => 'WebTechnologies',
])->assertStatus(200)->assertSee('WebTechnologies');
});
The test passes.
If I remove the second request instead:
it('allows admins to create courses', function () {
$admin = User::factory()->admin()->create();
actingAs($admin);
$this->get('/courses')->assertDontSee('WebTechnologies');
});
It also passes.
So why should the combination of the two cause them to fail? I feel Laravel is caching the original response, but I can't find anything within the documentation supporting this claim.
I have created an issue about this on Laravel/Sanctum as my problem was about authentication an stuff...
https://github.com/laravel/sanctum/issues/377
One of the maintainers of Laravel Said:
You can't perform two HTTP requests in the same test method. That's not supported.
I would have wanted a much clearer explanation on why it's not supported.
but I guess, we would never know. (Unless we dive deep into the Laravel framework and trace the request)
UPDATE:
My guess is that, knowing how Laravel works, for each REAL request Laravel initializes a new instance of the APP...
but when it comes to Test, Laravel Initializes the APP for each Test case NOT for each request, There for making the second request not valid.
here is the file that creates the request when doing a test...
vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php
it's on the call method line: 526 (Laravel v9.26.1)
as you can see...
Laravel only uses 1 app instance... not rebuilding the app...
Line 528: $kernel = $this->app->make(HttpKernel::class);
https://laravel.com/docs/9.x/container#the-make-method
the $kernel Variable is an instance of vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php
My guess here is that the HttpKernel::class is a singleton.
P.S. I can do a little more deep dive, but I've procrastinated too much already by answering this question, it was fun thou.
TL;DR.
You can't perform two HTTP requests in the same test method. That's not supported.
UPDATE:
I was not able to stop myself...
I found Laravel initializing Kernel as a singleton
/{probject_dir}/bootstrap/app.php:29-32
Please make sure to not use any classic singleton pattern which isn't invoked with singleton binding or facades.
https://laravel.com/docs/9.x/container#binding-a-singleton
$this->app->singleton(Transistor::class, function ($app) {
return new Transistor($app->make(PodcastParser::class));
});
The Laravel app won't be completely restarted during tests unlike different incoming HTTP requests - even if you call different API endpoints in your tests

Laravel 8 - Setting a cookie in response not working

I'm using Laravel 8 for my backend and I'm trying to store my auth token in a cookie.
I want to set that cookie in my controller response, and I'm trying this way:
return response()->cookie('token', $tokenResult->accessToken, 10000);
The problem here is that Laravel can not find cookie method.
According to Laravel 8 documentation, cookie() is a method from ResponseTrait, but Laravel is trying to get it from Macroable trait:
BadMethodCallException: Method Illuminate\Routing\ResponseFactory: :cookie does not exist. in file /var/www/html/t2t-api/vendor/laravel/framework/src/Illuminate/Macroable/Traits/Macroable.php
How could I solve this issue? Thank you.
P.S.
It seems to work if I add a parameter to response() like this:
return response('Hello World')->cookie('token', $tokenResult->accessToken, 10000);
But I need a JSON response instead of this.
You should use cookie on a new \Illuminate\Http\Response
$response = new \Illuminate\Http\Response();
return $response->cookie("name","value",360);

How to queue cookies in lumen?

I am trying to queue various cookies in response in Lumen.
I've added \Illuminate\Cookie\ into my Composer.
I added Following code in app.php
$app->singleton('cookie', function () use ($app) {
return $app->loadComponent('session', 'Illuminate\Cookie\CookieServiceProvider', 'cookie');
});
$app->bind('Illuminate\Contracts\Cookie\QueueingFactory', 'cookie');
In My Controller, I am trying the following code
Cookie::queue(Cookie::make('test', 'tada', 10, '/'));
//Few more business logic here: before returning the response
$response = new \Illuminate\Http\Response('exit');
return $response->withHeaders($headers);
I can see my queued cookies using Cookie::getQueuedCookies()
but still, after a response, my cookie is nowhere to be found.
I tried various answers from StackOverflow questions but still couldn't resolve it
I can't use response()->withCookie() solution because I am creating cookies at various points of my code, and can't pull them together at the time of response
Queued cookies in Laravel are handled by the \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse class.
It begs the question why you'd use Lumen if you wanted cookies support but you should be able to add this middleware to your HTTP stack in Lumen.

Laravel 5.3 Passport API unauthenticated in Postman using personal access tokens

I have set-up Laravel using passport as per the documentation here: https://laravel.com/docs/5.3/passport
A few people have asked about this using the oAuth implementation but I'm trying to use the personal access tokens not oAuth. One suggestion was to remove the auth middleware but obviously this leaves the application wide open so anyone can make requests.
I have the following route (in routes/api.php):
Route::get('/test', function(){
return 'returned string from test route';
})->middleware('auth:api');
This works if I remove the auth middleware so the route is working correctly but when enabling the auth middleware again I get the following error in postman:
{"error":"Unauthenticated."}
These are the headers being sent via postman:
GET /api/test HTTP/1.1
Host: localhost:8000
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImU4ZmY5MDMwY2EyM2E2MDYwODViN2Y3ZWNiMzcxNDY1MzQxNDViNTk4ODU4NmZhNDljYzU2YjMzYWZiNzhkYTk5OTIwZWMzYzEwNTBkNjZjIn0.eyJhdWQiOiIyIiwianRpIjoiZThmZjkwMzBjYTIzYTYwNjA4NWI3ZjdlY2IzNzE0NjUzNDE0NWI1OTg4NTg2ZmE0OWNjNTZiMzNhZmI3OGRhOTk5MjBlYzNjMTA1MGQ2NmMiLCJpYXQiOjE0NzU1MDMxNjUsIm5iZiI6MTQ3NTUwMzE2NSwiZXhwIjowLCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.IpzKK29dJCpliUXQvPRss87kGFngFcXXwV3jRwhbZOZLxl-4UV70cBsSigmqUuBsHQ4onVl_Cjcq6cEmMFvTZZr7D9AtY3EmScvMPjoFh4KQ3wgd5CoyWfcLQgoBxbElNxL0xW2fIQhpeQd_8Yz_Pr5BByGVTpxfg4JJZ4PzovvZsa2R3izYtqw6-qeurQOtsfOnot5uoLDeDDc76klifnfHfOcNZSoIFGNP3gIGKYBe6lfFuDViR_mQkwQS5_UmERt3GSkEvJjGMtwcRjWY7VPAJ4tvWLnyLw0roGU2e37L0wsqfJ8OrG0Cipv-anXAW_utSo-fiVMr8ZeAWIPguq73Zd44x95YY3nNPOKD5dVIRZM7rQgdhjIwTEz1ggtSXLp-Fu3QOtXaHUahCHvjOTdiTYEa-GR4TZ5wGzt-aRhjdBB7WTe0C6T9ZWVwQr0kJk8AxW6ne87wwJYp_shGunTclZ3SCq5VYg2K_MclbJl65-dT8x-nwqg0lqfNx9s1wmtryrMFIPoBEyaGNEK1aWGHKq418-BIQ1_UAhcHHtEXclWvsGWwhyo3aso-E-sCN2o_IkYvSboIsdFAIXvDvQmoAwis6f1J57zWH8AW1ynCFcBgzBDjIyiaCE5nqtb_4zbEXr8L1EbcllbtZkq3vd9w996kO7xlpBEWwPY8IWg
Accept: application/json
Cache-Control: no-cache
Postman-Token: 6bc483b2-23df-acce-7eef-5a443f8f5d45
Firstly, NEVER modify the vendor files unless you have a fully legitimate reason for doing so and there's a feature you wish to see implemented or a bug you've discovered and fixed in the package you're using.
Expiration time on the JWT might be already set to expire as soon as it's made. Here's a link you can use to check the "ttl" (Time To Live) field of your access tokens:
https://jwt.io/
If you find that your tokens are expiring on creation, you can go to your app\providers\AuthServiceProvider.php class and add in these methods on use of Passport Class:
use Carbon\Carbon;
use Laravel\Passport\Passport;
...
Class AuthServiceProvider extends ServiceProvider {
...
...
public function boot() {
$this->registerPolicies();
Passport::routes();
Passport::tokensExpireIn(Carbon::now()->addYears(20));//You can also use addDays(10)
Passport::refreshTokensExpireIn(Carbon::now()->addYears(20));//You can also use addDays(10)
Passport::pruneRevokedTokens(); //basic garbage collector
}
}
Make sure you're using the most recent version of Passport
Currently I'm on version 1.0.8 but I might already be out of date as they and the community are constantly pushing new revisions every few weeks.
Here are links to some related issues regarding this problem. You might be able to locate your answer within one of the below links. If what's mentioned above isn't what you're looking for.
Passport - "Unauthenticated." - Laravel 5.3
Very Detailed
https://github.com/laravel/passport/issues/151
in passport.php there are two functions
public static function tokensExpireIn(DateTimeInterface $date = null)
{
if (is_null($date)) {
return static::$tokensExpireAt
? Carbon::now()->diff(static::$tokensExpireAt)
: new DateInterval('P100Y');
} else {
static::$tokensExpireAt = $date;
}
return new static;
}
/**
* Get or set when refresh tokens expire.
*
* #param \DateTimeInterface|null $date
* #return \DateInterval|static
*/
public static function refreshTokensExpireIn(DateTimeInterface $date = null)
{
if (is_null($date)) {
return static::$refreshTokensExpireAt
? Carbon::now()->diff(static::$refreshTokensExpireAt)
: new DateInterval('P100Y');
} else {
static::$refreshTokensExpireAt = $date;
}
return new static;
}
you must change P100Y to P1Y. and also in PassportserviceProvider.php at line 101 there is code
$server->enableGrantType(
new PersonalAccessGrant, new DateInterval('P100Y')
);
change P100Y to P1Y. hope it helps you :)
Please check if the token was copied properly, i always observed when i copy the personal tokens, in the last there is a word 'Close' copied also.
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImU4ZmY5MDMwY2EyM2E2MDYwODViN2Y3ZWNiMzcxNDY1MzQxNDViNTk4ODU4NmZhNDljYzU2YjMzYWZiNzhkYTk5OTIwZWMzYzEwNTBkNjZjIn0.eyJhdWQiOiIyIiwianRpIjoiZThmZjkwMzBjYTIzYTYwNjA4NWI3ZjdlY2IzNzE0NjUzNDE0NWI1OTg4NTg2ZmE0OWNjNTZiMzNhZmI3OGRhOTk5MjBlYzNjMTA1MGQ2NmMiLCJpYXQiOjE0NzU1MDMxNjUsIm5iZiI6MTQ3NTUwMzE2NSwiZXhwIjowLCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.IpzKK29dJCpliUXQvPRss87kGFngFcXXwV3jRwhbZOZLxl-4UV70cBsSigmqUuBsHQ4onVl_Cjcq6cEmMFvTZZr7D9AtY3EmScvMPjoFh4KQ3wgd5CoyWfcLQgoBxbElNxL0xW2fIQhpeQd_8Yz_Pr5BByGVTpxfg4JJZ4PzovvZsa2R3izYtqw6-qeurQOtsfOnot5uoLDeDDc76klifnfHfOcNZSoIFGNP3gIGKYBe6lfFuDViR_mQkwQS5_UmERt3GSkEvJjGMtwcRjWY7VPAJ4tvWLnyLw0roGU2e37L0wsqfJ8OrG0Cipv-anXAW_utSo-fiVMr8ZeAWIPguq73Zd44x95YY3nNPOKD5dVIRZM7rQgdhjIwTEz1ggtSXLp-Fu3QOtXaHUahCHvjOTdiTYEa-GR4TZ5wGzt-aRhjdBB7WTe0C6T9ZWVwQr0kJk8AxW6ne87wwJYp_shGunTclZ3SCq5VYg2K_MclbJl65-dT8x-nwqg0lqfNx9s1wmtryrMFIPoBEyaGNEK1aWGHKq418-BIQ1_UAhcHHtEXclWvsGWwhyo3aso-E-sCN2o_IkYvSboIsdFAIXvDvQmoAwis6f1J57zWH8AW1ynCFcBgzBDjIyiaCE5nqtb_4zbEXr8L1EbcllbtZkq3vd9w996kO7xlpBEWwPY8IWg Copy
If this is not your case check if that token exist, or generate a new one. you can use this format to protect route like this
Route::middleware('auth:api')->get('/home', function(){
return 'test';
});
I Had this problem ... two hours down the pan. Something very strange was happening and in my case I think Postman was the culprit.
Inspect the received header to rule it out:
Route::get('/test', function(){
dd( Request::header());
})/*->middleware('auth:api')*/; //disable auth middleware to inspect header
this is the strange thing I found:
.........
"authorization" => array:1 [
0 => b"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJ ................
what the hell was the 'b' before Bearer ACCESS_TOKEN ???
I typed it out again and it dissapeared and auth middleware started working.
Inspect the header exactly and then add the middleware back. This might just be the cause!

Setting Cookies in Laravel 4 only works in App::after()

I try to set a Cookie in Laravel 4 in a specific route.
Unfortunately, setting the Cookie does only work in the global App::after() filter.
First thing I tried was returning a response with a Cookiefrom my Controller.
This doesn't work:
return Response::make($view)->withCookie(Cookie::make('foo','bar'));
However, this does:
return Response::make()->withCookie(Cookie::make('foo','bar'));
But does not solve my problem.
Next I tried it with an after filter as follows.
Route::filter('cookie', function($route, $request, $response)
{
$response->withCookie(Cookie::make('foo', 'bar'));
});
This does not work either.
Next, I tried it using Cookie::queue(), which I've found in another answer - with no luck.
The only place the Cookie is set properly is in App::after().
App::after(function($request, $response)
{
$response->withCookie(Cookie::make('foo', 'bar'));
});
Besides I'm pretty sure that one of the other approaches should work, this solution doesn't give me the control I'm looking for.
I'm running Laravel v4.0.9.
Try this tested, working code.
Specify expiration time (in minutes from now). Dont you use some cookie extension in your browser, which may protect/blacklist specified cookies from being modified...
Route::get('cookieset', function(){
$cookie = Cookie::make('foo', 'bar', 60);
return Redirect::to('cookieget')->withCookie($cookie);
});
Route::get('cookieget', function(){
dd(Cookie::get('foo'));
});

Categories