want to login in laravel passport multiauth - laravel

here how i can pass my new guard name is trainer
if (auth()->attempt($credentials)) {
$token = auth()->user()->createToken('TutsForWeb')->accessToken;
return response()->json(['token' => $token], 200);
} else {
return response()->json(['error' => 'UnAuthorised'], 401);
}
here I am using
'Auth::guard('trainer')->attempt($credentials)'
but it show Method Illuminate\Auth\RequestGuard::attempt does not exist.
i am using this code
https://github.com/tusharkhan/Laravel-Passport-MultiAuth/blob/master/app/Http/Controllers/PassportController.php

Actually,
The attempt method is part of the SessionGuard you cannot use Passport, and attempt to log in a user, Passport is used for API authentication, and APIs typically use tokens to authenticate users and do not maintain session state between requests.
To make it work change your driver for the trainer guard to session like this
'trainer' => [
'driver' => 'session',
'provider' => 'trainers',// Your trainer table name here
],

Related

Laravel API Endpoint "401 Unauthorized" on Server But Works Fine On Localhost

Background
I have developed a React app that uses Laravel as an API. I have added logins via Passport and have been using the Personal Access Client approach quite successfully. I can add new users and tokens, I can revoke tokens, I can reset passwords... All API calls (except login and register) are guarded by the API middleware and it works. If I remove the Bearer ${token} from the header on any of these calls it returns 401 unauthenticated due to the ->middleware('auth:api') wrapper.
The Problem
Everything works completely as expected... until I move everything to my Raspberry Pi server. As soon as I moved everything, the problem began. I can login and I can register, but as soon as I use the new bearer token (that I received from my login or register call(s)) on any of the endpoint calls that follow in my flow, it fails with 401 unauthenticated, immediately. I ran the php artisan passport:client --personal command and successfully entered the id and secret into my .env file as usual. I installed all the composer and vendor packages. I installed all passport package(s) and CLI commands.
It only fails on calls that use the auth middleware.
I have done some digging and it seems the only change I can find (significantly) is that the Pi runs a 32 bit PHP where my localhost runs a 64 bit PHP. Other than that its the same code, DB, versions of Laravel and PHP, everything.
I have tried using the command php artisan passport:client --personal --name="app-name" --redirect_uri="http://192.168.1.1/" which puts a record in the "oauth_clients" table but shows the redirect as http://localhost/. I then try to use SQL to change the value of the column named "redirect" to http://localhost/, manually... but again the change does nothing. Calls still return 401 unauthenticated.
The only other things I can find that might be an issue are:
The fact that all tokens in the database table "oauth_access_tokens", under the column called "redirect", are created with the redirect_uri of http://localhost. No matter what I do it's always localhost and not my servers domain or IP address (which is concerning). Manually changing SQL as I said does nothing but I know Laravel uses a few "read-only" columns for auth so I wonder if this is one of them... perhaps personal access tokens only work on localhost?
My email_verified_at column in my "users" table (generated by passport commands) is null because I was not able to setup the "forgot my password" flow of Passport on localhost since emails won't send out on localhost.
What I have setup is this:
public function boot()
{
$this->registerPolicies();
Passport::pruneRevokedTokens();
Passport::tokensExpireIn(Carbon::now()->addDays(1));
Passport::refreshTokensExpireIn(Carbon::now()->addDays(14));
Passport::personalAccessTokensExpireIn(Carbon::now()->addDays(1));
}
AuthServiceProvider Class
public function register(Request $request) {
$validatedData = $request->validate([
'image_url' => 'required',
'last_name' => 'required|max:55',
'image_url' => 'required|max:250',
'first_name' => 'required|max:55',
'password' => 'required|confirmed',
'email' => 'email|required|unique:users',
]);
$validatedData['password'] = bcrypt($request->password);
if ($request->hasFile('image_url')) {
$imageFile = $request->file('image_url');
$imageExtension = $imageFile->extension();
if (strtolower($imageExtension) === 'png' || strtolower($imageExtension) === 'jpg') {
$validatedData['image_url'] = Storage::url( $request->file('image_url')->store('user_pics', 'public') );
}
$user = User::create($validatedData);
date_default_timezone_set('UTC');
$date = new \DateTime( date('Y-m-d H:i:s') );
$user->email_verified_at = $date->format('c');
$accessToken = $user->createToken('authToken-'.$user->id, ['*'])->accessToken;
return response([ 'user' => $user, 'access_token' => $accessToken ]);
} else {
abort(404, 'Cannot register user without a user image!');
}
}
public function login(Request $request) {
$loginData = $request->validate([
'email' => 'email|required',
'password' => 'required'
]);
if (!auth()->attempt($loginData)) {
return response()->json(['statusText' => 'Unauthorized'], 401);
}
$user = auth()->user();
$accessToken = auth()->user()->createToken('authToken-'.$user->id, ['*'])->accessToken;
return response([ 'user' => $user, 'access_token' => $accessToken ]);
}
public function logout(Request $request) {
if (auth()->guard('api')->check()) {
auth()->guard('api')->user()->OauthAcessToken()->delete();
return response()->json([ 'msg' => 'Successfully logged out!' ]);
} else {
return abort(404, 'Must be logged in to log a user out');
}
}
public function refreshToken(Request $request) {
if (auth()->guard('api')->check()) {
$user = auth()->user();
$accessToken = auth()->user()->createToken('authToken-'.$user->id, ['*'])->accessToken;
return response([ 'user' => $user, 'access_token' => $accessToken ]);
} else {
return abort(404, 'Must be logged in to refresh a token!');
}
}
AuthController Class
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users'
],
],
config/Auth.php
APP_NAME=MyName
APP_ENV=dev
APP_DEBUG=true
APP_URL=http://192.168.1.1
PASSPORT_PERSONAL_ACCESS_CLIENT_ID="1"
PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="[SOME LONG HASH]"
.env File
Finally solved it!!
Turns out it was Apache on the Raspberry Pi server blocking the Authorization header. This finally unblocked me and solved my issues.
For anyone else coming from a Google search, you can go into your /etc/apache2/apache2.conf file and at the very bottom, paste:
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
I am using a Raspberry Pi 4 with 32 bit PHP and Apache2.
Also, I didn't mention in my post that I have been using the following for my apache server root htaccess:
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
.htaccess file, server root

Laravel API response Unauthenticated even when Authentication is passed

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',
],
],

Laravel: Set a cookie on successful login using a custom guard/attempt method

I am using a third party database for authentication. Everything is working great but now would like to set a cookie when a user has logged in.
As stated in the Laravel Docs:
The attempt method will return true if authentication was successful. Otherwise, false will be returned.
This is what I am doing in my controller:
MyLoginController.php
$user = Auth::guard('foo')->attempt(['userid' => $request->username, 'password' => $request->password], $request->remember);
dd($user);
...
return redirect()->intended(route('home'));
Everything here is great. I'm getting true or false back as expected.
What I am trying to do is if the login is successful, set a cookie on the response. I need the user object back to get a value from. Something like this:
MyLoginController.php
$user = Auth::guard('foo')->attempt(['userid' => $request->username, 'password' => $request->password], $request->remember);
if ($user) {
switch (App::environment()) {
case 'local':
$cookie = cookie('localCookieName', $user->token, 480);
break;
case 'development':
$cookie = cookie('devCookieName', $user->token, 480);
break;
case 'production':
$cookie = cookie('cookieName', $user->token, 480);
break;
default:
//
break;
}
return redirect()->intended(route('home'))->cookie($cookie);
}
return redirect()->intended(route('home'));
I am using a custom User Provider to authenticate my users - everything there is working great as well. I am getting the user, and saving any data to my local db if needed. I thought I might be able to just set the cookie in the UserProvider, but without doing ->cookie($cookie) nothing is getting set.
The value of $user->token is coming back from my 3rd party authentication. So that's why I need to be able to access that value.
Reading the docs, it looks like I need to be setting cookie(s) on the response ->cookie($cookie) or withCookies($cookies).
This leads me to believe I need to set the cookie on my controller, but I'm not sure how to get the user object back since the attempt method only returns true or false.
How can I get the user object from within the attempt method? Maybe I am making thins incredibly difficult for myself and there is an easier way to set the cookie?
Thank you for any suggestions!
EDIT
Here is my config/auth.php file:
...
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
'foo' => [
'driver' => 'session',
'provider' => 'foo',
],
],
...
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'foo' => [
'driver' => 'foo', // Using a 3rd party for auth.
'model' => App\MyUser::class, // User model for auth.
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
attempt does a login if the credentials are valid and correct for a User. So you can just get the user from the Request or the Auth guard, since they are logged in:
$user = $request->user();
$user = $request->auth('foo')->user();
$user = Auth::guard('foo')->user();
...
If you know that attempt passed, the User is also available via getLastAttempted on the session guard:
$user = Auth::guard('foo')->getLastAttempted();
Although you can use that I would not, as you have to check that attempt actually returned true before trusting this value. This holds the last user retrieved by credentials, which could not have been authenticated potentially, attempt returned false.
You do not have to directly be adding a cookie to the Response. In the Cookie section of the docs should be information about "queue"ing a cookie to automatically be attached to the outgoing Response:
Cookie::queue('name', 'value', $minutes);
Laravel 6.x Docs - Responses - Attaching Cookies to Responses

Auth::user(); doesn't returns users, using passport

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!

Laravel: How to use Gates with multiple 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.

Resources