Im making a gym app on laravel 9 as an api. The app will have a normal user and also an admin which has access to certain files that a normal user doesnt. For that i created a middleware but im facing a few problems there.
See even after i log in, when i try using a route protected by my isAdmin middleware, i get the error: Log in to gain access. This is the Login function:
public function login(Request $request)
{
$creds = $request->validate([
'email' => 'required|email|string|exists:users,email',
'password' => ['required']
]);
if (!Auth::attempt($creds)) {
return response([ 'message' => 'Provided email or password is incorrect' ], 422);
}
/** #var \App\Models\User $user */
$user = Auth::user();
Auth::login($user);
$token = $user->createToken('main')->plainTextToken;
return response(compact('user', 'token'))->header('Authorization', 'Bearer '.$token);
}
and then this is the middleware
public function handle(Request $request, Closure $next)
{
if (Auth::check()) {
$user = Auth::guard('auth')->user();
dd($user);
if ($user->role == 1) {
return $next($request);
} else{
return response(['msg'=>'You are not authorized for this route!']);
}
} else{
return response(['msg'=>'Log In to gain access!']);
}
return $next($request);
}
Im pretty sure the problem has to do with the token either not being sent or being stored properly, but i cant seem to find where the problem is. Also this is what im doing in Postman if that would help:
Login
Add Recipe (this is a route guarded by the middleware)
And also here are their headers respectively
Also i dont know if this is info that will help but i have to manually write the that bearer token for it to show up. (im a beginner so idk how it should be done)
I have implemented a custom middleware in my application. And I have found an issue.
It somehow does't reach the abort(403), but instead gives me this error:
"No query results for model [App\Models\Meeting]"
My goal is to redirect the user back if they're on a meeting page that doesn't exist/exists to another user. But instead it doesn't redirect me, but sends me that error.
Here is my middleware:
public function handle(Request $request, Closure $next)
{
$userid = Auth::user()->id;
$meetings = Meeting::where('user_id', $userid)->get();
$url = $request->segment(4);
$idArr = [];
foreach ($meetings as $meeting) {
array_push($idArr, $meeting->id);
}
if (!in_array($url, $idArr)) {
abort(403);
} else {
return $next($request);
}
}
The url is just the id of the meeting, so that works fine.
For your info, it can reach the middleware, so it is connected/added properly. It just doesn't access the if code.
For what it's worth, here is my controller where I connect the middleware.
public function __construct()
{
$this->middleware(CheckMeeting::class)->only('show');
}
Can anyone explain or help me? Thanks in advance.
edit:
These are my routes
Route::apiResource('login', LoginController::class)->only(['index', 'store', 'show']);
Route::delete('logout', [LoginController::class, 'destroy']);
Route::apiResource('register', RegisterController::class)->only('store');
Route::apiResource('meeting', MeetingController::class)->middleware('auth:sanctum');
Route::post('keywords', [FileController::class, 'index']);
Route::apiResource('file', FileController::class)->middleware('auth:sanctum')->except(['index']);
Route::apiResource('userignoreword', IgnoreWordController::class)->only(['store'])->middleware('auth:sanctum');
the URL I use is: http://xxx/meeting/10
xxx is the placeholder as it is not relevant.
Try using response helper
return response(null,403)
I'm trying to implement authentication & authorization of users between my microservices and API Gateway.What I have now:
API Gateway which can request to any microservice.
User microservice - where I'm storing all users. laravel/passport implemented to authenticate user in this microservice. Works as it should be, login route returns token which I'm using to authenticate user in this microservice.
Other 5 microservices without any authentication or authorization.
Question is: what is the right way to use authentication & authorization with microservices? I know that I should authenticate users in my API Gateway and authorization will happen inside microservices. But how authorization in other microservices happening if they don't know anything about users?
I'm planning to use somehow JWT token with information about user roles but haven't found yet how to put that information into token
I'll try to explain with a basic example for API.
Let's say you have currently 3 microservices :
Users
Posts
Core
I assume you're using httpOnly cookie to store user token.
In Core microservice I have this route structure:
Route::prefix('core')->group(function () {
Route::post('register', [AuthController::class, 'register']);
Route::post('login', [AuthController::class, 'login']);
Route::middleware('scope.trader')->group(function () {
Route::get('user', [AuthController::class, 'user']);
});
});
Now i want to login which i should send an API request, and I should think of a solution to send token anytime I need it.
login(this is where you get token) and register don't need token
user need token (this is where you asked for solution)
So in addition to get a result, I should create a service for user, and here how I've done it :
UserService :
class UserService extends ApiService
{
public function __construct()
{
// Get User Endpoint Microservice API URL
$this->endpoint = env('USERS_MS') . '/api';
}
}
ApiService :
abstract class ApiService
{
protected string $endpoint;
public function request($method, $path, $data = [])
{
$response = $this->getRequest($method, $path, $data);
if ($response->ok()) {return $response->json();};
throw new HttpException($response->status(), $response->body());
}
public function getRequest($method, $path, $data = [])
{
return \Http::acceptJson()->withHeaders([
'Authorization' => 'Bearer ' . request()->cookie('token')
])->$method("{$this->endpoint}/{$path}", $data);
}
public function post($path, $data)
{
return $this->request('post', $path, $data);
}
public function get($path)
{
return $this->request('get', $path);
}
public function put($path, $data)
{
return $this->request('put', $path, $data);
}
public function delete($path)
{
return $this->request('delete', $path);
}
}
If you're wondering where, this UserService come from, then I should say, I've created a package to use it in other microservices, so you can do the same or just create a service and use it in your microservices or etc.
Everything is obvious about ApiService, but I'll try to explain the base.
Anytime we want to do an API call, we can simply call Allowed methods in this class, then our methods, will call request, to pass common arguments, and eventually using those arguments to do the API call.
getRequest method, is doing the call and get the stored token from httpOnly cookie, and will send it as an Authorization header to the target endpoint, and eventually it'll return whatever it get from target.
So If we want to use this, we can simply do like this in our controller :
class AuthController extends Controller
{
// use Services\UserService;
public UserService $userService;
/**
* #param UserService $userService
*/
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function register(RegisterRequest $request)
{
$data = $request->only('name', 'email', 'password') + ['additional_fileds' => 0 ];
// additional fields can be used for something except from request and
// optional, like is it admin or user or etc.
// call the post method, pass the endpoint url(`register`), pass $data
$user = $this->userService->post('register', $data);
// get data from target endpoint
// and ...
return response($user, Response::HTTP_CREATED);
}
public function login(Request $request)
{
// same thing here again, but this time i passed scope to help me
// get the specific user scope
$data = $request->only('email', 'password') + ['scope' => 'writer'];
$response = $this->userService->post('login', $data);
// as you can see when user do success login, we will get token,
// which i got that token using Passport and set it to $cookie
$cookie = cookie('token', $response['token'], 60 * 24); // 1 day
// then will set a new httpOnly token on response.
return response([
'message' => 'success'
])->withCookie($cookie);
}
public function user(Request $request)
{
// Here, base on userService as you saw, we passed token in all requests
// which if token exist, we get the result, since we're expecting
// token to send back the user informations.
$user = $this->userService->get('user');
// get posts belong to authenticated user
$posts = Post::where('user_id', $user['id'])->get();
$user['posts'] = $posts;
return $user;
}
}
Now, how about user microservice? well Everything is clear here, and it should work like a basic app.
Here's the routes :
Route::post('register', [AuthController::class, 'register']);
Route::post('login', [AuthController::class, 'login']);
Route::middleware(['bunch','of', 'middlewares'])->group( function (){
Route::get('user', [AuthController::class, 'user']);
});
And in controller :
class AuthController extends Controller
{
public function register(Request $request)
{
$user = User::create(
$request->only('first_name', 'email', 'additional_field')
+ ['password' => \Hash::make($request->input('password'))]
);
return response($user, Response::HTTP_CREATED);
}
public function login(Request $request)
{
if (!\Auth::attempt($request->only('email', 'password'))) {
return response([
'error' => 'user or pass is wrong or whatever.'
], Response::HTTP_UNAUTHORIZED);
}
$user = \Auth::user();
$jwt = $user->createToken('token', [$request->input('here you can pass the required scope like trader as i expalined in top')])->plainTextToken;
return compact('token');
}
public function user(Request $request)
{
return $request->user();
}
}
So here's the complete example and you can use the Core microservice approach on other microservices to get your information related to authenticated user, and as you can see everything will be authenticated due to those requests from core to other microservices.
How to check if the user is authenticated when using Laravel sanctum?
Example :
Controller :
public function testAuth(Request $request)
{
if ($request->user()) {
return "auth";
} else {
return "guest";
}
}
api.php
Route::get('testauth', [MyTestController::class, 'testAuth']);
this route always returns guest even if I pass token in headers.
when I add sanctum middleware, route return auth
api.php
Route::get('testauth', [MyTestController::class, 'testAuth'])->middleware('auth:sanctum');
but I don't want that , I want to check if the user is authenticated in the controller without using middleware
Try this following code will help you.....You can use user('sanctum') instead of user()
public function testAuth(Request $request)
{
if ($request->user('sanctum')) {
return "auth";
} else {
return "guest";
}
}
first attach auth middleware with sanctum guard like this to route
Route::get('/somepage', 'SomeController#MyMethod')->middleware('auth:sanctum');
Then inside route closure/controller action access it with
auth()->user()
as usual
authorization http header must hold your bearer token
return auth('sanctum')->check() ? "Auth" : "Guest";
(I'm aware that the following example isn't safe)
I have a basic authentication system where a user has to enter his credentials. When the entered credentials are correct I set in my laravel application a session, which indicates that he can perform authenticated requests to the api. I do that the following way:
public function authenticate(Request $request){
$data = array(
'name' => $request->input('UserName'),
'password' =>$request->input('Password'),
'email' => 'test#test.ch'
);
If(Auth::attempt($data)){
$request->session()->put('isAuthenticated',true);
$request->session()->save();
return "success";
}
return "wrong";
}
My middleware where I check if the user is allowed to make requests:
public function handle($request, Closure $next){
if(!empty($request->session()->get('isAuthenticated')) && $request->session()->get('isAuthenticated') === true){
return $next($request);
}
return redirect('/');
}
My routes:
Route::post('/login', 'UserController#authenticate');
Route::group(['middleware' =>['web','check_auth']], function (){
Route::get('/logs','LogController#getAllLogEntries');
Route::get('/logs/{id}','LogController#getLogEntryById');
});
My problem:
Whenever a user logs in with the right credentials the server returns "success" as response, but always directs me back to the base root ('/'). That makes me assume that the session doesn't get set. How can I fix this error?