I am new to both laravel and lumen.
I was creating a login api with oauth2.0 in lumen 5.6, i have installed passport and generated token.
Below is my login controller function and it is working fine. It returns token.
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Route;
//use Illuminate\Support\Facades\DB;
use App\User;
use Auth;
public function login(Request $request)
{
global $app;
$proxy = Request::create(
'/oauth/token',
'post',
[
'grant_type' => env('API_GRAND_TYPE'),
'client_id' => env('API_CLIENT_ID'),
'client_secret' => env('API_CLIENT_SECRET'),
'username' => $request->username,
'password' => $request->password,
]
);
return $app->dispatch($proxy);
}
Since i have to check user status apart from username and password, i need to check the user credential first. so i do like this.
public function login(Request $request)
{
$credentials = $request->only('username', 'password');
if (Auth::attempt($credentials)) {
return ['result' => 'ok'];
}
return ['result' => 'not ok'];
}
Here i am getting this error.
Method Illuminate\Auth\RequestGuard::attempt does not exist.
So i tried Auth::check instead of Auth::attempt.
Now there is no error but it always return false even though the credentials are valid.
I searched a lot for a solution but i didn't get.
The function guard is only available for routes with web middleware
public function login() {
if(Auth::guard('web')->attempt(['email' => $email, 'password' => $password], false, false)) {requests
// good
} else {
// invalid credentials, act accordingly
}
}
Changing default guard to "web" fixed my problem.
config/auth.php:
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
using jwt it's worked for me
1-Auth::attempt($credentials)
just replace line 1 to line 2
2-$token = Auth::guard('api')->attempt($credentials)
Related
My API route in routes/api.php file is like below.
Route::post('/register', [RegisterController::class, 'register']);
My register function of RegisterController is like below
public function register(RegistrationValidate $request)
{
$user = User::create([
'username' => request('username'),
'email' => request('email'),
'full_name' => request('full_name'),
'store_name' => request('store_name'),
'store_logo' => $fileName,
'password' => Hash::make(request('password')),
]);
$token = $user->createToken($this->clientToken())->accessToken;
return response()->json([
'user' => $user,
'token' => $token,
], 201);
}
I am getting SERVICE UNAVAILABLE error like below
I used php artisan up command and clear the cache. But the result is as like before.
Using the authentication plugin, I'm unable to verify my credentials via json. I can use Form, Jwt and Basic(For testing it works). The error returned is 'FAILURE_CREDENTIALS_INVALID'. Here's the sample code "simplified for brevity"
public function token()
{
$result = $this->Authentication->getResult();
if ($result->isValid()) {
//new jwt token etc
$message = true;
$this->log($result->getStatus());
}else{
$message = false;
$this->log($result->getStatus());
}
$this->set([
'message' => $message,
]);
$this->viewBuilder()->setOption('serialize', ['message']);
}
My application class has the method
public function getAuthenticationService(ServerRequestInterface $request) : AuthenticationServiceInterface
{
$service = new AuthenticationService();
$service->setConfig([
'unauthenticatedRedirect' => '/users/login',
'queryParam' => 'redirect',
]);
$fields = [
'username' => 'email',
'password' => 'password'
];
// Load the authenticators, you want session first
$service->loadAuthenticator('Authentication.Session');
$service->loadAuthenticator('Authentication.Form', [
'fields' => $fields,
'loginUrl' => '/users/login'
]);
//Testing jwt
$service->loadAuthenticator('Authentication.Jwt', [
'secretKey' => Security::getSalt()]);
// Load identifiers
$service->loadIdentifier('Authentication.Password', compact('fields'));
$service->loadIdentifier('Authentication.JwtSubject');
return $service;
}
The app is serving some json well if I pass in the jwt, but somehow I can't figure out how to request a new one, when the old expires.
Here's my middlewareQueue:
//some code
$middlewareQueue
->add(new ErrorHandlerMiddleware(Configure::read('Error')))
->add(new AssetMiddleware([
'cacheTime' => Configure::read('Asset.cacheTime'),
]))
->add(new RoutingMiddleware($this))
->add($csrf)
->add(new BodyParserMiddleware())
->add(new AuthenticationMiddleware($this));
//more code
I DO NOT use basic
As mentioned in the comments, if you want to authenticate using the form authenticator on your token retrieval endpoint, then you need to make sure that you include the URL path of that endpoint in the authenticators loginUrl option.
That option accepts an array, so it should be as simple as this:
$service->loadAuthenticator('Authentication.Form', [
'fields' => $fields,
'loginUrl' => [
'/users/login',
'/api/users/token.json',
],
]);
The error that you were receiving was because the form authenticator simply wasn't applied on the token endpoint, and therefore the authentication service would go to the next authenticator, the JWT authenticator, which isn't bound to a specific endpoint, and therefore can run for all endpoints, and the JWT authenticator of course expects a different payload, it looks for a token, and if it can't find it, it will return the error status FAILURE_CREDENTIALS_INVALID.
I have already written an API call for check authentication using Laravel. I need to move that controller to Lumen for use it as micro service.
This is my controller in Laravel.
public function byCredantial(Request $request)
{
$user = [
'email' => $request->input('email'),
'password' => $request->input('password')
];
if (Auth::attempt($user)) {
$response = $this->getSuccess(Auth::user()->id);
return response()->json($response, 200);
} else {
$response = $this->getError($user);
return response()->json($response, 401);
}
}
Lumen doc is not provide how to do such authentication. They has not function for check creadential is correct. How can i do this in Lumen. Is this possible?
You can do this in Lumen. Facades are disabled by default (if you want to enable it you can see the instructions in the documentation), but I would not recommend enabling the facades as the add additional overhead to your application. Instead, I would modify your function to call app('auth'). This will return the class that the Auth facade proxies without loading all the other facades.
public function byCredantial(Request $request)
{
$user = [
'email' => $request->input('email'),
'password' => $request->input('password')
];
$auth = app('auth');
if ($auth->attempt($user)) {
$response = $this->getSuccess($auth->user()->id);
return response()->json($response, 200);
} else {
$response = $this->getError($user);
return response()->json($response, 401);
}
}
Also, I would recommend reading the authentication documentation and placing the bulk of this code in the AuthServiceProvider.
In Laravel 5.3 API routes were moved into the api.php file. But how can I call a route in api.php file? I tried to create a route like this:
Route::get('/test',function(){
return "ok";
});
I tried the following URLs but both returned the NotFoundHttpException exception:
http://localhost:8080/test/public/test
http://localhost:8080/test/public/api/test
How can I call this API route?
You call it by
http://localhost:8080/api/test
^^^
If you look in app/Providers/RouteServiceProvider.php you'd see that by default it sets the api prefix for API routes, which you can change of course if you want to.
protected function mapApiRoutes()
{
Route::group([
'middleware' => 'api',
'namespace' => $this->namespace,
'prefix' => 'api',
], function ($router) {
require base_path('routes/api.php');
});
}
routes/api.php
Route::get('/test', function () {
return response('Test API', 200)
->header('Content-Type', 'application/json');
});
Mapping is defined in service provider App\Providers\RouteServiceProvider
protected function mapApiRoutes(){
Route::group([
'middleware' => ['api', 'auth:api'],
'namespace' => $this->namespace,
'prefix' => 'api',
], function ($router) {
require base_path('routes/api.php');
});
}
My two routes 'admin.media.begin-destroy' and 'admin.media.destroy' fails when middleware => 'auth'.
When trying to access the route 'admin.media.begin-destroy' I will get presented with the login page from the authentication controller, and I cannot get away from that without trying to access another page.
on my development machine it works fine both with and without auth middleware
Here are my routes that are protected:
Route::group(['prefix' => 'admin', 'middleware' => 'auth'], function() {
Route::get('/', ['as' => 'admin.dashboard', 'uses' => 'Admin\DashboardController#index']);
Route::get('show/{show}/toggle',['as' => 'admin.show.toggle','uses'=>'Admin\ShowController#toggle']);
Route::resource('show', 'Admin\ShowController');
// The admin.media.begin-destroy and admin.media.destroy fails when middleware => 'auth'
Route::get('media/begin-destroy-ddd/{id}', ['as' => 'admin.media.begin-destroy', 'uses' => 'Admin\MediaController#beginDestroy']);
Route::resource('media', 'Admin\MediaController', ['only' => ['index', 'create', 'store', 'destroy']]);
Route::get('order', ['as'=> 'admin.order.index', 'uses' => 'Admin\OrderController#index']);
Route::get('order/list-for-modification', ['as' => 'admin.order.list-for-modification', 'uses' => 'Admin\OrderController#listModify']);
Route::get('order/{order}/begin-destroy', ['as' => 'admin.order.begin-destroy', 'uses' => 'Admin\OrderController#beginDestroy']);
Route::delete('order/{order}/destroy', ['as' => 'admin.order.destroy', 'uses' => 'Admin\OrderController#destroy']);
});
The controller method for 'admin.media.begin-destroy' and 'admin.media.destroy' are super simple:
public function beginDestroy($filename)
{
return view('admin.media.begin-destroy', compact('filename'));
}
/**
* Remove the specified resource from storage.
*
* #param int $filename
* #return Response
*/
public function destroy($filename)
{
Storage::disk('free-media')->delete($filename);
return redirect()->route('admin.media.index');
}
I am very puzzled why this doesn't work.
Edit!---
Trying to work me around the problem I
/ Moved the problematic routes into its own group with no middleware
Route::group(['prefix' => 'admin'], function() {
Route::get('media/begin-destroy/{id}', ['as' => 'admin.media.begin-destroy', 'uses' => 'Admin\MediaController#beginDestroy']);
Route::resource('media', 'Admin\MediaController', ['only' => ['index', 'create', 'store', 'destroy']]);
});
/ Added the middleware registration in the Admin\Mediacontroller Constructor, I cannot add beginDestroy, because it will fail for unknown reason
public function __construct()
{
$this->middleware('auth', ['except' => ['beginDestroy']]);
}
// In beginDestroy, I then check for the user, and the User is not logged in WTF
public function beginDestroy($filename)
{
if (Auth::check())
{
return view('admin.media.begin-destroy', compact('filename'));
}
// I end up here.
return "Un-authorized";
}
for this particular use case, it is OK for me to disable the check in beginDestroy, because in the actual destroy code, the auth works again, but what is happening ?
Changing the Route to have the filename passed using good old question mark made the difference!
This is really more of a workaround than an answer, and I still have no idea why it doesn't work the other way, anyway, here is my workaround that did the trick.
// This works
Route::get('media/begin-destroy',
['as' => 'admin.media.begin-destroy',
'uses' => 'Admin\MediaController#beginDestroy']);
// This does not work!
// For reason beyond me, the 'auth' middleware fails here
// and Auth::check() also fails
// (Yes ofcourse I updated the signature accordingly in beginDestroy)
Route::get('media/begin-destroy/{filename}',
['as' => 'admin.media.begin-destroy',
'uses' => 'Admin\MediaController#beginDestroy']);