I'm writing a tiny sms gateway to be consumed by a couple of projects,
I implemented laravel passport authentication (client credentials grant token)
Then I've added CheckClientCredentials
to api middleware group:
protected $middlewareGroups = [
'web' => [
...
],
'api' => [
'throttle:60,1',
'bindings',
\Laravel\Passport\Http\Middleware\CheckClientCredentials::class
],
];
The logic is working fine, now in my controller I need to get client associated with a valid token.
routes.php
Route::post('/sms', function(Request $request) {
// save the sms along with the client id and send it
$client_id = ''; // get the client id somehow
sendSms($request->text, $request->to, $client_id);
});
For obvious security reasons I can never send the client id with the consumer request e.g. $client_id = $request->client_id;
.
There is a tricky method.
You can modify the method of handle in the middleware CheckClientCredentials, just add this line.
$request["oauth_client_id"] = $psr->getAttribute('oauth_client_id');
Then you can get client_id in controller's function:
public function info(\Illuminate\Http\Request $request)
{
var_dump($request->oauth_client_id);
}
So, no answers ...
I was able to resolve the issue by consuming my own API, finally I came up with simpler authentication flow, the client need to send their id & secret with each request, then I consumed my own /oauth/token
route with the sent credentials, inspired by Esben Petersen blog post.
Once the access token is generated, I append it to the headers of Symfony\Request
instance which is under processing.
My final output like this:
<?php
namespace App\Http\Middleware;
use Request;
use Closure;
class AddAccessTokenHeader
{
/**
* Octipus\ApiConsumer
* @var ApiConsumer
*/
private $apiConsumer;
function __construct() {
$this->apiConsumer = app()->make('apiconsumer');
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$response = $this->apiConsumer->post('/oauth/token', $request->input(), [
'content-type' => 'application/json'
]);
if (!$response->isSuccessful()) {
return response($response->getContent(), 401)
->header('content-type', 'application/json');
}
$response = json_decode($response->getContent(), true);
$request->headers->add([
'Authorization' => 'Bearer ' . $response['access_token'],
'X-Requested-With' => 'XMLHttpRequest'
]);
return $next($request);
}
}
I used the above middleware in conjunction with Passport's CheckClientCredentials
.
protected $middlewareGroups = [
'web' => [
...
],
'api' => [
'throttle:60,1',
'bindings',
\App\Http\Middleware\AddAccessTokenHeader::class,
\Laravel\Passport\Http\Middleware\CheckClientCredentials::class
],
];
This way, I was able to insure that $request->input('client_id')
is reliable and can't be faked.
The OAuth token and client information are stored as a protected variable in the Laravel\Passport\HasApiTokens trait (which you add to your User model).
So simply add a getter method to your User model to expose the OAuth information:
public function get_oauth_client(){
return $this->accessToken->client;
}
This will return an Eloquent model for the oauth_clients table
I dug into CheckClientCredentials class and extracted what I needed to get the client_id
from the token. aud
claim is where the client_id
is stored.
<?php
Route::middleware('client')->group(function() {
Route::get('/client-id', function (Request $request) {
$jwt = trim(preg_replace('/^(?:\s+)?Bearer\s/', '', $request->header('authorization')));
$token = (new \Lcobucci\JWT\Parser())->parse($jwt);
return ['client_id' => $token->getClaim('aud')];
});
});
Few places to refactor this to in order to easily access but that will be up to your application
public function handle($request, Closure $next, $scope)
{
if (!empty($scope)) {
$psr = (new DiactorosFactory)->createRequest($request);
$psr = $this->server->validateAuthenticatedRequest($psr);
$clientId = $psr->getAttribute('oauth_client_id');
$request['oauth_client_id'] = intval($clientId);
}
return $next($request);
}
put above to your middleware file, then you can access client_id by request()->oauth_client_id
I use this, to access the authenticated client app...
$bearerToken = $request->bearerToken();
$tokenId = (new \Lcobucci\JWT\Parser())->parse($bearerToken)->getHeader('jti');
$client = \Laravel\Passport\Token::find($tokenId)->client;
$client_id = $client->id;
$client_secret = $client->secret;
Source