Laravel Passport not authenticating with JWT cookie (self consuming API) - laravel

I've gone through the entire page of documentation and as far as I can tell I have everything set up exactly as the documentation states. However, when I attempt to make a GET request to /api/users it always returns a 401 Unauthorized.
If I inspect the request, I see that the laravel_token is indeed being passed along with the request, as well as CSRF.
At this point, I'm not really sure why it's always failing to authenticate, but it's pretty frustrating and I'm sure it's something minor that I'm overlooking somehow.
I'm using Laravel 5.7.5.
Configuration steps done:
Ran php artisan passport:install
Added trait to User model
Added Passport::routes() to AuthServiceProvider::boot()
Changed API driver to passport in config/auth.php
Added CreateFreshApiToken::class to web middleware

After a lot of digging, I finally figured out what my issue was.
In version 5.6 and later of Laravel, cookies are no longer serialized/unserialized. However, Passport still expects that the cookies are serialized. Neither the documentation for Laravel or Passport point this out, and hopefully they'll get more in sync so this isn't an issue.
To fix this, you just need to add Passport::withoutCookieSerialization(); to app\Providers\AuthServiceProvider::boot()

Related

Laravel sanctum logout not working even after tokens are deleted

I have tried so many options I could think of or find online, user details are still persisted even after successful deletion of all the tokens!
I am using Laravel sanctum with Vue. The logout function I created works as expected when I use postman to make the request, but this is not the case when I make the request via axios from the browser. The tokens gets deleted quite alright but I still can access protected routes.
I have also tried
Auth::logout()
I have continued to try this using sanctum and web as guard.
Any help will be appreciated.

Laravel Passport suddenly returning 401 on Vue/Axios API calls

My application has an API that I consume from my JavaScript/Vue front end. Recently, I (unrelated) tried to unsecure Valet in order to share the site to perform webhook tests. I received a Brew 'Unable to determine linked PHP' error. Long story short I restored the symlink, updated composer dependencies and resecured Valet to ensure nothing was broken.
Upon loading my application, all calls to my Laravel Passport secured API are returning 401. My unit tests are all passing, so the non-javascript authentication is working.
Both the laravel_token and the x-csrf-token are being sent in the request header and I still have CreateFreshApiToken middleware in my Kernel.php.
Has anyone got any ideas as to where I can start to look to debug this?
If you've just recently upgraded to Laravel 5.6.30, this was a security patch with breaking changes.
Read upgrade notes here.
TL;DR
Call Laravel\Passport\Passport::withoutCookieSerialization() in your AppServiceProvider

Getting error: "Unauthenticated" with Laravel Passport

I'm trying to create a single page app with Laravel/Passport as the API backend. To sign up, I create my user as usual, then use Passport's ApiTokenCookieFactory to generate my JWT cookie like so:
// api_success is a response macro
return $this->response->api_success('User successfully created')
->withCookie($this->cookie->make($newUser->getModel()->getKey(), $request->header('X-CSRF-TOKEN')));
You can assume that $this->cookie is the container's resolved cookie factory. This all works fine and I get a laravel_token cookie with the JWT.
When I go to make a request though, for example to api/users/me, I get this response:
{
"error": "Unauthenticated."
}
I'm sending the CSRF token, I'm setting X-Requested-With to XMLHttpRequest, and the cookie is being sent along with the request.
The /api/users/me route has the api:auth middleware, so I'm at a loss as to what I'm doing wrong. If anyone has any suggestions they'd be greatly appreciated <3
I've solved the issue I was having, if anyone gets here in the future, note that cookies are not encrypted by default!! Make sure to either add the encrypt cookies middleware to your route, or encrypt cookies manually. My god I was so happy when I figured this out. It turns out that the passport guard was trying to decrypt my unencrypted cookie and silently (!!) failing.

Laravel Passport tokensExpireIn seems not working

i'm using Larave 5.4 passport to create SPA application. However, i was able to make authentication work. but access token are always short-lived tokens with 600s expiration time.
i could not increase expiration time with:
Passport::tokensExpireIn(Carbon::now()->addDays(15));
Passport::refreshTokensExpireIn(Carbon::now()->addDays(30));
it have no effect at all.
any help? thanks in advance.
Personal access tokens are always long-lived. Their lifetime is not modified when using the tokensExpireIn or refreshTokensExpireIn methods - as explained in Laravel's official documentation (https://laravel.com/docs/5.7/passport#personal-access-tokens).
The option of editing PassportServiceProvider.php in the vendor directory is a bad idea. Every time, you make an update (e.g composer update/install) or by another developer in production, code will be reverted to status quo, and it would start failing.
A better approach is to use Password Grant Tokens. The OAuth2 password grant allows your other first-party clients, such as a mobile application, to obtain an access token using an e-mail address / username and password. This allows you to issue access tokens securely to your first-party clients without requiring your users to go through the entire OAuth2 authorization code redirect flow. Be sure that you have duly installed passport (See Guide: https://laravel.com/docs/5.7/passport#installation), then run this command
php artisan passport:client --password
Having done this, you can request an access token by issuing a POST request to /oauth/token. Remember, this route is already registered by the Passport::routes method so there is no need to define it manually. If the request is successful, you will receive an access_token and refresh_token in the JSON response from the server. See payload sample below:
{
"grant_type" : "password",
"client_id":"your-client-id",
"client_secret":"your-client-secret",
"username":"twady77#gmail.com",
"password":"123456",
"scope":""
}
Sample response:
{
"token_type":"Bearer",
"expires_in":1296000,
"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjVkMWJjN2NhOTU0ZWU2YTZmOGNkMGEyOTFjOTI5YzU4Zjk3ODk3M2YxZDJmNjQ2NjkyZjhjODQyZjYxNTBjZGRiYzMwY2RjMzRmZjJhYmU1In0.eyJhdWQiOiI4IiwianRpIjoiNWQxYmM3Y2E5NTRlZTZhNmY4Y2QwYTI5MWM5MjljNThmOTc4OTczZjFkMmY2NDY2OTJmOGM4NDJmNjE1MGNkZGJjMzBjZGMzNGZmMmFiZTUiLCJpYXQiOjE1NDkyOTI5MjcsIm5iZiI6MTU0OTI5MjkyNywiZXhwIjoxNTUwNTg4OTI3LCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.cSvu30xAT-boA5zmVuxTr0TfH_5MYuVWYi6NVQRbryZSswt8EAFTi5QXHH1f0O63DWnLA6VFBS2AfDe4-ryJZACDnt4gtPJOeuu1rNMZ53MU1vjxnyC8FsYz8v9vmYJsZPKqfTJpuJFYRFh7kkV7uWAmrEkuF3POnDn-GjW50f4i26lIZW5ta5j4nZQrIJCQUEzwXaQtn9H-qef3bTWAaplWaV-k7Blic-0TXXVfWa_CdoKCAzHROVBRWY1Idhe1LJkvGKldUGzUfliiB1x7EVVInq94VYEP5d9__90Z2UMUn5dCEgWkXvcEHYy87_4OSwu4TQk_f3hD82OVOEtJGgPyJqK51WqnQCBYwNtxNjqAW2oaMgpritp3G8nccUiyhkE4Pd_kj3cb2OvSNRXdDS9z-RnJb1OXUkja-4Xe_JfIWUjlTnkss18xMg89hcU_3xtBwUXBWHgffzcbNoI1oOwUL6Whekduiy8csf665v0cnzkPXISmvyGhiMseIlBEN9m9uESaJqD_g7WzbsEs7meI0CAF3230UgrI1MdYSAJMW0mMPF9EScH31a_Qpde5O233Ty6-S4NAp323Wneqs_jpGSfw81CvoI1JeY0hZccRC-MBBsQ2Ox7AM36H5L3p-ybricmT3oCcHEqhufq-ygyfqk1RufJwwRblwYPyaJE",
"refresh_token":"def50200c6b2378110190ac28d9d55f622885bb0b470a20543a6f1eefb18ed93c57b7040dc4db9444aa8853209bde9d5443a407d43fcaf1deb2e1f3f5ea3ce7431c4ec5e111bdc0cc71ca76034cd2a884441c51e4c922dddfa3f6e3a3fa8e1fbb8efe4581ce70d76590e732b3fa8b0c41a8abff4a8759f9dd1cc3ae46134fb67a8f25cd79e3229f6ee3238701ebfe0e8b0e2f14bd13c7fde3f813708a3de9928c8e992850994ca97bf61984cdb846bd0d72916312d9985472fc4293a3b3f2c55e1ef19621ef009623a6780f800ece9c8d835871dc795fda5daa43ac3fdae467e66b46e4eb73d53b8cb821522ee60979711c28c54fb2085f6000ac7e96e019ce51b9f92ea3fa2028aa0238fc3dca9c900e8dd77907782b22482f95a5e55708e5bda8c28f3732ff55e361f08447b33fe05d5646cecfb9faed462d327efdcc2a3742f46f9f825275d296b4ced25c05f3b6add68f43a2b448e4523d5410c631dc45bba"
}
Try to use this library: https://github.com/GeneaLabs/laravel-caffeine , and you can look laravel session config options
tokenExpireIn() instead of refreshTokensExpireIn() use then solve tokenExpireIn problem.
for passport grant token
Passport::tokensExpireIn(Carbon::now()->addDays(10));
Passport::refreshTokensExpireIn(Carbon::now()->addDays(15));
This is only adding 10 minutes of expiry time. Don't know how and why, but instead changing internal codes. I Changed
Passport::tokensExpireIn(Carbon::now()->addDays(10000));
Passport::refreshTokensExpireIn(Carbon::now()->addDays(12000));
Now it's adding 7 days of expiry time. Seems like addDays function adding 10000 minutes.
I have the same issue before for my application, I spent two days try to find what is the problem, The best solution that I came up with is to change the expire date directly in the PassportServiceProvider
Go to vendor/laravel/passport/src/PassportServiceProvider.php line 108
new PersonalAccessGrant, new DateInterval('P1Y')
for example to set the expire date to one week
new PersonalAccessGrant, new DateInterval('P1W')
I know this is a bad solution for fixing the issue, recently I have found the same issue on Laravel Git repo
https://github.com/laravel/passport/issues/47

Laravel 5.4: how to protect api routes

I have a react app that fetch datas from laravel api defined like so in routes/api.php:
// this is default route provided by laravel out of the box
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
// ItemController provides an index methods that list items with json
Route::resource('items', 'Api\ItemController', array('except' => array('create','edit')));
// this is to store new users
Route::resource('users', 'Api\UserController', array('only' => array('store')));
for example http://example.com/api/items returns the data as intended but it's really insecure since anyone could access it through postman.
How to make those routes only accessible inside the app?
As I'm new to it I don't understand if I need to set up api_token and how?
Do I need to setup Passport?
Is is related to auth:api middleware?
It may sounds really basic but any help or tutorial suggestions would be greatly appreciated
EDIT
End up with a classic session auth. Moved routes inside web.php. Pass csrf token in ajax request. Actually i didn't need a RESTful API. You only need token auth when your API is stateless.
As you are using Laravel 5.4 you can use Passport, but I haven't implemented yet, but i implemented lucadegasperi/oauth2-server-laravel for one of my laravel projects and it was developed in Laravel 5.1
Here is the link to github repository
lucadegasperi/oauth2-server-laravel
Here is the link to the documentation Exrensive Documentation
Just add the package to the composer json and run composer update,the package will get installed to your application , once installed add the providers array class and aliases array class as mentioned in the Laravel 5 installation part of the documentation,
you have to do a small tweak in order to work perfectly cut csrf from $middleware array and paste it into $routeMiddleware array and again run php artisan vendor:publish after publishing the migrations will be created and run the migration php artisan migrate
if you only want to secure api routes for each client like ios, android and web you can implement Client Credentials Grant, or if you need to every user with oauth the you can implement Authorization Server with the Password Grant or some other.,
Never use the client id or other credentials, generating access token in the form, but add it some where in helper and attach it in the request to the api,
Hope this answer helps you.
You could use JWT it's pretty easy to get it to work. You basically generate a token by requesting Username/Password and passing that token in every request that requires authentication, your URL would look like http://example.com/api/items?token=SOME-TOKEN. without a proper token, he doesn't have access do this endpoint.
As for
How to make those routes only accessible inside the app?
If you mean only your app can use these requests, you can't. Basically the API doesn't know who is sending these requests, he can only check if what you are giving is correct and proceed with it if everything is in order. I'd suggest you to have a look at this question

Resources