I am trying to figure out to provide multiple ways of authentication for the API service within my Laravel app. The app is a SPA using Vue.js and uses the API route to render and present all the view components. Currently, I am using a JWT driver for the API guard within the application. However, I'd also like to offer my clients the ability to access the same API via OAuth and Laravel's personal API token. With that being said, how do I protect my resources with the Auth middleware where it can be accessed internally with a JWT or externally by a client with OAuth or an API Token.
Controller:
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
// Make sure user is authenticated
$this->middleware('auth:api');
//$this->middleware('auth:oauth');
}
Auth Guards:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
'oauth' => [
'driver' => 'token',
'provider' => 'users',
]
],
If you want to be able allow multiple guards for your routes you can supply the different guards to the middleware call, like you have done already with the api guard, except you supply them as comma separated values:
$this->middleware('auth:api,oauth,web');
This will mean that if a user has been authenticated with one of the guards they will be able to access the route(s).
Related
I am using the jwt for creating the tokens while login. After I login, I try to hit the /me api pointing to the function:
public function me()
{
$user = auth()->user();
return response()->json($user);
}
I followed the JWT official documentation, initially I was able to get the response for the API. Suddenly it started throwing a
{
"message": "Unauthenticated."
}
Why is this happening?? Is there any workaround? It would be great if someone could help.
i tried documentation setup and worked fine, you might missed passing authentication header in your api call. since idk what's your setup i can only tell when you logged in, you should use received token in api calls with authentication.
PostMan Software: In headers tab add a key as Authorization and assign token for value with Bearer, like Breaer token......
for more help please clarify how you're trying api calls.
Edit: added an alternate way for using middleware
Another way of implementing or using middleware :
Create a Middleware with JWT name and put below code in handle function
Don't forget to import
use JWAuth;
public function handle($request, Closure $next)
{
JWTAuth::parseToken()->authenticate();
return $next($request);
}
Then in Kernel add jwt to $routeMiddleware like this :
protected $routeMiddleware = [
// you should add below code.
'jwt' => \App\Http\Middleware\JWT::class,
];
in routes/api
Route::apiResource('/posts', 'PostController');
now in PostController add your middleware to Constructor like this.
public function __construct()
{
$this->middleware('jwt', ['except' => ['index','show']]);
}
So in construct you will set your middleware base on JWT, then with except you can modify which one of your functions don't need to authentication base on JWT token. now when you use auth()->user() you can get your info or etc.
So if i had index, show, update, delete, store, create when i try to do API call if i use GET METHOD for url.com/posts or url.com/posts/23 i can get my posts without passing JWT token.
When you tried to use JWT you should realize that it's working base on token you're passing, you're getting token when you using login, but you're not getting user info because you're not passing user's token to app, before all of this you should consider to verify token then do the rest Logics. Good Luck.
Edit : added more info
auth.php
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
I'm using this package for "jwt-auth" for my Laravel backend project, here is the link for the package:
https://jwt-auth.readthedocs.io/en/develop/
I have 2 middleware that I put the names Tenant and JWT, when my user tries to log in to the app he must send the company code, so my middleware Tenant picks the information from the specific connection database client and all is working fine.
But when I use 2 middlewares together, he gives me an error that I don't have a user table. He is right because when I made a searching from the problem I found that the package executes a function before all my 2 middlewares that is this:
/**
* Get the currently authenticated user.
*
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user()
{
// MODIFICAÇÃO CUSTOMIZADA DA FUNÇÃO USER
if ($this->user !== null) {
return $this->user;
}
if ($this->jwt->setRequest($this->request)->getToken() &&
($payload = $this->jwt->check(true)) &&
$this->validateSubject()
) {
return $this->user = $this->provider->retrieveById($payload['sub']);
}
}
The problem was solved if I comment this line "return $this->user = $this->provider->retrieveById($payload['sub']);", but this is not a good practice. The main reason for this error is that this function is executed before my Tenant middleware that doesn't have a user table in the database that Tenant middleware tries to connect.
The file name is **JWT_GUARD.php in tymon\jwt-auth\src**, i'm think is something about my configuration that i must change in
config/auth.php from laravel
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
And here:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
I comment on this part "return $this->user = $this->provider->retrieveById($payload['sub']);" and waiting for a better solution
I have situation about returning users from DB. In my controller I am trying it like below:
UPDATED:
NOTE: for clear misunderstanding. Actually I am logged in as a user. No problem with that part. But it looks like auth:: doesn't understand that and when I try to retrieve users. it's redirecting me to login's endpoint...
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Auth;
class UsersController extends Controller
{
public function getUser(){
$users = Auth::user();
dd($users);
}
}
And about the api route:
Route::group(['middleware' => 'auth:api'], function() {
Route::post("logout", "Api\AuthController#logout");
/* User */
Route::get('/user', 'Api\UsersController#getUser');
});
Route::group(["prefix" => "v1"], function(){
/* Auth */
Route::post("login", "Api\AuthController#login")->name("login");
Route::post("register", "Api\AuthController#register");
});
Here is the thing. If I use my UserController route outside the middleware:api then endpoint is returns null. And if use it inside the middleware it redirects me to my login's endpoint. Because of the "->name('login')"
In the end I can't return the users. Additionally this is what config/auth looks like.
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
By the way before asked. I tried to change guard's web to api but nothing is changed.
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
Is there anyone have better understanding on this situation. How can I return users with using passport? Do I missing something here?
Apparently, the problem is with the request header. Only a logged in user can call /api/user endpoint with an access_token in the request header.
Request header will have this pair
Authorization: Bearer eyJ0eXAiOiJKV1..........
Nothing to do in laravel part, as it's working as expected.
If you are using Laravel Passport. Let's read and make your step same in document: https://laravel.com/docs/5.8/passport
From your API request, need to pass the access_token to backend.
Hoping you can resolve that issue!
I am working on an application which contains its front end and admin panel
How do I implement separate authentication for them?
Let's say for ex. Front-end authentication is working fine I'm using the standard auth()->attempt() but what about admin panel I think I can not use the same for the admin panel,
once if I logged in from front-end then if I check dd(auth()->user()) in admin area somewhere but it returns the front end users data.
In short, I have been stuck in two Authentication can someone tell me the logic "How do I implement two separate authentication one for admin panel and for front end"
Thanks
if you are using laravel inbuilt authentication method, you can use custom guards
In your auth.php file add custom guard
'guards' => [
'admin' => [
'driver' => 'session',
'provider' => 'users',
],
'customer' => [
'driver' => 'session',
'provider' => 'customers',
],
]
and access by
Auth::guard('customer')->attempt() function.
for more details https://laravel.com/docs/5.6/authentication#adding-custom-guards
I have a traditional web application that has a number of different user types, and each user type has its own Authentication guard.
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admin',
],
'timekeeper' => [
'driver' => 'session',
'provider' => 'timekeeper',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
],
],
Most my users authenticate using the 'web' guard, however administrators and timekeepers each use their own guard, which is attached to an appropriate user provider.
This is fine until I try to use authentication gates. If I authenticate a user against the system's default guard (e.g. 'web'), then the gates work as expected. If I authenticate against any other guard however, then all Gate::allows(...) calls are DENIED.
Even the following ability is denied:
Gate::define('read', function ($user) {
return true;
});
Presumably this is due to line 284-286 in Illuminate\Auth\Access\Gate:
if (! $user = $this->resolveUser()) {
return false;
}
As far as I can see, my options are to:
Go back to using a single 'web' guard, with a user provider that can locate any type of user (but I'm not sure how that would work if I start using an API in parallel)
Somehow set the default guard at run time, depending on the type of the current user. (It is currently set in the config file)
Somehow inject a different user resolver in to the Gate facade (again, depending on the type of the current user)
None of these seems intuitive however. Am I missing something?
It's not the most elegant solution because it requires a lot of extra boilerplate code, but you can use Gate::forUser($user)->allows() instead of just Gate::allows() where $user comes from Auth::guard().
I had the same problem and I didn't really like this solution. After quite a lot of research I came up with this way to make your own user resolver in the Gate:
public function register()
{
$this->app->singleton(GateContract::class, function ($app) {
return new \Illuminate\Auth\Access\Gate($app, function () use($app) {
$user = call_user_func($app['auth']->userResolver());
if (is_null($user)) {
// Implement your own logic for resolving the user
}
return $user;
});
});
}
I put this in my AuthServiceProvider.