Open philliperosario opened 7 years ago
When i set in composer the "tymon/jwt-auth": "1.0.0-rc.1" and go to the "composer update" i have this error:
Problem 1
I have a fresh laravel 5.5.18 version installed, witch jwt-auth version must i choose?
I guess dev-develop
Yep i try dev-develop
too and i have a big issue too
Problem 1
This is the first part of my composer.json file
{
"name": "laravel/laravel",
"description": "The Laravel Framework.",
"keywords": ["framework", "laravel"],
"license": "MIT",
"type": "project",
"require": {
"php": ">=7.0.0",
"fideloper/proxy": "~3.3",
"laravel/framework": "5.5.*",
"laravel/tinker": "~1.0",
"tymon/jwt-auth": "dev-develop"
},
@philliperosario how can i implement the same for a non User class. my issue is the the mobile API should be authenticated against the \App\Customer class (not user class). I'm using Laravel 5.4.
@Rafeethu see in config/auth.php the providers['users'] array.
@philliperosario my app has web module for internal users and api for mobile users (customers). So in config/app.php the guards array has paras for web and api separately (laravel 5.4 has this)
I need web section to use \App\User model and api section to use \App\Customer model. So I set the providers for api accordingly. But when calling through api route, it still use the web guard section (that's the user model).
What am I missing here.
@Rafeethu put here your config/auth.php file
This is my config/auth.php file `
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session", "token"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'customers',
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'customers' => [
'driver' => 'eloquent',
'model' => App\Customer::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expire time is the number of minutes that the reset token should be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
],
],
];
`
@Rafeethu
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'customers',
],
],
and as said in this post:
You can choose which guard you're using to protect your routes by adding a colon and the guard name after auth in the middleware key (e.g. Route::get('whatever', ['middleware' => 'auth:api'])). You can choose which guard you're calling manually in your code by making guard('guardname') the first call of a fluent chain every time you use the Auth façade (e.g. Auth::guard('api')->check()).
Yes, i tried
$token = JWTAuth::guard('api')->attempt($credentials)
in login controller but it throws
in JWT.php (line 399) at JWT->call('guard', array('api'))in Facade.php (line 221) at JWTAuth->guard('api')in Facade.php (line 221) at Facade::callStatic('guard', array('api'))in AuthController.php (line 38) at JWTAuth::guard('api')in AuthController.php (line 38) at AuthController->login(object(Request))
use the guard on your routes
You mean like this
Route::middleware('jwt:api')->get('/user', function (Request $request) { return ['name' => 'test']; });
still no luck :-(
@akkhan20 Because the master version is not compatible with laravel 5.5 at the moment, and the dev one looks like they have the laravel 5.5 compatibility issues solved
@Rafeethu I think the guard must be called like this
Route::get('/user', function (Request $request) ['middleware' => 'auth:api']);
@ralbear you can download 1.0.0-rc.1 and u can certainly find some docs for that and its compatible with L5.5 too
@akkhan20 If you read my first comment, when i try with the 1.0.0-rc.1 version in composer i get an error, composer says that version is not available.
php artisan vendor: publish --provider = "Tymon\JWTAuth\Providers\LaravelServiceProvider" should be php artisan vendor: publish --provider "Tymon\JWTAuth\Providers\LaravelServiceProvider" no =
also: remove spaces vendor:publish - php artisan jwt:secret
If you are using CORS - do not forget to expose response header "Authorization" to give JS allow getting header.
If you are using Laravel CORS, then In config/cors.php
specify expose header:
...
'exposedHeaders' => ['Authorization'],
...
@core01 well remembered, I updated my answer
@philliperosario , thanks for tutorial. however, what about the signing up?
@zhekaus sorry, I did not implement the sign up
@zhekaus try this:
public function register(Request $request)
{
$request->validate(
[
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
'password_confirmation' => 'required|string|min:6',
]
);
$user = new User();
$user->email = $request->email;
$user->password = bcrypt($request->password);
$user->save();
$token = JWTAuth::attempt($request->only('email', 'password'));
return response()->json(['token' => "Bearer $token"]);
}
@core01 , many thanks indeed! However, I've got this:
Type error: Argument 1 passed to Tymon\JWTAuth\JWT::fromUser() must implement interface Tymon\JWTAuth\Contracts\JWTSubject
at the line $token = JWTAuth::attempt($request->only('email', 'password'));
However, I did implemented it as described above.
@zhekaus please check if you are using the right facade inside AuthController it should be use Tymon\JWTAuth\Facades\JWTAuth;
yes, I am
Your User
model must implement Tymon\JWTAuth\Contracts\JWTSubject
interface
@Frondor , As I said before, I did it. There is no problem with a fresh Laravel project. I just can't make it work with the real one.
Finally I’ve solved my problem. Tracing led to wrong config. I had user provider driver set to 'database' before I cached configuration following the tutorial.
I changed it to 'eloquent' according to the docs, but hadn’t run php artisan config:clear
since that.
Caching step is definitely superfluous for this tutorial. :-)
Also you don’t need aliases for JWT’s facades.
I have a question. I have several public pages. Those pages are in (laravel) a different group and I don't use the refresh middleware. That means that if the user doesn't call the routes that does have the refresh middleware, the used token will be the same... The token will change only if the user request for those "middlewared" routes... so.. if the token is expired, will be refreshed. If the user doesn't request for those routes the token could be refreshed until the JWT_REFRESH_TTL pass? For instance...
.env
JWT_TTL=1
JWT_REFRESH_TTL=20160
JWT_BLACKLIST_ENABLED=true
JWT_BLACKLIST_GRACE_PERIOD=180
This is correct? There is a better approach to this? refresh from the frontend every several time? or something like that?
I'm following this setup for the middleware yet i always get blacklisted token.... i don't know why. I have already return the response back with the new token and give an interceptor if there is any new token.
It is always caught in exception when arrived at this line
$this->auth->refresh();
Blacklisted Token.
@philliperosario maybe you can clear my mind on something, I also have a setup very much like yours (with vue).
I need to keep track of my user's active tokens (in case I have to blacklist them), so I have a database table having every user ID and their active tokens, every time the user access a protected route I have to update my database to edit the token? Is there a better way to do it (even not being JWT related)? I'm kinda in the dark for this.
@rafaelpimpa you can create a table on database with "user_id" and "logged_until". When a new token is generated for user you UPDATE_OR_CREATE a registry with user id and the timestamp of expiry date. Users actives = logged_until > now.
@philliperosario makes sense, but you think should be better if I set the token's ttl to a higher number and do not refresh them, or just use like your setup and update database for every request? I do have some sensitive data, not sure if the first option is secure enough.
Thanks for your insight :)
The idea behind JWTokens relies on its payload. If you need to query a DB to validate/invalidate a token, then you probably have to reconsider your authentication mechanism. Short-lived tokens and an in-memory cache'd blacklist should be enough.
thanks! :)
I has some troubles to implement to, but I solved them. 1 - Exactly the same @philliperosario 2 - I create a App\Http\Middleware\BaseJWTMiddleware.php with
`<?php
/*
namespace TradeAppOne\Http\Middleware;
use Tymon\JWTAuth\JWTAuth; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Contracts\Routing\ResponseFactory;
abstract class BaseJWTMiddleware { /**
@var \Illuminate\Contracts\Routing\ResponseFactory */ protected $response;
/**
@var \Illuminate\Contracts\Events\Dispatcher */ protected $events;
/**
@var \Tymon\JWTAuth\JWTAuth */ protected $auth;
/**
@param \Tymon\JWTAuth\JWTAuth $auth */ public function __construct(ResponseFactory $response, Dispatcher $events, JWTAuth $auth) { $this->response = $response; $this->events = $events; $this->auth = $auth; }
/**
@return mixed */ protected function respond($event, $error, $status, $payload = []) { $response = $this->events->fire($event, $payload, true);
return $response ?: $this->response->json(['error' => $error], $status);
} } `
3 - Then the RefrshToken.php, same path `<?php
namespace TradeAppOne\Http\Middleware;
use Tymon\JWTAuth\Exceptions\JWTException; use Tymon\JWTAuth\Exceptions\TokenExpiredException; use Tymon\JWTAuth\Facades\JWTAuth;
class RefreshToken extends BaseJWTMiddleware {
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, \Closure $next) {
if (!$token = $this->auth->setRequest($request)->getToken()) {
return $this->respond('tymon.jwt.absent', 'token_not_provided', 400);
}
try {
$user = $this->auth->authenticate($token);
} catch (TokenExpiredException $e) {
try {
$newToken = $this->auth->setRequest($request)->parseToken()->refresh();
} catch (TokenExpiredException $e) {
return $this->respond('tymon.jwt.expired', 'token_expired',
500, [$e]);
} catch (JWTException $e) {
return $this->respond('tymon.jwt.invalid', 'token_invalid',
500, [$e]);
}
header('Authorization: Bearer ' . $newToken);
JWTAuth::setToken($newToken)->toUser();
$user = $this->auth->authenticate($newToken);
} catch (JWTException $e) {
return $this->respond('tymon.jwt.invalid', 'token_invalid', 500,
[$e]);
}
if (!$user=JWTAuth::parseToken($token)) {
return $this->respond('tymon.jwt.user_not_found', 'user_not_found', 404);
}
$this->events->fire('tymon.jwt.valid', $user);
return $next($request);
}
}`
for Lumen walkthrough ?
@ralbear I came across the same problem, the solution to this is to set minimum-stability in composer.json file.
{ "minimum-stability": "dev", "prefer-stable": true }
I wish I could slowly kiss you on the lips!
Thanks for this great contribution, sir!
Am really frustrated right now if anyone can help
Route::any('{all}', function () { return view('index'); }) ->where(['all' => '.*']);
I've this route in route/web.php
which except all path and return the home page
but all my
site.com/api/* requests are still going the web instead of api am I missing anything here
I have changed my default guard to api still they're still going to web
@Akumzy , your api calls should go to the routes defined on api.php by default. You shouldn't have to mess with web.php routes at all.
Am handling my routing with vue-router
Hello.There is something I don't know. Maybe my program is running to add 'routes\api.php' step. But, I don't know add 'Authenticate method' and 'Axios interceptor' step. I want an example on a simple login vue. Is this example using vuex?
@KazukiSadasue login.vue - https://gist.github.com/core01/e10ded8de504d8bb919797c0d536c53f; Axios interceptor's step - https://gist.github.com/core01/9cb3c292576049e3be5cca0889ed3e52
@core01 Thank you! What is store file imported in connection.js? I don't have such a file.
@KazukiSadasue this is my vuex store:
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: { ... },
getters: { ... }
...
})
export default store
@core01 Thank you ! I will try !
@philliperosario how do you avoid axios from creating an instance without the token when it's first loaded (assuming the user is not logged in yet)
@philliperosario, awesome, thanks! All is working fine. Already tested this on "1.0.0-rc.2" version. Very usefull solution!
I am using
"php": ">=7.0.0",
"fideloper/proxy": "~3.3",
"laravel/framework": "5.5.*",
"laravel/tinker": "~1.0",
"tymon/jwt-auth": "1.0.0-rc.1"
it worked here Add the following code to the render method within app/Exceptions/Handler.php
public function render($request, Exception $e)
{
if($e instanceof \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException){
return response()->json([$e->getMessage()], $e->getStatusCode());
}
return parent::render($request, $e);
}
Since I lost tons of time doing tymon/jwt-auth work in my application, I decided to share my code in this walkthrough.
FOR LARAVEL:
Add
"tymon/jwt-auth": "1.0.0-rc.1"
to composer.json and runcomposer update
Add the service provider to the providers array in config\app.php:
Tymon\JWTAuth\Providers\LaravelServiceProvider::class
Add the facades to the aliases array also in config/app.php:
Run:
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
Run:
php artisan jwt:secret
If you are using CORS expose the response header "Authorization" to give JS allow getting header. Already if you are using Laravel CORS, then in config/cors.php specify expose the header:
The 1.0.0-rc.1 version requires you to implement Tymon\JWTAuth\Contracts\JWTSubject on your user model too. You must then add the required methods, which are getJWTIdentifier() and getJWTCustomClaims() to app\User.php:
App\Http\Controllers\AuthController.php:
Add to .env file:
Run:
php artisan config:cache
I created my own middleware, which works like this:
Create the file App\Http\Middleware\RefreshToken:
Add to routeMiddleware array in App\Http\Kernel.php:
'jwt' => \App\Http\Middleware\RefreshToken::class
routes\api.php:
FOR VUE.JS:
Authenticate method:
Axios interceptor for watch and save new tokens:
I think that's it. Good luck. And I hope I have helped.