I'm trying to change the expiration date of access token Laravel Passport.
Here's what I have tried:
AuthServiceProvider
public function boot(){
$this->registerPolicies();
Passport::routes();
Passport::tokensExpireIn(Carbon::now()->addDays(1));
Passport::refreshTokensExpireIn(Carbon::now()->addDays(2));
Passport::personalAccessTokensExpireIn(Carbon::now()->addMonths(1));
}
UserController
public function login() {
$credentials = [
'email' => request('email'),
'password' => request('password')
];
if (Auth::attempt($credentials)) {
$success['token'] = Auth::user()->createToken('MyApp')->accessToken;
$success['name'] = Auth::user()->name;
return response()->json(['success' => $success]);
}
return response()->json(['error' => 'Unauthorized'], 401);
}
But it didn't work. The expired date didn't change in the database at field expires_at, it's still one year by default.
I'm trying to do this, cause I want to make a redirect to login form when access token will get expired. How can I do it?
I'm also not sure what would happen with a refresh token, will it return another access token and the user will not need to authorizе?
You're creating a personal access token that belongs to user.
A personal access token has a default expiration date of 1 year.
Looking at your code I'm pretty sure that this command should do the work:
Passport::personalAccessTokensExpireIn(Carbon::now()->addMonths(1));
Double-check the expire_at column in the database and expires_in value in your response when you getting the token. It shows the number of seconds the token lives.
Related
I have an api that has a method to start and I am calling it from a frontend project.
In the front end project I use Guzzle to make the call via post to the api and login, from which I get back a json with the user data and a jwt token.
But when I receive the token as I manage the session, I must create a session and save the token, since the laravel to authenticate I need a model user and have a database, which of course I do not have in this backend because I call the api to log in, which brings a token and user data, then as I manage it from the backend, I'm a little lost there.
$api = new Api();
$response = $api->loginapi(['user'=>'wings#test.com','password'=>'123']);
Because here I could not do Auth::login($user) to generate the session.
Because I don't have here the database because the login is done from the api.
There I call the api, of which the answer is the token, but how do I manage it from here, creating a session? saving the token?
thanks for your help.
With api, you don't usually manage a session. usually, you'd call something like
Auth::attempt([
'email' => 'me#example.com',
'password' => 'myPassword'
]);
If the credentials are correct, laravel will include a Set-Cookie header in response, and, that is how you authenticate with api. Via an auth cookie. You don't need to do anything else.
Let's show you how:
//AuthController.php
public function login(Request $request) {
$validatedData = $request->validate([
'email' => 'required|email',
'password' => 'required'
]);
if(Auth::attempt($validatedData)){
return ['success' => 'true'];
}
else{
return ['success' => false, 'message' => 'Email or password Invalid'];
}
}
public function currentUser (){
return Auth::user();
}
Now, the APi file
Route::post('/login', ['App\Http\Controllers\AuthController', 'login']);
Route::get('/current_user', ['App\Http\Controllers\AuthController', 'currentUser']);
Now if you make a call to /api/current_user initially, you'll get null response since you're not currently logged in. But once you make request to /api/login and you get a successful response, you are now logged in. Now if you go to /api/current_user, you should see that you're already logged in.
Important ::
If you are using fetch, you need to include credentials if you're using something other than fetch, check out how to use credentials with that library or api
You want to use the API to authenticate and then use the SessionGuard to create session including the remember_me handling.
This is the default login controller endpoint for logging in. You don't want to change this, as it makes sure that user's do not have endless login attempts (protects for brut-force attacks) and redirects to your current location.
public function login(Request $request)
{
$this->validateLogin($request);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if (method_exists($this, 'hasTooManyLoginAttempts') &&
$this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
if ($request->hasSession()) {
$request->session()->put('auth.password_confirmed_at', time());
}
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
The core happens when we try to "attemptLogin" at
protected function attemptLogin(Request $request)
{
return $this->guard()->attempt(
$this->credentials($request), $request->boolean('remember')
);
}
When using the SessioGurad (which is default) the method attemptLogin fires a couple of events, checks if the user has valid credentials (by hashing the password and matching it with db) and then logs the user in, including the remember me functionality.
Now, if you don't care about events, you can just check from your API if the credentials match and then use the login method from the guard. This will also handle the remember me functionality. Something like this:
protected function attemptLogin(Request $request)
{
$username = $request->input($this->username());
$password = $request->input('password');
$result = \Illuminate\Support\Facades\Http::post(env('YOUR_API_DOMAIN') . '/api/v0/login' , [
'username' => $username,
'password' => $password
])->json();
if(empty($result['success'])){
return false;
}
// Maybe you need to create the user here if the login is for the first time?
$user = User::where('username', '=', $username)->first();
$this->guard()->login(
$user, $request->boolean('remember')
);
return true;
}
I have an API with Laravel 8 and use JWT for authentication
When I log in several times, a new token is created for the user, but the problem is that the previously created tokens are valid and usable. (Until the token expires)
So how can I fix this bug?
this is my login() and createNewToken() method in AuthController() :
public function login(Request $request) {
$validator = Validator::make($request->all(), [
'email' => 'required|email',
'password' => 'required|string|min:6',
]);
if ($validator->fails()) {
return response()->json($validator->errors(), 422);
}
if (! $token = JWTAuth::attempt($validator->validated())) {
return response()->json(['error' => 'احراز نشده'], 401);
}
return $this->createNewToken($token);
}
protected function createNewToken($token) {
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => JWTAuth::factory()->getTTL() * 240,
'user' => auth()->user()
]);
}
I think the best way is to check in login method , if this user has a token, invalidate it and create a new one, else create a new token.
but I don't know how to check the user token with JWT
In advance thanks for your help.
You can check for sessions in the login method.
For example, you can create unique IDs for sessions and set them as jti claims for each token you issue (see here). You need to store them somewhere (e.g. a cache data store). Then, during login, you check if the jti of the received JWT is valid. This way, you can check if a token is revoked or not.
But keep in mind that this undermines the stateless nature of the JTI.
A better approach when using JWTs is to have short lived access tokens (e.g. 5-15 minutes) and use refresh tokens to get new tokens, so that the user doesn't have to log in every 5 minutes. In such a scenario you should not be bothered by an old token - it will only be valid for a few minutes anyway.
If you really need an option to "revoke" access tokens, you should implement it in the part which validates received JWTs, not the login method. As #Deniz suggested in his answer you will need a store to keep some data which can be validated with the content of the JWT. Using session ID in the jti claim is one valid option. Another option would be to keep in a database a timestamp of the last issued access token for a given user, and then validate the iat claim - issued at. If you receive a token for a given user with iat lower than what you have in the DB, you reject the request.
Anyway, if you want to revoke a JWT only because someone logged in again, then it sounds like you want to use sessions, and JWTs are not good for sessions. Just go with plain old cookies.
I'm doing my authentication system with Laravel & JWT, but I have some questions.
I use the tymon jwt package
I have a token generated at login, for 24 hours, and if the remember-me box is checked, it is valid for 2 years.
Except that how should I proceed to renew the token during these 2 years, I guess I don't have to keep the same one, for security reasons?
Do I have to store something in a database ? like a remember-me token for example, or a refresh-token ?
I'm a bit lost with all this, and I'd like to understand how to proceed.
I've already searched quite a bit on the internet, but I can't find what I want, or it's incomplete.
public function login()
{
$credentials = request(['email', 'password']);
$ttl = env('JWT_TTL');
if (request(['remember_me']) == true) {
$ttl = env('JWT_REMEMBER_TTL');
}
if (!$token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Wrong credentials'], 401);
}
return $this->respondWithToken($token, $ttl);
}
protected function respondWithToken($token, $ttl)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => $ttl
]);
}
JWT_TTL=1440
JWT_REMEMBER_TTL=1051200
Thanks in advance,
You don't have to store anything in a database.
Create two JWTs, one as the access token (login) and one for remember me. Set the access token to expire for 24 hours as usual, and the remember me token to expire for 2 years.
On your protected route, check if the access token is expired, and if it is, check for the remember me token. If the remember me token is present, issue a new access token.
You can handle it in this way as well and its working fine with JWT package using setTTL function and define two different time in .env file.
$ttl = ($request->remember_me === true) ? env('JWT_REMEMBER_TTL') : env('JWT_TTL'); if (! $token = auth()->setTTL($ttl)->attempt($credentials)) {}
Authentication is working, I have a few routes under auth middleware, Whenever i request it throws :
{
"message": "Failed to authenticate because of bad credentials or an invalid authorization header.",
"status_code": 401
}
How can i send the token with the request like :
Authorization bearer {{Long token}}
It works with `postman`, How can i send the token with request header, Or in any other best way.
Route :
$api->get('/categories', [
'uses' => 'App\Http\Controllers\CategoryController#index',
'as' => 'api.categories',
]);
Method :
public function index() {
$lessons = \App\Category::all();
$token = JWTAuth::getToken(); // $token have jwt token
return response()->json([
'data' => $lessons,
'code' => 200,
]);
}
The question was pretty vague to answer. Please be more specific from next time. From your comments i could finally realise that you want to consume the api from a mobile app.
You need to return the token generated for an user either during login or during registration or any other authentication method/route you have. The mobile app needs to read this response and store the token locally. Then the app needs to inject this token in the request header for every single request. That's the normal api token workflow.
The app should also be coded to read the error response from requests and if it returns errors for expired or invalid token, the app needs to clear the locally stored token and then request the user to login again to generate a fresh token.
you can use : https://github.com/tymondesigns/jwt-auth
requriment :
Laravel 4 or 5 (see compatibility table)
PHP 5.4 +
Steps:
1 : add below line in composer.json in require array
"tymon/jwt-auth": "0.5.*"
2 : run "composer update" in your terminal
3 : after this you have to register service provider
go to config/app.php
and add 'Tymon\JWTAuth\Providers\JWTAuthServiceProvider' this in provider array
and 'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth' , 'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory' this to aliases array
4 : publish pacakge :
"php artisan vendor:publis --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
5 : generate secrate key in config file
'php artisan jwt:generate'
6 : for addition configuration : https://github.com/tymondesigns/jwt-auth/wiki/Configuration
Usage :
AuthenticateController.php
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
class AuthenticateController extends Controller
{
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'], 401);
}
} catch (JWTException $e) {
// something went wrong whilst attempting to encode the token
return response()->json(['error' => 'could_not_create_token'], 500);
}
// all good so return the token
return response()->json(compact('token'));
}
}
You can also skip user authentication and just pass in a User object. e.g.
// grab some user
$user = User::first();
$token = JWTAuth::fromUser($user);
The above two methods also have a second parameter where you can pass an array of custom claims. e.g.
$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
JWTAuth::attempt($credentials, $customClaims);
// or
JWTAuth::fromUser($user, $customClaims);
create token based on anything
$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
$payload = JWTFactory::make($customClaims);
$token = JWTAuth::encode($payload);
d
Upon login, I return the user object + session token in JSON form, so that the mobile device that connects to my application can be authenticated.
However, I have a difficulty understanding how would I go about authenticating the user only with his session id?
Once logged in, the mobile device sends the session token upon every request, which means I somehow need to check whether it's the same user (using a custom auth filter).
How would I do it?
You may have a table for saving tokens
Add a filter in routes.php
Route::group(array('before' => 'auth'), function() { ... })
And in the filters.php you can search the token in the database, if isn't exist you return a no access response
Route::filter('auth', function () {
$input_token = Input::get('token');
if (!empty($input_token)) {
$validator = Validator::make(
['token' => $input_token],
['token' => 'token']
);
if (!$validator->fails()) {
$token = Token::where('hash', $input_token)->first();
if ($token) {
$user = User::find($token->user_id);
if ($user) {
Auth::login($user);
return;
}
}
}
}
$response = Response::make(json_encode([
'error' => true,
'messages' => [
Lang::get('errors.NO_ACCESS')
]
]), 200);
$response->header('Content-Type', 'application/json');
return $response;
});
You could do it like this:
$sessionID = '4842e441673747d0ce8b809fc5d1d06883fde3af'; // get this from \Session::getId(); from your previous authenticated request (after logging in because it changes).
$s = new \Illuminate\Session\Store(NULL, \Session::getHandler(), $sessionID);
$s->start();
$userID = $s->get('login_82e5d2c56bdd0811318f0cf078b78bfc');
\Session::set('login_82e5d2c56bdd0811318f0cf078b78bfc', $userID);
return \Auth::user();
Not the prettiest code but it works. It creates an instance of a session using the previous Session ID, then start loads it up from file. The user ID is in that key, so then it just sets the user id on the current session. Then when you call Auth::user() it loads up the User using that user id.
The reason for all the numbers in the key is because the larval developer thought it would be smart to hash the Auth class name to make the key as unique as possible... :-S