First Party use of Laravel Passport - laravel

I'm using Laravel Passport for API authentication,
This is my stack:
ReactJs (front-end)
Laravel (backend - API)
Currently I'm giving Personal Access Tokens directly to users like this, and after getting the token user's can access protected routes as usual:
public function login(LoginRequest $request)
{
$request->authenticate(); // it authenticate the user based on $request->email and $request->password
/** #var User $user */
$user = Auth::user();
$token = $user->createToken('auth');
return response([
'message' => 'success',
'user' => $user,
'token' => $token->accessToken,
]);
}
It works fine but the problem is now, anyone can get their token directly using tools like Postman, and use that token to access protected routes. which I don't want.
I only want the users to access the routes via my whitelisted frontends. like Reactjs SPA etc..
I read some articles sugesting to whitelist our domains on config/cors.php, it works for modern browsers, but still tools like postman would work. :(
Is there any-way I can acheive this?
Thank you so much for reading.

https://laravel.com/docs/9.x/passport#consuming-your-api-with-javascript you can check that part of the docs. I think you can protect your api with that middleware solution

Related

Laravel Guest Policies always return user as null unless using authentication middleware with Sanctum

I'm using Laravel Sanctum for authentication for an API and taking advantage of Policies for authorization.
The API has an endpoint to view a Post. Posts can either be draft or published. Anyone (including guests) can view published posts, but only admins can view draft posts.
The view policy looks like this:
/**
* Determine whether the user can view the model.
*
* #param \App\Models\User $user
* #param \App\Models\Post $post
* #return mixed
*/
public function view(?User $user, Post $post)
{
if($user && $user->isAdmin()){
return true;
}else{
return $post->published;
}
}
However, $user is always null, meaning the admins cannot view draft posts. But, if I run the auth:sanctum middleware on this route, it works fine for admins. However, that means then guest users get blocked from this endpoint because they are not authenticated.
My workaround currently is to use the authorizeForUser(auth('sanctum')->user(), 'view', $post) method to explicitly define the use. However, this forces me to break from the pattern used for other endpoints and doesn't seem like it should be necessary.
Any solutions for this problem?
I have also noticed if I want to access the user on these routes, I need to explicitly specify to use the sanctum guard, i.e. auth('sanctum')->user(). Is there a config setting required always to use Sanctum as default?
You can change your config file like this:
// config/auth.php
'defaults' => [
'guard' => 'sanctum',
],

Auth->user() don't work in API controller in Laravel

In my api.php file, I have apiResource
Route::apiResource('temperature', App\Http\Controllers\Api\TemperatureController::class);
In TemperatureController in store method, I try create new Temperature:
use Illuminate\Support\Facades\Auth;
public function store(CreateTemperatureRequest $request){
Temperature::create([
'temperature' => $request->get('temperature'),
'userName' => Auth()->user()->name,
'created_at' => Carbon::now()
]);
return true;
}
After the form submits, I have an error - Trying to get property 'name' of non-object. My form is vueJs component.
If I add auth middleware in the controller:
public function __construct()
{
$this->middleware('auth:api')->only('store');
}
and call Auth('api')->user()->name,
I have error 401 (Unauthorized).
I am not using Passport for authentication. I use standard Laravel authentication, but the form is a Vue component and I use an API controller.
How can I get user data in the API controller?
use auth() instead
auth($guard)->user()->name
$guard may be
'api' or 'web'
try
request()->user();
when working with API, You must send a header with you, this is working fine with sanctum. if it doesnt work switch to laravel Sanctum

Problem getting authenticated user with Laravel Lumen + Passport

I am using LumenPassport (https://github.com/dusterio/lumen-passport) and I followed a few tutorials listed here.
I used a combination of these tutorials as well as a heck of google and stackoverflow searches to achieve what I have thus far:
http://esbenp.github.io/2017/03/19/modern-rest-api-laravel-part-4/
http://esbenp.github.io/2015/05/26/lumen-web-api-oauth-2-authentication/
https://blog.pusher.com/make-an-oauth2-server-using-laravel-passport/
What I achieved so far
1. Using password grant to get an access & refresh token
2. Storing these tokens in a secure http only cookie
3. Retrieving these tokens in Lumen's AuthServiceProvider
What I am unable to do
1. Getting the authenticated user with the AccessToken
I am trying to access either of these endpoints:
$router->group(['middleware' => 'auth:api'], function () use ($router) {
$router->get('/', function () use ($router) {return $router->app->version();});
$router->post('/logout', '\App\Auth\LoginController#logout');
});
I will immediately get an unauthorized error.. After some deep diving, the error comes from Authenticate.php which I know is called after AuthServiceProvider. I took a look at AuthServiceProvider and according to Lumen's documentation, this is how the boot method should looks like. Of course it is using the "api" driver and I had to switch it to "passport" for it to work.
AuthServiceProvider.php
public function boot()
{
$this->app['auth']->viaRequest('passport', function ($request) {
// dd("test") // this works
// dd(Auth::user());
// dd($request->user());
// dd(Auth::guard('api')->user());
});
}
Authenticate.php
public function handle($request, Closure $next, $guard = null)
{
if ($this->auth->guard($guard)->guest()) {
$status = Response::HTTP_UNAUTHORIZED;
return response()->json(['success' => false, 'status' => $status, 'message' => 'HTTP_UNAUTHORIZED'], $status);
}
return $next($request);
}
From here, I am still unable to get any of the authenticated user's information. I have made sure to access these endpoints with Postman with the appropriate Authorization headers.
The reason why I need to retrieve the user is because I hope that in my logout method, I will be able to then retrieve the accessToken of that authenticated user and revoke the token and clear the cookies.
LoginController.php
public function logout()
{
// Get the accessToken from Auth
// Need to fix AuthServiceProvider first
$accessToken = $this->auth->user()->token();
$refreshToken = $this->db
->table('oauth_refresh_tokens')
->where('access_token_id', $accessToken->id)
->update([
'revoked' => true,
]);
$accessToken->revoke();
$this->cookie->queue($this->cookie->forget(self::REFRESH_TOKEN));
}
At that point you cannot use Auth::user() since that function is the functionality for resolving that. So what you need to do is extract the bearer token with $request->bearerToken() and use that to retrieve your user.
Update
I took a look at your code and I would recommend the following:
An API is recommended to be 'stateless' meaning that it should not persist any state (i.e. cookies). It is far better to pass the access token with each request and let the application that accesses your API handle the tokens. Therefore I would recommend to remove the log-out functionality. Then you can do the following in your AuthServiceProvider:
if ($token_exists) {
$user = User::find($token->user_id);
return $user;
}

Laravel rest API returns user object in the post request

I created an API for login and register with the passport package in Laravel. When I call the login url in Postman it returns key_token, refresh token and expired_date.
But I want also want to return the authorized user info in json (eg the username and pass and email).
I need this because the mobile phone team wants to integrate my web app.
Login controller method:
public function login(Request $request)
{
$this->validate($request,[
'username'=>'required',
'password'=>'required'
]);
$params = [
'grant_type'=>'password',
'client_id'=>$this->client->id,
'client_secret'=>$this->client->secret,
'username'=>request('username'),
'password'=>request('password'),
'scope'=>'*'
];
$request->request->add($params);
$proxy=Request::create('oauth/token','POST');
return Route::dispatch($proxy);
}
In my opinion, the best way to go is to create another URI that returns the authenticated user. This way, the client signs in and then performs a GET request to get the authenticated user. I like to simply set this URI to /user. Laravel side, you just have to create a route like this:
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:api');
By the way, this is how the GitHub API works.
Hope that helps.

How to have a route for both authenticated users and non-authenticated users

I have an issue with auth:api middleware!
We have a request that is accessible for both authenticated users and non-authenticated users when I define a route like this for non-authenticated users:
Route::post('{user}/leads', 'UsersController#getContact');
It's ok everything work fine when a guest user requesting this route.
is and I can access user with $request->user();
but if pass token with bearer header and get the user with $request->user() of course it doesn't work! because we didn't use auth:api on this route, and if we do we can't access this route with guest users!
So I can't find a way that we define one route for both authenticated users that if user is authenticated we get $request->user() and none authenticated users can access that route too!
Thanks in advance.
I found a way to do that I just wrote this:
$middleware = ['api'];
if (\Request::header('Authorization'))
$middleware = array_merge(['auth:api']);
Route::group(['prefix' => 'v1', 'namespace' => 'Api', 'middleware' => $middleware], function () {
//routes here
});
In api.php route file and it works fine.
Thanks
This is because Auth uses the default web guard. You have to check the api guard manually:
$user = Auth::user() ?? Auth::guard("api")->user();
Then you don't use any auth middleware. $user will be null if the user is a guest, otherwise it should be set.
The solution I used was to create a new middleware for auth:
public function handle($request, Closure $next, ...$guards)
{
try
{
$this->authenticate($request, $guards);
}
catch(AuthenticationException $ex)
{
}
return $next($request);
}
and in at the BOTTOM of my route I did:
Route::middleware('auth_optional:api')->group(function () {
Route::get('services', [ServiceController::class,'index']);
});
This way if Auth was needed ,it would assign the correct user to request, otherwise it would proceed as guest. I did need to do a $request->user() === null to make sure the user is guest
If you want the routes are visible to only Authenticate users you can put all routes in auth middleware that is default provided by laravel you can put like this:-
enter code here
Route::group(['middleware' => ['auth']], function () {
Route::post('{user}/leads', 'UsersController#getContact');
});
And if you want to show the route both authenticate and non-authenticate user
You can simply put outside the middleware
Lik that:-
Route::match(['get', 'post'], '/cms-page','CmsController#cms');
Hope you understand
I would like to use additional routes both authenticated and non-authenticated users,
But regarding the topic I add one simple way :
On the __constructor function of the Controller add those lines :
$authorizationHeader = \request()->header('Authorization');
if(isset($authorizationHeader)) {
$this->middleware('auth:api');
}
But I don't concentrate this way as best practice, this brokes Single Responsibility Principle.
If you are using Laravel Passport then this way can be more cleaner.
In controller you can directly get user by
$user = $request->user('api');
This will get you the authenticated user and if bearer token is invalid then it will not throw 'unauthenticated' error but result in null user.
Reference: How to authenticate user without auth:api middleware in laravel 5.3?

Resources