So I have been using Passport to handle any OAuth requests which works perfectly. However there are some scenarios where I do not need a user instance to request and API endpoint. Static lists etc. I have created a middleware for that and it works fine. And finally there are scenarios when pulling things like lists where i want to give developers the freedom to either use the Token or OAuth to post to those endpoints. And struggling on how to do that...
Anyone have any insights? I am thinking I could always call the Token middleware and then from the token middleware call the normal passport OAuth? Not really sure how i would go about doing that though.
Chaining would not work in this scenario because if one fails it would boot them out, where I want it to check for a token IF it does not exist THEN check OAuth token and follow normal behavior after that.
Route::group(['middleware' => 'token:auth:api',
Not seeing anywhere in the docs on how to do this. But maybe I am missing something.
Cheers
Citti
Below handle of middleware to check for normal token and otherwise send request to another middleware for authentication.
In api endpoint allows authorization token it can be either predefined token or an oauth token.
public function handle($request, Closure $next)
{
if ($request->is('api/*')) {
if($request->hasHeader('authorization')){
$bearertoken = $request->bearerToken();
$token = $request->header('authorization');
if(!empty($bearertoken)){
return app(Authenticate::class)->handle($request, function ($request) use ($next) {
return $next($request);
},'api');
}
if(!empty($token)){
$client = Client::where('secret',$token)->first();
if(!empty($client)){
return $next($request);
}
}
}
throw new APIException("Unauthorized Access.");
}
return $next($request);
}
You can achieve this by making another middleware. In that middleware first you call the token middleware, if that fails then call the passport OAuth middleware. I have done to authenticate token using Tymon JWT, if that fails will authenticate using Laravel Passport OAuth. Following is the handle() function of the middleware
public function handle($request, Closure $next)
{
try {
return app(\Tymon\JWTAuth\Http\Middleware\Authenticate::class)->handle($request, function ($request) use ($next) { //JWT middleware
return $next($request);
});
} catch (\Exception $exception) {
if ($exception instanceof UnauthorizedHttpException) {
return app(\Laravel\Passport\Http\Middleware\CheckClientCredentials::class)->handle($request, function ($request) use ($next) {
return $next($request);
});
}
throw $exception;
}
}
In the Handle method of your Middlvar, add the following code:
$classes = (array) MiddlewareNameResolver::resolve(
$guard, app('router')->getMiddleware(), app('router')->getMiddlewareGroups()
);
app(\Illuminate\Pipeline\Pipeline::class)
->send($request)
->through($classes)
->thenReturn();
Related
I am trying to achieve this using middleware
web.php
Route::get('/test', 'TestController#index')->middleware('TestLogin');
redirect to /test if session is found
Route::get('/test1', 'TestController#index1')->middleware('TestLogin');
redirect to test1 if session is set
Middleware -TestLogin
public function handle($request, Closure $next)`
{
if($request->session()->get('Username'))
{
return redirect()->route(\);
// what to write here to redirect to the path its being called from
}
return redirect()->route('login');
}
don't want to use default auth middleware
Your middleware is suppose to check the login (session), if it is not found then redirect to login page otherwise it should pass the request. Something like this:
public function handle($request, Closure $next)
{
// No login session, redirect
if(!$request->session()->get('Username'))
{
return redirect()->route('login');
}
// Pass the request down the rest of pipeline
return $next($request);
}
You should consider to use built in authentication system in Laravel itself, it will save you lots of code and you can be sure the auth is handled correctly in security point of view.
I have created a custom function in middleware which return the data after header token validation. It works fine and I can access the data using postman (Please check the attached screenshot below taken from postman). This is the code in middleware:
public function handle($request, Closure $next)
{
$token = $request->header('APP_KEY');
if ($token != 'XXXX') {
return response()->json(['message' => 'API Key not found!'], 401);
}
return $next($request);
}
The problem I am facing now, I can not get the data from browser URL. I have tried http://127.0.0.1:8000/api/?APP_KEY=XXXX
I am using LumenPassport (https://github.com/dusterio/lumen-passport) and I followed a few tutorials listed here.
I used a combination of these tutorials as well as a heck of google and stackoverflow searches to achieve what I have thus far:
http://esbenp.github.io/2017/03/19/modern-rest-api-laravel-part-4/
http://esbenp.github.io/2015/05/26/lumen-web-api-oauth-2-authentication/
https://blog.pusher.com/make-an-oauth2-server-using-laravel-passport/
What I achieved so far
1. Using password grant to get an access & refresh token
2. Storing these tokens in a secure http only cookie
3. Retrieving these tokens in Lumen's AuthServiceProvider
What I am unable to do
1. Getting the authenticated user with the AccessToken
I am trying to access either of these endpoints:
$router->group(['middleware' => 'auth:api'], function () use ($router) {
$router->get('/', function () use ($router) {return $router->app->version();});
$router->post('/logout', '\App\Auth\LoginController#logout');
});
I will immediately get an unauthorized error.. After some deep diving, the error comes from Authenticate.php which I know is called after AuthServiceProvider. I took a look at AuthServiceProvider and according to Lumen's documentation, this is how the boot method should looks like. Of course it is using the "api" driver and I had to switch it to "passport" for it to work.
AuthServiceProvider.php
public function boot()
{
$this->app['auth']->viaRequest('passport', function ($request) {
// dd("test") // this works
// dd(Auth::user());
// dd($request->user());
// dd(Auth::guard('api')->user());
});
}
Authenticate.php
public function handle($request, Closure $next, $guard = null)
{
if ($this->auth->guard($guard)->guest()) {
$status = Response::HTTP_UNAUTHORIZED;
return response()->json(['success' => false, 'status' => $status, 'message' => 'HTTP_UNAUTHORIZED'], $status);
}
return $next($request);
}
From here, I am still unable to get any of the authenticated user's information. I have made sure to access these endpoints with Postman with the appropriate Authorization headers.
The reason why I need to retrieve the user is because I hope that in my logout method, I will be able to then retrieve the accessToken of that authenticated user and revoke the token and clear the cookies.
LoginController.php
public function logout()
{
// Get the accessToken from Auth
// Need to fix AuthServiceProvider first
$accessToken = $this->auth->user()->token();
$refreshToken = $this->db
->table('oauth_refresh_tokens')
->where('access_token_id', $accessToken->id)
->update([
'revoked' => true,
]);
$accessToken->revoke();
$this->cookie->queue($this->cookie->forget(self::REFRESH_TOKEN));
}
At that point you cannot use Auth::user() since that function is the functionality for resolving that. So what you need to do is extract the bearer token with $request->bearerToken() and use that to retrieve your user.
Update
I took a look at your code and I would recommend the following:
An API is recommended to be 'stateless' meaning that it should not persist any state (i.e. cookies). It is far better to pass the access token with each request and let the application that accesses your API handle the tokens. Therefore I would recommend to remove the log-out functionality. Then you can do the following in your AuthServiceProvider:
if ($token_exists) {
$user = User::find($token->user_id);
return $user;
}
I created a custom middleware for checking if request is submitted by user who owns the resource or owned by admin.
Route::middleware(['web', 'selforadmin'])->group(function () {
Route::post('users/update-account/{id}', 'UsersController#UpdateAccount');
Route::post('users/update-email/{id}', 'UsersController#UpdateEmail');
Route::post('users/update-password/{id}', 'UsersController#UpdatePassword');
});
and then the middleware handler:
public function handle($request, Closure $next)
{
print_r($request->all());
print_r($request->user());
dd();
return $next($request);
}
But I don't know why user model is not accessible here. I read that request needs to pass from web middleware first so I did but still can't access this middleware.
It gives null on $request->user() or Auth::user()
I am using Laravel 5.4
EDIT:
Middleware is being called as I see other inputs. Only Auth is empty. And User is logged.
You can't access using models directly in middleware. You need to define a terminate method in your middleware to perform some processing after response has been sent to browser.
public function terminate($request, $response)
{
// Your code ...
}
If user is not logged in it will always return null.
There are two solution for this.
First is, check if $request->user() is not null.
public function handle($request, Closure $next)
{
if($request->user())
{
// do your stuff
}
else
{
// do otherwise
}
return $next($request);
}
Second is, add auth middleware before your middleware to assure that only logged in users are allowed.
Route::middleware(['web', 'auth', 'selforadmin'])->group(function () {
// .....
}
I'm using JavaScript to receive an access token to the user facebook account.
When I get the token, I send a request to the server with the token to get information about the user using Socialite.
$user = Socialite::driver('facebook')->userFromToken($request->token);
return $user->getName();
Now I'm trying to group API calls that only authenticated user can make, for example /my-profile, my-data, etc...
The middleware purpose is to check if the user is authenticated for a group of API calls instead typing the same condition in all methods:
if ($user->getName()) {
...Execute...
}
Any idea how I can place the following condition for a list of routes for autehnticated users?
My condition is simple:
$user = Socialite::driver('facebook')->userFromToken($request->token);
if ($user->getName()) {
return true;
}
else { return false; }
Okay,
First create the middleware:
php artisan make:middleware name_of_your_middleware
After that, inside the middleware file that has been created:
public function handle($request, Closure $next)
{
$user = Socialite::driver('facebook')->userFromToken($request->token);
if ($user->getName()) {
return $next($request);
}
return redirect('some_other_route_for_error');
}
Then assign the middleware to the routes middelware in app/Http/Kernel.php
protected $routeMiddleware = [
//.......
'name_of_your_middleware' => \Illuminate\Auth\Middleware\Authenticate::class,
];
In your Route file (api.php):
Route::group(['middleware'=>['name_of_your_middleware']],function(){
//your routes
});
Hope it helps :)