Reputation: 800
I'm setting up an authentication route with my API. I am using laravel 5.5 with tymondesigns/jwt-auth 1.0.0-rc.1 and Postman to interact with the API.
The authentication route/method seems to work:
/**
* Authenticates a json request, generating a token.
*
* @param Request $request
* @return JsonResponse
*/
public function authenticate(Request $request)
{
// grab credentials from the request
$credentials = $request->only('email', 'password');
try {
// attempt to verify the credentials and create a token for the user
if (! $token = JWTAuth::attempt($credentials)) {
return response()->json(
[
'error' => 'Invalid credentials.',
'detail' => 'Please use your email and password to generate a token.'
],
401);
}
} catch (JWTException $e) {
// something went wrong whilst attempting to encode the token
return response()->json(
[
'error' => 'Could not create token',
'detail' => 'There was an internal problem and your token could not be created.'
], 500
);
}
// all good so return the token
return response()->json(compact('token'));
}
A Postman API post request returns (what seems to be) a valid response, For example:
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vc29sZGVyc3RhcmFwaS5jb20ubG9jYWwvYXBpL2F1dGhlbnRpY2F0ZSIsImlhdCI6MTUwNzg4NjU2OSwiZXhwIjoxNTA3ODkwMTY5LCJuYmYiOjE1MDc4ODY1NjksImp0aSI6IkpFWjBkc0dNbEVydXRHcFciLCJzdWIiOiIwNzk2MjhDMC03QjBDLTExRTYtODRERC1DQjAzMzVGN0JBNUQiLCJwcnYiOiI4N2UwYWYxZWY5ZmQxNTgxMmZkZWM5NzE1M2ExNGUwYjA0NzU0NmFhIn0.Dl2EEaYZx3H5XXG9WUcPXYKuma0ZjCvcCsb99hgB6O4"
}
To begin with, for basic testing purposes, I am feeding this to an action using GET, with the following suffix:
?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vc29sZGVyc3RhcmFwaS5jb20ubG9jYWwvYXBpL2F1dGhlbnRpY2F0ZSIsImlhdCI6MTUwNzg4NjU2OSwiZXhwIjoxNTA3ODkwMTY5LCJuYmYiOjE1MDc4ODY1NjksImp0aSI6IkpFWjBkc0dNbEVydXRHcFciLCJzdWIiOiIwNzk2MjhDMC03QjBDLTExRTYtODRERC1DQjAzMzVGN0JBNUQiLCJwcnYiOiI4N2UwYWYxZWY5ZmQxNTgxMmZkZWM5NzE1M2ExNGUwYjA0NzU0NmFhIn0.Dl2EEaYZx3H5XXG9WUcPXYKuma0ZjCvcCsb99hgB6O4
In order to test this, if I do the following:
public function globalObjects(Request $request): JsonResponse {
var_dump(JWTAuth::parseToken()->authenticate(), JWTAuth::getToken()); exit;
// ... later code that never gets reached
}
I get the following:
bool(false) object(Tymon\JWTAuth\Token)#809 (1) { ["value":"Tymon\JWTAuth\Token":private]=> string(384) "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vc29sZGVyc3RhcmFwaS5jb20ubG9jYWwvYXBpL2F1dGhlbnRpY2F0ZSIsImlhdCI6MTUwNzg4NjU2OSwiZXhwIjoxNTA3ODkwMTY5LCJuYmYiOjE1MDc4ODY1NjksImp0aSI6IkpFWjBkc0dNbEVydXRHcFciLCJzdWIiOiIwNzk2MjhDMC03QjBDLTExRTYtODRERC1DQjAzMzVGN0JBNUQiLCJwcnYiOiI4N2UwYWYxZWY5ZmQxNTgxMmZkZWM5NzE1M2ExNGUwYjA0NzU0NmFhIn0.Dl2EEaYZx3H5XXG9WUcPXYKuma0ZjCvcCsb99hgB6O4" }
.. as in:
Items of note:
id
, but it is a UUID, so a binary(16)... and thus:Following request: here's \config\jwt.php
return [
'secret' => env('JWT_SECRET', 'AqAWUTYISA56lrl2vcRtZQn4M4zk9onl'),
'ttl' => 60,
'refresh_ttl' => 20160,
'algo' => 'HS256',
'user' => 'App\User',
'identifier' => 'email',
'required_claims' => ['iss', 'iat', 'exp', 'nbf', 'sub', 'jti'],
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
'providers' => [
'user' => 'Tymon\JWTAuth\Providers\User\EloquentUserAdapter',
'jwt' => 'Tymon\JWTAuth\Providers\JWT\Namshi',
'auth' => 'Tymon\JWTAuth\Providers\Auth\Illuminate',
'storage' => 'Tymon\JWTAuth\Providers\Storage\Illuminate',
],
];
Thanks
Upvotes: 0
Views: 7514
Reputation: 21
Here is the solution I use in my API works perfectly well.
First rewrite the class Tymon\JWTAuth\Providers\Auth\Illuminate::class
<?php
namespace Scryba\Code\Laravel\Providers\Auth\Jwt;
use Tymon\JWTAuth\Contracts\Providers\Auth;
use Illuminate\Contracts\Auth\Guard as GuardContract;
class Illuminate implements Auth
{
/**
* The authentication guard.
*
* @var \Illuminate\Contracts\Auth\Guard
*/
protected $auth;
/**
* Constructor.
*
* @param \Illuminate\Contracts\Auth\Guard $auth
*
* @return void
*/
public function __construct(GuardContract $auth)
{
$this->auth = $auth;
}
/**
* Check a user's credentials.
*
* @param array $credentials
*
* @return bool
*/
public function byCredentials(array $credentials)
{
return $this->auth->once($credentials);
}
/**
* Authenticate a user via the id.
*
* @param mixed $id
*
* @return bool
*/
public function byId($id)
{
//you can see i added hex2bin($id)because i save my UUID primary key as
//binary(16)
return $this->auth->onceUsingId(hex2bin($id));
}
/**
* Get the currently authenticated user.
*
* @return mixed
*/
public function user()
{
return $this->auth->user();
}
}
Then update \config\jwt.php
file to
'providers' => [
/*
|--------------------------------------------------------------------------
| JWT Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to create and decode the tokens.
|
*/
'jwt' => Tymon\JWTAuth\Providers\JWT\Namshi::class,
/*
|--------------------------------------------------------------------------
| Authentication Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to authenticate users.
|
*/
//'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,
'auth' => Scryba\Code\Laravel\Providers\Auth\Jwt\Illuminate::class,
/*
|--------------------------------------------------------------------------
| Storage Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to store tokens in the blacklist.
|
*/
'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,
],
in my routes file
////// Protected methods (requires Authenticatication)
$api->group(
[
'middleware' => ['api.auth'],
'providers' => ['jwt'],
],function($api){
$api->resource('cars', 'CarController', ['only' => [
'index' ,'show'
]]);
});
So in your case leave the source file \vendor\tymon\jwt-auth\src\JWTAuth.php as is and write your code as below in your custom class if applicable.
/**
* Authenticate a user via the id.
*
* @param mixed $id
*
* @return bool
*/
public function byId($id)
{
$id_text = $this->getPayload()->get('sub');
$uuid_helper = new UuidHelper();
$id = $uuid_helper->textIdToId($id_text);
return $this->auth->onceUsingId($id);
}
Upvotes: 2
Reputation: 800
The answer is : "this is not compatible with this feature in it's current state without extending it yourself".
I hope this helps anyone else using UUIDs as a primary key. You can't do it without editing the vendor items or extending... But, this is an easy fix.
I will be raising this with the package author and hopefully finding a more permanent solution. However, here is a workaround:
Background:
id
columns in the database. id_text
fields exist as auto generated mysql fieldsProblem in JWT:
In file \vendor\tymon\jwt-auth\src\JWTAuth.php
/**
* Authenticate a user via a token.
*
* @return \Tymon\JWTAuth\Contracts\JWTSubject|false
*/
public function authenticate()
{
$id_text = $this->getPayload()->get('sub');
$uuid_helper = new UuidHelper();
$id = $uuid_helper->textIdToId($id_text);
if (! $this->auth->byId($id)) {
return false;
}
return $this->user();
}
The documentation isn't the clearest, so I assumed 'identifier' => 'email'
would sidestep this problem... turns out it doesn't. I will feed this back to the authors
Modifying the core was just an exercise... I am pretty sure this class can be extended, and I will be attempting this shortly. Editing sources is of course sub-optimal most cases.
However - I hope this digging will help people understand the problem.
Upvotes: 0
Reputation: 394
You token is received but not parsed correctly you need to check for token parsing success.
if (! $auth = JWTAuth::parseToken();) {
throw Exception('JWTAuth unable to parse token from request');
}
dd(\Auth::id());
i suggest you go to your kernal.php and add following lines in protected $routeMiddleware array
'jwt.auth' => 'Tymon\JWTAuth\Middleware\GetUserFromToken',
'jwt.refresh' => 'Tymon\JWTAuth\Middleware\RefreshToken',
Then go to your routes and use it like middleware to authenticate like this
Route::group(['middleware' => 'jwt.auth'], function () {
// Your routes here that you want to protect
Route::get('foo', function () {
return 'Hello World';
});
]):
Upvotes: 0