I'm newbie for Laravel passport. What I want to achieve is able to verify whether there is access token pass in by user.
I want to call #index route when there are access token found,
call #indexTest route when no access token is found.
Do I have to create custom middleware for this case?
Thanks.
In api.php
/**
* Category
*/
Route::resource('categoriesbranches', 'Category\CategoryBranchController#index', ['only' => ['index']])->middleware('client.credentials');
Route::resource('categoriesbranches', 'Category\CategoryBranchController#indexTest');
In CategoryBranchController.php
// public function __construct()
// {
// $this->middleware('client.credentials')->only(['index']);
// }
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
//different action
}
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function indexTest()
{
//different action
}
Yes, you need to create a middleware and check for access token and then redirect the call where you want, if it has the access token you just $next($request)
You can crate a middleware and add a before middleware method to check if the request has token even before any routing.
https://lumen.laravel.com/docs/5.6/middleware
Related
When a user signs up they land on the register page with some GET parameters. After they register I'd like to redirect them to the next page and keep these GET parameters.
In Auth\RegisterController I think I can use the method redirectTo as defined in the Illuminate\Foundation\Auth\RedirectsUsers trait.
But how can I achieve what I like using this method? Do I call it at the end of the create() method in the RegisterController like:
create()
{
// ...
$this-> redirectTo($request->get('param1'), $request->get('param2'))
}
and then define
protected function redirectTo($param1, $param2)
{
return redirect()->route('example?param1='.$param1.'¶m2='.$param2);
}
I'm not sure where redirectTo() should go and if this is the right way to achieve this.
I recommend that you use the routes and then play on the status of the application with the session.
Or you can modify the value of $this->redirectTo in the authenticated which gives you in parameter an instance of Request and the registered user.
/**
* Register listener
*
* #param Reauest $request
* #param User $user
* #return mixed
*/
protected function registered(Request $request, $user)
{
$param1 = $request->get('param1');
$param2 = $request->get('param2');
$user->sendWelcomeNotification();
$this->redirectTo = 'example?param1='.$param1.'¶m2='.$param2;
}
When issuing access and refresh tokens I send them to the frontend with httpOnly = true. I made a middleware class which checks if the access token has expired and if the refresh token hasn't expired. If both are the case I call the oauth/token route with refresh_token as grant_type.
All of this works, however I'm not sure how to use the authenticated user inside the controllers. Right now I've set the middleware up that when there's a response it sends the new access and refresh token.
class RefreshAccessToken
{
/* #var AuthController $authController */
private $authController;
/**
* RefreshAccessToken constructor.
*
* #param AuthController $authController
*/
public function __construct(AuthController $authController)
{
$this->authController = $authController;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
// User already has an access token
if ($request->hasCookie('access_token')) {
return $next($request);
}
// User doesn't have an access token and doesn't have a refresh token.
if (!$request->hasCookie('refresh_token')) {
return $next($request);
}
$response = $next($request);
$cookies = $this->authController->refreshToken($request);
return $response
->cookie($cookies['access_token'])
->cookie($cookies['refresh_token']);
}
}
Now inside a controller if I want to access the logged in user I always get null as a response:
public function logout()
{
dd(auth()->user());
}
}
Inside the controller's construct method:
$this->middleware('auth:api')->only('logout');
Does anyone know how to handle this use case?
I would like a group of routes to be accessible with either the standard auth:api middleware, or via the CheckClientCredentials middleware.
I can't see how this would be possible as there is no ability to set middleware as only requiring one of the listed middleware.
Is there a Passport middleware which allows any type of API authentication that I don't know about?
Or is there a clean way of creating a custom middleware which tests for either of the middleware?
I took Joshuas advice about the similar answer here and created a new middleware encompassing the two auth middleware.
Working Middleware class for anyone else hitting this issue below. This will try the Auth guard middleware first and if this fails to authenticate the request it will then try to authenticate using the Client Credentials Passport middleware.
namespace App\Http\Middleware;
use Closure;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Auth\Middleware\Authenticate as AuthGuardMiddleware;
use Laravel\Passport\Http\Middleware\CheckClientCredentials as ClientCredMiddleware;
class AuthenticateWithApiOrClientCreds
{
/**
* Authenticate a request with either Authenticate OR CheckClientCredentials Middleware
*
* #param $request
* #param Closure $next
* #param mixed ...$scopes
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function handle($request, Closure $next, ...$scopes)
{
$auth_guard_middleware = app()->make(AuthGuardMiddleware::class);
try {
$response = $auth_guard_middleware->handle($request, $next, 'api');
} catch (AuthenticationException $e) {
$client_cred_middleware = app()->make(ClientCredMiddleware::class);
$response = $client_cred_middleware->handle($request, $next, ...$scopes);
}
return $response;
}
}
Laravel doesn't provide the OR middleware. Although there are several workarounds like it was previously asked here.
If you are looking for the way to change the api.php default middleware (default to auth:api), you can see it in directory: app\Providers\RouteServiceProvider.php with function name: mapApiRoutes().
I ended up storing all the routes that need authentication in a separate route file called api-authenticated.php. In RouteServiceProvider.php I'm applying the web and api middleware groups for those routes conditionally based on the presence of the Authorization header. When the Authorization header is set, you know the user is an API client.
In Providers/RouteServiceProvider.php:
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*
* #return void
*/
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->apiNamespace)
->name('api.')
->group(__DIR__ . '/../../routes/api.php');
}
/**
* Define the authenticated "api" routes for the application.
*
* These routes are typically stateless, but are also used in
* a stateful context as the first-party uses cookies for
* authenticaton
*
* #return void
*/
public function mapAuthenticatedApiRoutes()
{
$stateless = request()->hasHeader('Authorization');
Route::prefix('api')
->middleware($stateless ? ['api', 'auth:api'] : ['web', 'auth'])
->namespace($this->apiNamespace)
->name('api.')
->group(__DIR__ . '/../../routes/api-authenticated.php');
}
I've set up a laravel app with client authentification. I send it my client id and client secret and it gives me a token. I'm able to log in to my laravel app, but I can't figure out how to get the id of the client that's been authorized.
I've seen hints to use auth()->user()->Token()->getAttribute('client_id') to get the client id, but since I'm only using clients there is no user and I get an error about trying to call Token() on a null object. Auth::id() also returned nothing. I grabbed the token from the header with Request::header('Authorization'), but it didn't match anything in the database.
I'm assuming you're using client credentials grant tokens, and the CheckClientCredentials middleware.
You can get this information from the bearer token, but it's not that straightforward. You would need to create a new PSR7 request with the token, and send it off to the oauth server to have it converted to readable data.
This is already done inside the CheckClientCredentials middleware provided by Passport. So, one way to do this would be to extend the CheckClientCredentials middleware and just manually set the needed fields on the request object from inside the middleware.
First, create app/Http/Middleware/MyCheckClientCredentials.php:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Auth\AuthenticationException;
use League\OAuth2\Server\Exception\OAuthServerException;
use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
use Laravel\Passport\Http\Middleware\CheckClientCredentials;
class MyCheckClientCredentials extends CheckClientCredentials
{
/**
* The Resource Server instance.
*
* #var \League\OAuth2\Server\ResourceServer
*/
private $server;
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param mixed ...$scopes
* #return mixed
* #throws \Illuminate\Auth\AuthenticationException
*/
public function handle($request, Closure $next, ...$scopes)
{
$psr = (new DiactorosFactory)->createRequest($request);
try {
$psr = $this->server->validateAuthenticatedRequest($psr);
// This is the custom line. Set an "oauth_client_id" field on the
// request with the client id determined by the bearer token.
$request['oauth_client_id'] = $psr->getAttribute('oauth_client_id');
} catch (OAuthServerException $e) {
throw new AuthenticationException;
}
$this->validateScopes($psr, $scopes);
return $next($request);
}
}
Next, update your app/Http/Kernel.php to use your custom middleware instead of the build in Passport middleware:
protected $routeMiddleware = [
'client' => \App\Http\Middleware\MyCheckClientCredentials::class,
];
Apply the middleware to your route as normal:
Route::get('/user', function(Request $request) {
// Should show "oauth_client_id" field.
dd($request->all());
})->middleware('client');
If you don't want to do this inside a middleware, you can study how the Passport middleware works and reuse this code in some type of service if you'd like.
NB: all untested.
I had to do something similar in a logger middleware of mine:
.......................
$user = $request->user();
if($user) {
// assume the authorization header exists, since the user is authenticated
$header = $request->headers->get('authorization');
if($header) { // authorization header is not set when testing via Passport::actingAs()
/**
* Stolen from League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator#63
*/
// Get the actual jwt string from the header
$jwt = trim(preg_replace('/^(?:\s+)?Bearer\s/', '', $header));
// Parse the token from the string
$token = (new Lcobucci\JWT\Parser())->parse($jwt);
// Get the ID from the token
$oauthClientId = $token->getClaim('aud');
}
}
.......................
I have a Laravel API (actually a Lumen API) serving a VueJS front-end. The Vue app allows users to login to Google. The Google token is then sent back to the Lumen API, which verifies the token with Google, then verifies that the email address is a valid user. Then it generates a token, stores that in the DB with the user and returns the user object.
I'm not using Passport or jwt-auth or anything like that. So now, how do I go about using the default Auth middleware to verify the token header that the (now logged in) user will be returning with every request? (i.e. is the token in the DB and is it expired?). And is there a way to do this more efficiently, so Laravel caches the valid tokens and doesn't have to hit the DB for every request?
You can use the Authentication Middleware. Pass with your request an api_token. A more detailed answer can be found here.
Add a api_token column to your User table.
$table->string('api_token', 60)->unique();
Have an authentication middleware as such:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class Authenticate
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('auth/login');
}
}
return $next($request);
}
}
And use Route Group
Route::group(['middleware' => 'auth:api'], function () {
Route::resource('api/v1/foo/bar', 'API\FooBarController');
}