In my unit test, I have a user for whom I generate a token:
$tokenString = $this->user->createToken('PHPunit', ['example'])->accessToken;
How can I afterward delete this user's token?
This is what I do when a user logged out.
public function logout() {
Auth::user()->tokens->each(function($token, $key) {
$token->delete();
});
return response()->json('Successfully logged out');
}
This code will remove each token the user generated.
I think something like this can revoke the token:
$this->user->token()->revoke()
Based on this link.
Laravel Sanctum documentation stated 3 different ways to revoke tokens. you can find it here.
but for most cases we just revoke all user's tokens via:
// Revoke all tokens...
auth()->user()->tokens()->delete();
note: for some reason intelephense gives an error saying tokens() method not defined but the code works fine. Hirotaka Miyata found a workaround here.
so the over all logout method can be something like this:
public function logout()
{
//the comment below just to ignore intelephense(1013) annoying error.
/** #var \App\Models\User $user **/
$user = Auth::user();
$user->tokens()->delete();
return [
'message' => 'logged out'
];
}
the best working solution is this
public function logout(LogoutRequest $request): \Illuminate\Http\JsonResponse
{
if(!$user = User::where('uuid',$request->uuid)->first())
return $this->failResponse("User not found!", 401);
try {
$this->revokeTokens($user->tokens);
return $this->successResponse([
], HTTP_OK, 'Successfully Logout');
}catch (\Exception $exception) {
ExceptionLog::exception($exception);
return $this->failResponse($exception->getMessage());
}
}
public function revokeTokens($userTokens)
{
foreach($userTokens as $token) {
$token->revoke();
}
}
public function __invoke(Request $request)
{
$request->user()
->tokens
->each(function ($token, $key) {
$this->revokeAccessAndRefreshTokens($token->id);
});
return response()->json('Logged out successfully', 200);
}
protected function revokeAccessAndRefreshTokens($tokenId) {
$tokenRepository = app('Laravel\Passport\TokenRepository');
$refreshTokenRepository = app('Laravel\Passport\RefreshTokenRepository');
$tokenRepository->revokeAccessToken($tokenId);
$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId);
}
Related
I am working with Laravel, Vue.js and I am currently using JWT auth for user authentication. I have implemented function for my buttons where I click the up button the vote increases and when I click the down it decreases but the problem is that if I switch user and login with another user I am still able do decrement another user's vote. The user_id in database is still the same and is not updated with new request. I am unable to find the problem please help as I am new to these technologies.
I have tried passing the id manually and then it works sometimes. I guess it is some problem with the authentication or my passing of user_id
this is my vote code:
public function vote(Request $request, $id) {
$issue = Issue::findOrFail($id);
$issueVotes = new \App\IssueVotes();
$issueVotes->user_id = auth()->id();
$issueVotes->issue_id = $request->issue_id;
if($request->vote_type =='up') {
$already_voted = \App\IssueVotes::where('user_id', auth()->id())->where('issue_id', $request->issue_id)->where('vote_type', $request->vote_type)->first();
if(!$already_voted) {
$issue->increment('votes');
$issueVotes->vote_type = $request->vote_type;
$issueVotes->save();
}
} elseif ($request->vote_type =='down') {
$voted_up = \App\IssueVotes::where('user_id', auth()->id())->where('issue_id', $request->issue_id)->where('vote_type', 'up')->first();
if($voted_up) {
#$delete_down = \App\IssueVotes::where('user_id', auth()->id())->where('issue_id', $request->issue_id)->where('vote_type', 'up')->delete();
$issue->decrement('votes');
$issueVotes->vote_type = 'down';
// $issueVotes->save();
}
}
return response()->json($request->post_id, 200);
}
And my JWT auth login code is:
public function login(Request $request)
{
$credentials = $request->json()->all();
try{
if (!$token = JWTAuth::attempt($credentials)){
return response()->json(['error'=>'invalid_credentials'], 400);
}
}catch(JWTException $e) {
return response()->json(['error' => 'could_not_create_token'], 500); // something went wrong whilst attempting to encode the token
}
return response()->json(compact('token'));
}
public function getAuthenticatedUser()
{
try{
if(!$user = JWTAuth::parseToken()->authenticate()){
return response()->json(['user_not_found'], 404);
}
}catch(Tymon\JWTAuth\Exceptions\TokenExpiredException $e){
return response()->json(['token_expired'], $e->getStatusCode());
}catch(Tymon\JWTAuth\Exceptions\TokenInvalidException $e){
return response()->json(['token_invalid'], $e->getStatusCode());
}catch(Tymon\JWTAuth\Exceptions\JWTException $e){
return response()->json(['token_absent'], $e->getStatusCode());
}
return $user;
}
What I am trying to achieve is that only the user who is logged in can change the vote value but of the posts on which he has given votes not on every post.
Here is the screenshot of my database where I switched the user but the user_id remains the same
I have a problem that I wanted to create a reactivate option for users, but after trying several times, it is not working and I am confused.
here is the middleware (original version):
public function handle($request, Closure $next)
{
if (!Auth::check()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('/');
}
}
else
{
$user = Auth::user();
if (!$user->activated) {
$activation = action('Auth\AuthController#getActivationMail', ['username' => $user->username]);
Auth::logout();
return redirect()->guest('auth')
->withErrors(array('message' => 'Please activate your account. Re-send your activation by clicking <a href=' . $activation . '>here</a>.'));
}
else if (!$user->enabled) {
Auth::logout();
return redirect('/auth')->withErrors(array('message' => 'Your account has been deactivated. Please email ... for any inquiries.'))->withInput();
// I tried to add the same method as the above if statement but not working here
}
$user->runDailyNotifications();
}
return $next($request);
}
I wanted to update my database using this way:
$user = Auth::user();
$user->enabled = 1;
$user->save();
which should be working fine.
I am new to Laravel. At first, I added these code in the middleware (which is a mistake).
After trying a bit I know it is impossible for it to work (when users click login twice they will log in after deactivating their account). Now I'm just wondering how could I achieve that since I kept getting error messages from everywhere. Thank you for the help!
I have done email confirmation and resend confirmation in one of my older projects. I've done the email confirmation validation in the post login check in the LoginController. Let me post you some snippets which might help you.
// Overwrite the authenticated method in LoginController
protected function authenticated(Request $request, $user)
{
if ($user->isBanned()) {
$this->logout($request);
flashError('Your account has been banned.');
return back();
}
if (!$user->isEmailConfirmed()) {
$this->logout($request);
flashWarning('Email confirmation pending. Click here to resend confirmation email.');
return back();
}
return redirect()->route($this->redirectRoute);
}
public function resendConfirmationEmail(Request $request, User $user)
{
//resend confirmation email
}
public function confirmEmail(Request $request, $token)
{
// Validate the token and update the user email confirmation status
}
Model
public function isBanned()
{
return (bool) $this->banned;
}
public function isEmailConfirmed()
{
return (bool) $this->confirmed;
}
Route
Route::get('confirm/resend/{user}', 'Auth\RegisterController#resendConfirmationEmail')->name('confirm.resend');
Route::get('confirm/email/{token}', 'Auth\RegisterController#confirmEmail')->name('confirm.email');
I'm using dingo/api (that has built-in support for jwt-auth) to make an API.
Suppose this is my routes :
$api->group(['prefix' => 'auth', 'namespace' => 'Auth'], function ($api) {
$api->post('checkPhone', 'LoginController#checkPhone');
//Protected Endpoints
$api->group(['middleware' => 'api.auth'], function ($api) {
$api->post('sendCode', 'LoginController#sendCode');
$api->post('verifyCode', 'LoginController#verifyCode');
});
});
checkPhone method that has task of authorize and creating token is like :
public function checkPhone (Request $request)
{
$phone_number = $request->get('phone_number');
if (User::where('phone_number', $phone_number)->exists()) {
$user = User::where('phone_number', $phone_number)->first();
$user->injectToken();
return $this->response->item($user, new UserTransformer);
} else {
return $this->response->error('Not Found Phone Number', 404);
}
}
And injectToken() method on User Model is :
public function injectToken ()
{
$this->token = JWTAuth::fromUser($this);
return $this;
}
Token creation works fine.
But When I send it to a protected Endpoint, always Unable to authenticate with invalid token occures.
The protected Endpoint action method is :
public function verifyCode (Request $request)
{
$phone_number = $request->get('phone_number');
$user_code = $request->get('user_code');
$user = User::wherePhoneNumber($phone_number)->first();
if ($user) {
$lastCode = $user->codes()->latest()->first();
if (Carbon::now() > $lastCode->expire_time) {
return $this->response->error('Code Is Expired', 500);
} else {
$code = $lastCode->code;
if ($user_code == $code) {
$user->update(['status' => true]);
return ['success' => true];
} else {
return $this->response->error('Wrong Code', 500);
}
}
} else {
return $this->response->error('User Not Found', 404);
}
}
I used PostMan as API client and send generated tokens as a header like this :
Authorization:Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI5ODkxMzk2MTYyNDYiLCJpc3MiOiJodHRwOlwvXC9hcGkucGFycy1hcHAuZGV2XC92MVwvYXV0aFwvY2hlY2tQaG9uZSIsImlhdCI6MTQ3NzEyMTI0MCwiZXhwIjoxNDc3MTI0ODQwLCJuYmYiOjE0NzcxMjEyNDAsImp0aSI6IjNiMjJlMjUxMTk4NzZmMzdjYWE5OThhM2JiZWI2YWM2In0.EEj32BoH0URg2Drwc22_CU8ll--puQT3Q1NNHC0LWW4
I Can not find solution after many search on the web and related repositories.
What is Problem in your opinion?
Update :
I found that not found error is for constructor of loginController that laravel offers :
public function __construct ()
{
$this->middleware('guest', ['except' => 'logout']);
}
because when I commented $this->middleware('guest', ['except' => 'logout']); all things worked.
But if I remove this line is correct?
How should be this line for APIs?
updating my config/api.php to this did the trick
// config/api.php
...
'auth' => [
'jwt' => 'Dingo\Api\Auth\Provider\JWT'
],
...
As I mentioned earlier as an Update note problem was that I used checkPhone and verifyCode in LoginController that has a check for guest in it's constructor.
And because guest middleware refers to \App\Http\Middleware\RedirectIfAuthenticated::class and that redirects logged in user to a /home directory and I did not created that, so 404 error occured.
Now just I moved those methods to a UserController without any middleware in it's constructor.
Always worth reading through the source to see whats happening. Answer: The is expecting the identifier of the auth provider in order to retrieve the user.
/**
* Authenticate request with a JWT.
*
* #param \Illuminate\Http\Request $request
* #param \Dingo\Api\Routing\Route $route
*
* #return mixed
*/
public function authenticate(Request $request, Route $route)
{
$token = $this->getToken($request);
try {
if (! $user = $this->auth->setToken($token)->authenticate()) {
throw new UnauthorizedHttpException('JWTAuth', 'Unable to authenticate with invalid token.');
}
} catch (JWTException $exception) {
throw new UnauthorizedHttpException('JWTAuth', $exception->getMessage(), $exception);
}
return $user;
}
I use Laravel5.2 to build a E-commerce platform and get some troubles.When I loginout user state and I use Session::forget('user') or $request->session()->forget('user'), it works in current page,and I redirect to the login page and dd(Session::all()) find the session 'user' still exist!!! so I am confused to know what is wrong with it? plz tell me the reason,thanks all.
ps. loginout code
public function logout(Request $request)
{
if ($request->session()->has('user')) {
$is_forgotten = $request->session()->forget('user');
if ($is_forgotten === null)
echo json_encode(['result' => 1]);
exit;
}
echo json_encode(['result'=>0,'msg'=>'loginout error']);
exit;
}
The session doesn't get saved until the response is sent and events/middleware are triggered.
Try doing:
public function logout(Request $request)
{
if ($request->session()->has('user')) {
$is_forgotten = $request->session()->forget('user');
if ($is_forgotten === null)
return Response::json(['result' => 1]);
return '';
}
return Response::json(['result'=>0,'msg'=>'loginout error']);
}
Or if your using the helpers:
public function logout(Request $request)
{
if ($request->session()->has('user')) {
$is_forgotten = $request->session()->forget('user');
if ($is_forgotten === null)
return response()->json(['result' => 1]);
return '';
}
return response()->json(['result'=>0,'msg'=>'loginout error']);
}
I wanted to find out how I would do as requested in the subject line, as the code below works fine but the user is logged in before checking the $user->Activated status. Here is some code to illustrate:
AuthController
public function authenticated(Request $request, User $user)
{
if ($user->Activated) {
return redirect()->intended($this->redirectPath());
} else {
Auth::logout();
return redirect($this->loginPath())
->withInput($request->only('email', 'remember'))
->withErrors([
'activated' => 'You need to activate your account to login'
]);
}
}
Preferably I would like to do the following:
AuthController
public function getCredentials(Request $request)
{
$credentials = $request->only($this->loginUsername(), 'password');
return array_add($credentials, 'Activated', '1');
}
But then the only message that gets returned is "These credentials do not match our records.", instead of "You need to activate your account to login". Also how would I update a LoginStatusId once the user is logged in, currently I do it like this:
AuthController
public function authenticated(Request $request, User $user)
{
if ($user->Activated) {
$user->LoginStatusId = 1;
$user->save();
return redirect()->intended($this->redirectPath());
} else {
Auth::logout();
return redirect($this->loginPath())
->withInput($request->only('email', 'remember'))
->withErrors([
'activated' => 'You need to activate your account to login'
]);
}
}
Is there a better place to set the $user->LoginStatusId once they login, or is this the best place to put it?
Open this file vendor\laravel\framework\src\Illuminate\Foundation\Auth\AuthenticatesUsers.php
Add this block of code inside postLogin
// If activated is equal to 1, user allowed to login or throw an credential mismatch error
$userData = User::select('activated')->where('email', $request['email'])->first();
if ($userData->activated == 1) {
$request['activated'] = $activated;
}
$credentials = $this->getCredentials($request); //add these code before this line
And add 'activated' to getCredentials method. It will look like this:
protected function getCredentials(Request $request)
{
return $request->only($this->loginUsername(), 'password', 'activated');
}
You can check user login status anywhere just using this Auth::user(). No need to store login status by yourself. As example in any controller you can write this:
if(Auth::user()){
// do this;
}else{
// do that;
}