How to user Laravel resource with /user api of Authentication - laravel

I am building a project with Laravel and Vue using Axios and Passport.
My authentication is working and generating token and saving in my local storage to check for login.
I am also getting the data using
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
However, my user also has some relationship which I have described in my model like
public function types()
{
return $this->belongsTo(types::class, 'type_id');
}
and my user resource looks like this
public function toArray($request)
{
$array = parent::toArray($request);
$array['group'] = $this->groups;
$array['type'] = $this->types->typeName;
return $array;
}
So when user login I am trying to get user data using auth:api however I want a relationship to come with it.
I tried
Route::middleware('auth:api')->get('/user', function (Request $request) {
return UserResource::collection($request->user());
});
and get error
> Call to undefined method App\User::mapInto()
I also tried
```php
return new UserResource::collection($request->user());
error: syntax error, unexpected 'collection' (T_STRING), expecting variable (T_VARIABLE) or '$'
return UserResource::collection($request->user()-get());
error: Trying to get property 'typeName' of non-object
So what am I doing wrong? thanks for your time and if need more details please let me know

You can achieve similar, if not better results using Eager loading. source
Sample:
Route::get('me', function(Request $request) {
$request->user()->load('types');
});

Related

How can I change Laravel Policy response status code?

I'm using Laravel 9
I want to prevent User to view another User personal information.
This is my Policy method
public function viewUser(User $user, User $model)
{
return $user->id === $model->id;
}
and this is Controller method
public function show(User $user)
{
$this->authorize('viewUser', $user);
return view('users.show', compact('user'));
}
It shows 403 as expected
But I want to change status code to 404 like this
public function viewUser(User $user, User $model)
{
return $user->id === $model->id
? Response::allow()
: Response::deny(code: 404);
}
And it's still shows 403 not 404. What am I doing wrong with the Policy? I know I can change response using another approach but my question about Laravel Policies itself.
Route
Route::group([
'middleware' => ['auth'],
'prefix' => 'users/{user}',
'as' => 'users.',
], function () {
Route::get('/', [UserController::class, 'show'])->name('show');
});
In app\Exceptions\Handler.php in the render() method you can define what should be done when X Exception is thrown. That being said, adding below piece of code should do the trick for you:
if ($exception instanceof AuthorizationException) {
$exception = new NotFoundHttpException;
}
return parent::render($request, $exception);
What it does is basically checking if the Exception that is thrown is an AuthorizationException (which policies in Laravel throw) and if that is the case, throw a new NotFoundHttpException (404). This will however change any and all AuthorizationExceptions to a 404, which is probably not wanted behaviour.
Update:
After digging I found a closed proposal for returning 404.
Personally I feel that a policy should not return anything but a 403 status code, since that is the correct code, something is forbidden. Returning a 404 would not be correct since the policy does not handle X resource not found.
If you'd really want you could change the Handler.php. I feel like this is not the correct way to use policies however, but that's beside the point.
A user in above mentioned closed proposel used request parameters to check if the route belonged to a certain type, i.e. product and returned a 404 instead of 403. Maybe this can be applied to your use case aswell, check it out here. Hope I've been thorough and it maybe helps.
Since Laravel 9.20 it can be achieved with denyAsNotFound method - link to docs
public function viewUser(User $user, User $model)
{
return $user->id === $model->id ?
$this->allow() :
$this->denyAsNotFound();
}
or more universal way with denyWithStatus()
public function viewUser(User $user, User $model)
{
return $user->id === $model->id ?
$this->allow() :
$this->denyWithStatus(404);
}

I have problem when i logout from admin "Trying to get property 'id' of non-object" using laravel

I am trying to logout from admin but unfortunately, I face error Trying to get property 'id' of non-object How to fix this error? please help me thanks.
public function index(){
$user_permission = Users_Permissions::with('user')
->Where('user_id',Auth::user()->id)
->paginate(5);
return view('index',compact('user_permission'));
}
The problem is clear. When you log out, then Auth::user() is null. so there is no id . You can solve the issue like this.
public function index()
{
if (Auth::check()) {
$user_permission = Users_Permissions::with('user')->Where('user_id',Auth::user()->id)
->paginate(5);
return view('index',compact('user_permission'));
} else {
// The condition when no user logged in
// For an example
return redirect('login'); // This is just an example
}
For that, use try & catch in every function, and use auth middleware for specific routes while you are working with auth users.
so that specific routes are required to use auth, so you don't need to check in every function. auth is always present.
Route::middleware('auth')->group(function () {
Route::get('logout', 'AuthController#logout');
});
or else you can use middleware in a controller too,
class AuthController extends Controller
{
public function __construct(){
$this->middleware(['guest'])->except('logout');
}
}
It's because after logout you don't have user ID, so in this situation the User is null, and you want to get the if from null object. you can change this part of code like below
Users_Permissions::with('user')->Where('user_id',Auth::user()->id ?? 0)
then you pass the error. you can put every number or null in query based on your needs.

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;
}

Getting 403, error when using private channels with laravel broadcast

Unable to authenticate users in my chat app. I am getting a 403 error from the console. This happens though when I use private channels, but when using a public channel, this is working really fine but I definitely want authenticated users only.
It is more like an spa, hence using axios for almost everything including user authentication requests to laravel.
below is my code:
BroadcastServiceProvider:
` public function boot()
{
Broadcast::routes();
require base_path('routes/channels.php');
}`
Channels.php:
`Broadcast::channel('App.User.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});
Broadcast::channel('chat', function ($user) {
return Auth::check();
});
`
listen directive from vue component:
`Echo.private('chat')
.listen('.App\\Events\\Chats\\MessageSent', (e) => {
console.log(e);
this.sentMessages.push({
message: e.message.message,
user: e.user
});
`
MessageSent event:
` public function broadcastOn()
{
return new PrivateChannel('chat');
}
`
Now using the vue-echo wrapper but still I got this problem, I still haven't figured out what I am missing
It's is just basically as the error suggests, an authentication problem, well I am using tokens for authentication in my app but now needed to also pass this token issued to the user as well to vue-echo.
And also change:
Broadcast::routes();
to
Broadcast::routes(['middleware' => ['auth:api']]);
since am creating a single page application hence using axios for authentication which therefore interprets to me using the api middleware.
Guided by the answer provided by Alex on the question 'Laravel /broadcasting/auth Always Fails With 403 Error
'
You can get more details there.
Thanks all .

Laravel Passport post route test on postman

I set up Laravel Passport and started to create a few Get routes to get some data, that worked fine.
Now I am trying to post to get on auth token, which currently does not work:
This is my route which I call (Get route works, Post route does not work):
Route::group(['middleware' => 'auth:api'], function ()
{;
Route::get('users', ['as' => 'users', 'uses' => 'ApiController#users']);
Route::post('login/{id}/{name}', ['as' => 'login', 'uses' => 'ApiController#login']);
});
The method in my ApiController looks currently like this:
public function login(Request $request, $id, $name)
{
if($request->isMethod('post'))
{
$id = $request->id;
$name = $request->name;
$inquiry = new Inquiry();
$inquiry->user_id = $id;
$inquiry->user_name = $name;
if($inquiry->save())
{
return redirect()->route('inquiry.index')->with('success', 'Success.');
}
else
{
return redirect()->route('inquiry.index')->with('error', 'An error accured.')->withInput();
}
}
else
{
dd("Use Post.");
}
}
I tried to call it with following options:
Edit
I somehow managed to get this work after many hours, but still dont understand something.
First I did following:
public function callback(Request $request)
{
dd($request->code) // this holds a token I need for the code parameter in the post
...
With that I could get the token for the code parameter, but I think there is a better way to do that.
And finally this is now how I get the access + refresh token:
But there has to be a better way to get the code token of the callback request ($request->code), instead of dumping it and copying it.
The problem is that you have your login route inside a route group with auth:api on it. This means the user needs to be authenticated to even be able to authenticate. Just remove the login route outside the group and you should be fine.
You should call the Passport::routes method within the boot method of your AuthServiceProvider. This method will register the routes necessary to issue access tokens and revoke access tokens, clients, and personal access tokens:
public function boot()
{
$this->registerPolicies();
Passport::routes();
}
/oauth/authorize route is already defined by the Passport::routes method. You do not need to manually define this route.

Resources