Retrieve user by Sanctum plainTextToken - laravel

How to retrieve the 'logged in' user from a Sanctum token.
For logging in I have the following method
public function login(Request $request)
{
if (Auth::attempt($request->toArray())) {
/* #var User $user */
$user = $request->user();
$token = $user->createToken('web-token')->plainTextToken;
return response()->json([
'user' => $user,
'token' => $token,
], Response::HTTP_OK);
}
}
Now for logging out I use a custom method.
public function logout(Request $request)
{
dd($request->user()); // <- Always returns null
}
I want to revoke the token, but I don't know how to retrieve the currently logged in user. Obviously for logging out I send the Authorization header with the Bearer and plainTextToken as value.

for sure you have first add token in bearer token
and to get user out of sanctum middleware now token is optional
$user = auth('sanctum')->user();
than log out
if ($user) {
$user->currentAccessToken()->delete();
}
note : this delete only current token
if u need all tokens use
foreach ($user->tokens as $token) {
$token->delete();
}

If you don't use the default Sanctum middleware, you can get the user from the plain text token as follow:
use \Laravel\Sanctum\PersonalAccessToken;
/** #var PersonalAccessToken personalAccessToken */
$personalAccessToken = PersonalAccessToken::findToken($plainTextToken);
/** #var mixed $user */
$user = $personalAccessToken->tokenable;

Since you're sending the bearer/token to the Logout url you can try to override the logout function of the AuthenticatesUsers:
/**
* Log the user out of the application.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function logout(Request $request)
{
$this->guard()->logout();
$request->user()->tokens()->delete();
return redirect('/');
}

simply add the route within middleware('auth:sanctum') grouped routes
then from inside the targeted function you can get user like this auth()->user()
or if you just want to log out the user you can revoke token like this
$request->user()->currentAccessToken()->delete();

Related

Laravel: Using multiple columns for authentication

I have a Laravel 6 application.
In a typical application, the user would just specify their email, where the email is unique.
However, in my application, I have 2 columns in the User model that is used to authenticate users.
app_id
email
unique(app_id, email)
So in order to login, we need to pass both an app_id and an email, along with the password. The same email could be used across different app_ids.
How would I achieve this?
The default login actions provided by Auth::routes() are the following:
Route::get('login', 'Auth\LoginController#showLoginForm')->name('login');
Route::post('login', 'Auth\LoginController#login');
This is the default login function, part of the AuthenticatesUsers trait used by the LoginController:
/**
* Handle a login request to the application.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*
* #throws \Illuminate\Validation\ValidationException
*/
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)) {
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);
}
There are a few ways to approach this.
Option 1: Override login function in LoginController
# app/Http/Controllers/Auth/LoginController.php
public function login(Request $request)
{
// add more stuff like validation, return the view you want, etc.
// This is barebones
auth()->attempt($request->only(['app_id', 'login', 'password']);
}
Option 2: Override both the validateLogin and the credentials functions in LoginController
# app/Http/Controllers/Auth/LoginController.php
protected function validateLogin(Request $request)
{
$request->validate([
'app_id' => 'required|string',
'email' => 'required|string',
'password' => 'required|string',
]);
}
/**
* Get the needed authorization credentials from the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(Request $request)
{
return $request->only('app_id', 'email', 'password');
}

How to throw an error if user role is not allowed Laravel

I am trying to leverage the Laravel auth system and add logic that does not allow you to log in if you have the user role of user. I want to extend authenticate users.
protected function sendLoginResponse(Request $request)
{
$user = $this->guard()->user();
$request->session()->regenerate();
if ($user->role != 'admin') {
return redirect()->back()->with('error', 'You do not have permissions to view this page');
$this->clearLoginAttempts($request);
return $this->authenticated($request, $this->guard()->user())
?: redirect()->intended($this->redirectPath());
}
}
The issue is it still logs me in. How can I make this logic work?
Add this function to your LoginController, it will override the default function and add an extra constraint for logging in:
/**
* override to add check
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(Request $request)
{
$credentials = $request->only($this->username(), 'password');
$credentials['role'] = 'admin'; //only login admin
return $credentials;
}

how to add bearer access token in request header from controller in laravel

I'm working on Laravel API Authentication. I have done login, register part. when I want to get-details of any user using postman every time i need add Bearer access_token in request header.
But I want to send it from my Backend Controller or Middleware .
How can i do this.
Here is my Code Sample. I try this way But not working.
public function details(Request $request)
{
$user = Auth::user();
$token='Bearer '.$request->bearerToken();
$request->header('Authorization',$token);
return response()->json(['success' => $user], $this-> successStatus);
}
I do this in middleware, for example:
<?php
namespace App\Http\Middleware;
use Closure;
class AccessTokenMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$request->headers->add(['Authorization' => "Bearer {$request->access_token}"]);
return $next($request);
}
}

User confirmation, login with username and email

I changed the login function a bit, that the user can only log in with his username and his email if his email address was confirmed by a sent email.
what do I want to do
If the email address has not yet been confirmed, I would like to redirect the user to a page to confirm his email address. If the table "users, active" has a 1, the address has been confirmed.
Currently I have problems logging in with the username. Does anyone recognize a mistake?
How can I implement that? Does anyone have a similar code?
/**
* Get the needed authorization credentials from the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(Request $request)
{
$field = filter_var($request->get($this->username()), FILTER_VALIDATE_EMAIL)
? $this->username()
: 'username';
return [
$field => $request->get($this->username()),
'password' => $request->password,
];
}
/**
* Validate the user login request.
*
* #param \Illuminate\Http\Request $request
* #return void
*/
protected function validateLogin(Request $request)
{
$this->validate($request, [
$this->username() => [
'required', 'string',
Rule::exists('users')->where(function ($query){
$query->where('active', true);
})
],
'password' => 'required|string',
], $this->validationError());
}
New loginController
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo = '/iboard';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest', ['except' => ['logout', 'userLogout']]);
}
/**
* Get the needed authorization credentials from the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(Request $request)
{
$field = filter_var($request->get($this->username()), FILTER_VALIDATE_EMAIL)
? $this->username()
: 'username';
return [
$field => $request->get($this->username()),
'password' => $request->password,
];
}
public function login(Request $request)
{
$this->validateLogin($request);
if (Auth::once($this->credentials($request))) { //use auth once so that it will not create auth session
$user = Auth::user();
if($user->active){
Auth::login($user); //now create auth session, check
return redirect('/iboard'); //redirect to dashboard url
}else{
return redirect('email_confirm')->with('fail', 'Please confirm your email'); //redirect to email confirm page
}
}
return redirect()->back()->with('fail', "Invalid username or password");
}
public function userLogout()
{
Auth::guard('')->logout();
return view('/exit');
}
}
You can try the below code for your login, assuming you have validateLogin and credentials functions in the same controller because the below login action used both of these function. Check details here
public function login(Request $request)
{
$this->validateLogin($request);
if (Auth::once($this->credentials($request))) { //use auth once so that it will not create auth session
$user = Auth::user();
if($user->active == 1){
Auth::login($user); //now create auth session
return redirect('dashboard'); //redirect to dashboard url
}else{
return redirect('email_confirm')->with('error', 'Please confirm your email'); //redirect to email confirm page
}
}
return redirect()->back()->with('error', "Invalid username or password");
}

Checking if user account is confirmed by email code With unnecessary logging in Laravel

I have a laravel project, where I try implement checking if user account is confirmed by activation code from user's email:
public function SignIn(Request $request){
if(Auth::attempt(['email' => $request['email'], 'password' => $request['password']])) {
if(Auth::user()->status==false){
Auth::logout();
Session::flash('activationError','First please active your account');
return back();
}
return redirect()->route('showDashboardWelcome');
}
else{
Session::flash('loginError','Your username and password are wrong');
return back();
}
}
In above code there is function from controller to Sign in user. This function check if the user email and password are correct and if user has confirmed account($status variable from user model with two boolean values) by email code.
If account is not confirmed by user I must logout user from account, because function attempt start simultaneously user's session. Is there any possible way to do this operation without starting a user session and then logout user?
I would be grateful for help. Best regards
Add a middleware to directly query the users table and check the status field. If not verified redirect away to the page showing the message. For example:
<?php
namespace App\Http\Middleware;
use Closure;
class CheckVerified
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$user = User::where('email', $request->email)->first();
if (!user->status) {
return redirect()->back()->with('not verified');
}
return $next($request);
}
}
You can pass as many parameters to attempt([]) as you want. So here you can add 'status' to the array like this
if(Auth::attempt([
'email' => $request['email'],
'password' => $request['password'],
'status' => true
])) {
}
Every single parameter you pass to that array has to be met for the Auth::attemp() to start the session.

Resources