Laravel 9/Passport not working in production - php

I just deployed a Laravel 9 Passport based API to Digital Ocean. Everything works fine, but when I try lo log an user or an admin and create a token to be stored and returned, I get 500 server error.
I already ran php artisan passport:keys. Because it wasn´t working, I tried to pass the keys by .env through php artisan vendor:publish --tag=passport-config, but still got the error.
Here is my LoginController:
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
if (!Auth::attempt($credentials)) {
return response()->json([
'message' => 'Usuario y/o contraseña inválidas'
], 401);
}
// If I comment this line and the accessToken in the json response, and return just the Auth::user(), it works fine
$accessToken = Auth::user()->createToken('my_token')->accessToken;
return response()->json([
'user' => Auth::user(),
'accessToken' => $accessToken
], 200);
}
This is my AuthServiceProvider.php boot method:
public function boot()
{
$this->registerPolicies();
/* Passport::routes(); */
if (!$this->app->routesAreCached()) {
Passport::routes();
}
/**
* Controls the expire time for the access token
*/
Passport::personalAccessTokensExpireIn(now()->addDays(30));
Passport::tokensCan([
'admin' => 'Admin',
'user' => 'User',
]);
}
No errors related are being logged neither in Laravel logs nor in nginx ones.
Thanks for your help.

So I figured out the solution with aynber guess.
I try catched the accesstoken line and set debug to true.
It was missing the Personal Client ID, so I ran php artisan passport:client --personal and it worked.
That happens because the client id is self-generated when you first run artisan passport:install.
But when you are cloning a repo and running a migration and a composer install command, you really never run passport:install, so a new client personal id must be run manually.

Related

Unauthorized 401 error in laravel 6 passport

I'm trying to use the laravel passport to authenticate users. I followed every step in laravel passport documentation. But it shows error when I access a route that is protected by 'auth:api' middleware. I've searched over the internet but any of the answers are not working so far. Please see the code, and what I've tried so far below.
Tech used
Laravel 6
Laravel Passport
Vue 2
Axios
Tried so far:
Changed Passport::routes(); to Passport::routes(null, ['prefix' => 'api/oauth', 'middleware' => ['auth:api', 'web', 'auth']]); in AuthServiceProvider. Also tried Passport::routes(null, ['prefix' => 'api/oauth');
Tried to run php artisan cache:clear
routes/api.php
Route::middleware('auth:api')->group(function() {
Route::get('user', function() {
return request()->user();
});
Route::get('posts', 'PostController#index')->name('posts');
Route::post('posts/store', 'PostController#store')->name('posts.store');
Route::get('posts/{id}/show')->name('posts.show');
Route::get('users/{id}/show')->name('users.show');
});
Axios
mounted() {
axios.get('/api/posts')
.then(res => this.posts = res.data).catch(error => console.log('Unable to fetch posts.'))
}
Error
Headers:
Please let me know if you need some details that is not indicated on my post yet.
Setup
Run composer require laravel/passport:9 for the installation.
Run php artisan migrate for the tables.
Run php artisan passport:install to generate the encryption keys.
Added Laravel\Passport\HasApiTokens trait in User model.
Added Passport::routes(); in AuthServiceProvider.
Changed guard's api driver to passport in config/auth.php.
Full source code:
Click this link
I think you should review your code with the following steps.
Step 1: User model has to use HasApiTokens
...
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
}
Step 2: Passport::routes method within the boot method of your AuthServiceProvider
class AuthServiceProvider extends ServiceProvider
{
...
public function boot()
{
$this->registerPolicies();
Passport::routes();
}
}
Step 3: In your config/auth.php configuration file, you should set the driver option of the api authentication guard to passport
'guards' => [
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
Step 4 (Optional):
If you want to consume your API from your JavaScript application, you need to manually send an access token to the application and pass it with each request to your application. However, Passport includes a middleware that can handle this for you. All you need to do is add the CreateFreshApiToken middleware to your web middleware group in your app/Http/Kernel.php file:
'web' => [
// Other middleware...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
! You should ensure that the CreateFreshApiToken middleware is the last middleware listed in your middleware stack.
Edit (2021/10/17):
You should try to add the auth middleware to $middlewareGroups of your Kernel.php
protected $middlewareGroups = [
...
'api' => [
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
'auth:api', //Need to add
],
];
Edit (2021/10/25):
I downloaded your code and debug it. When you debug it, you will get the following result:
Header JWT is not valid for JSON structure. There seems to be an incompatibility between the Laravel Passport library and your system. You should check your system version (PHP, Laravel, etc). I will continue to investigate. If you have any new information, please let me know
Edit (2021/11/02):
Update your laravel/framework dependency to ^6.0 in your composer.json file. If installed, update your laravel/passport dependency to ^9.3.2 in your composer.json file.
https://laravel.com/docs/6.x/upgrade#updating-dependencies
According to Laravel 6.x documentation, the version of Laravel Passport is from 9.3.2. Please try this version.
If you are using Api/oauth prefix AND add the auth:api middleware than you need to authenticate via Bearer tokens.
So it may be you have csrf token related issue so try below to disable it.
Passport::ignoreCsrfToken(true);
You should add token bearer into your ajax requests. You can get it after successful login.
// laravel
use Illuminate\Support\Facades\Auth;
use App\Models\User;
public function login()
{
$credentials = request(['email', 'password']);
$user = User::where('email', $credentials['email'])->first();
if (!$user || !$token = Auth::attempt($credentials)) {
return response()->json(['success'=> false, 'error' => 'неверный логин или пароль', 'cr' => $credentials], 422);
}
return $this->respondWithToken($token);
}
// vue login
async function login(email, password) {
try {
const response = await axios.post('/auth/login', {email, password});
const token = response.data.token;
// some func to store token, I am using vuex mutations for this
storeToken(token);
}
catch(error) {
console.error(error);
}
};
// vue regular function
async function changePassword(newPassword) {
// get token from global storage, with vuex it will be like store.state.auth.token
const token = getToken();
await axios.post('/user/changePassword', {newPassword}, headers: { Authorization: `Bearer ${token}`});
}
You've forgotten to run php artisan passport:keys ...so there may be no RSA key-pair available, which is crucial. Clients usually can be added with: php artisan passport:client. And generally speaking, there is no default recipe for debugging Laravel middleware ...there only is xdebug.

Lumen 5.8 Auth attempt does not exist

I'm new to Lumen and building a RESTful API and I got stuck for how many hours now because Auth::attempt is not working. I've been searching for any answers but all the results are only Laravel not Lumen.
So I created a AuthController and login method to authenticate user but I got an error.
public function login(Request $request)
{
$this->validate($request, [
'email' => 'required|string|email',
'password' => 'required|string',
]);
$credentials = $request->only('email', 'password');
if( !Auth::attempt($credentials) ) {
return response()->json([
'message' => 'Unauthorized'
], 401);
}
}
This is the error: "Method Illuminate\Auth\RequestGuard::attempt does not exist."
Anyone can help me? Thank you!
Can you try following:
if (!Auth::guard('web')->attempt($credentials) {
return response()->json([
'message' => 'Unauthorized'
], 401);
}
The method is only available for routes which are using the web middleware, could you check that out?
You may need to edit your config/auth file.
Lumen Passport Setup:
composer require dusterio/lumen-passport
Enable the app and auth service providers in bootstrap/app.php
add the following service providers in the bootstrap file
$app->register(Laravel\Passport\PassportServiceProvider::class);
$app->register(Dusterio\LumenPassport\PassportServiceProvider::class);
replace the contents inside the boot function of the auth service provider in /app/Providers/AuthServiceProvider with:
LumenPassport::routes($this->app->router);
Run php artisan migrate
run php artisan passport:install
Then follow this https://laravel.com/docs/5.8/passport#issuing-access-tokens
remeber the lumen version does not have views or routes for authorising access you have to build them seperatly on your client.

php process goes unresponsive when making a HTTP POST method call using GuzzleHttp

I am using guzzle HTTP client to issue a password grant based access token after successful user login. I am using passport package for oauth and I have done all the setup which includes the Password Grant Client it creates. In my login controller I override the sendLoginResponse method of the AuthenticatesUsers trait so as to issue an access token on successful email/password authentication
public function sendLoginResponse(Request $request)
{
try {
Log::debug("Auth attempt sucessful, obtaining access_token for user :: ".$request->email);
$client = new Client();
$token_response = $client->post(config('app.url').'/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => config('auth.password_grant.client.id'),
'client_secret' => config('auth.password_grant.client.secret'),
'username' => $request->email,
'password' => $request->password,
'scope' => '*',
],
]);
if($token_response->getStatusCode()!=200) {
Log:error("Login failed to generate Access Token");
throw new InvalidCredentialsException();
}
$request->session()->regenerate();
$this->clearLoginAttempts($request);
$data = json_decode((string) $token_response->getBody(), true);
Cookie::queue('refresh_token',$data->refresh_token,config('auth.tokens.refresh.expire.days')*1440);
Log::debug("Adding Bearer token to Authorization header");
return response()->view('dashboard', [
'expires_in' => $data->expires_in
], 200)->header('Authorization', $data->token_type.' '.$data->access_token);
} catch(Exception $e){
Log::error('Error :: '.$e->getMessage());
throw $e;
}
}
The whole PHP process goes unresponsive when I make this post request, and there are no errors in any of the logs. Exactly at this line
$token_response = $client->post($token_url, .......
I ran this in Debug session; and the URL, Client ID and Secret are generated correctly via configuration properties; the only Exception I could see was a FileNoFoundException that occurs when it does find any cache key for throttle login and that all happens much before this call is made and the app proceeds to authenticate the user.
When I make this very request with same parameters through Postman or via artisan tinker I can get a response with access_token, refresh_token and expires_in data.
A couple of hours with 'Hit And Trial' does really save you 10 minutes of going through 'Documentation'.
Turns out I really don't have to do all this heavy lifting this link shows how we can add the \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, to web middleware in app/http/Kernel.php which takes care of generating ApiToken for first party apps such as the React JS which I'd be using to consume my own API.
While that solved the intent of writing all this code, I'm still not sure what was causing the process unresponsiveness making the access_token from within the php code.

auth laravel always return false

I often create a login with Laravel, and it runs smoothly, but I don't understand the actual process run by Laravel php artisan make:auth. and now I want to try creating this login process myself to understand the actual process
I'm trying to make the login process run by myself without Laravel make:auth. create a user table with a password that hashed. when logging in Auth::attempt is always false, why?
public function authenticate(Request $request)
{
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials)) {
// Authentication passed...
return redirect()->intended('dashboard');
}
}
This function is in the documentation of Laravel Manually Authenticating Users
if i dd(Auth::attempt($credentials)); always return false, so it can't go to route /home:
and my register controller like this:
public function create()
{
$user = User::create([
'name' => Input::get('name'),
'email' => Input::get('email'),
'password' => Hash::make(Input::get('password')),
]);
return redirect::back();
}
how to deal with this?
i think laravel use json callback and middlware and some handled session and cookie
better use basic auth
and check by step to all work currectly in front to end
and see data send to backend currectly

Login api route always return 200

I've created a Laravel API that is consumed by a Vue front-end. It works fine locally, I have now deployed it to a staging server and my /login route does not work - it returns a 200 status with no response text regardless of whether the credentials are correct or not, the response text should contain the access_token or an error in JSON.
My process was to deploy by git, use composer to set up the dependencies, setup mySQL, configure the .env file, migrate the database, run php artisan passport:install, configure the .env file again with the client secret and id, create a user with tinker, using Postman I hit the endpoint /oauth/tokenwith the necessary data and I successfully get the Bearer token, I can hit /user with the Bearer token and get the corresponding user. Trying log in either through Postman or Vue I get them problem stated previously.
Here is the network tab of the request.
AuthController.php
public function login(Request $request)
{
$http = new \GuzzleHttp\Client;
try {
$response = $http->post(config('services.passport.login_endpoint'), [
'form_params' => [
'grant_type' => 'password',
'client_id' => config('services.passport.client_id'),
'client_secret' => config('services.passport.client_secret'),
'username' => $request->username,
'password' => $request->password,
]
]);
return json_decode((string) $response->getBody(), true);
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
if ($e->getCode() === 400) {
return response()->json('Invalid Request. Please enter a username or a password.', $e->getCode());
} else if ($e->getCode() === 401) {
return response()->json('Your credentials are incorrect. Please try again', $e->getCode());
}
return response()->json('Something went wrong on the server.', $e->getCode());
}
}
Debug is set to true, the I've checked in the log and the only error is this:
[2018-07-19 13:12:01] production.ERROR: The resource owner or authorization server denied the request. {"exception":"[object] (League\\OAuth2\\Server\\Exception\\OAuthServerException(code: 9): T$
[stacktrace]
However after searching Google I don't know if this is relevant. How can I better debug this? Does anyone know of a solution, any pointers wouldbe greatly appreciated.

Categories