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;
}
Related
I want to make the password optional while login into the system. If the user enters the password the login works fine and return the jwt token, when I entered to try to login only with email it gives the following error:-
Undefined index: password (500 Internal Server Error)
The following is the code of my login method
public function authenticateUser($request)
{
$input = $request->only('email','password');
if (!$authorized = Auth::attempt($input, true)) {
return $this->failure('Credentials doesnot match our records!', 401);
} else {
$token = $this->respondWithToken($authorized);
return $this->success('Login Successfully !', $token, 200);
}
}
protected function respondWithToken($token)
{
return [
'token' => $token,
'token_type' => 'Bearer',
'expires_in' => Auth::factory()->getTTL() * 60,
'user' => Auth::user()
];
}
so basically, what I want is when a user enters an email it will login and should return the token, and if the user login with email and password then it should also work and return the token.
You can create a custom Authentication User Provider that will work around this potentially missing 'password' field. Though, I would probably not here. You can check the input yourself to see if there is a password or not. If there is pass it through attempt like normal. If it is not there find the user using the configured User Provider and login to the guard (what attempt is doing).
Perhaps something like this:
public function authenticateUser($request)
{
if ($request->has('password')) {
$token = Auth::attempt($request->only(['email', 'password']));
} else {
$token = ($user = Auth::getProvider()->retrieveByCredentials($request->only(['email'])))
? Auth::login($user)
: false;
}
return $token
? $this->success('Login Successfully !', $this->respondWithToken($token), 200)
: $this->failure('Credentials do not match our records!', 401);
}
The error that you're getting means that there is no password key in the input array that you're sending via request. This happens on this line:
$input = $request->only('email','password');
In order to bypass that, you would need go get all inputs, or check if those inputs exist and then read from them:
//Get all inputs
$input = $request->input();
//Or get email first, and then check for password
$input['email'] = $request->email;
$input['password'] = $request->filled('password') ? $request->password : null;
Note: Since I can't see your actual login functions, this might not work with only email, since password might be required parameter. If that's the case, you will have to alter those functions.
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.
I'm learning Laravel 5.4 and customizing and making my original Auth functionalities.
The below is my "authenticate" method.
public function authenticate(Request $request)
{
$remember_me = (Input::has('remember')) ? true : false;
Auth::guard('web');
$this->validateLogin($request);
$credentials = array(
'username' => trim($request->input('username')),
'password' => trim($request->input('password'))
);
if(Auth::attempt($credentials, $remember_me)){
$user = Auth::guard('web')->user();
Auth::guard('web')->login($user, $remember_me);
return redirect()->route('mypage');
}
return redirect()->back();
}
I have a question about the part of $remember_me argument about both attempt and login methods noted above.
What is the difference between them?
When I saw the documentation, it said similar to, if you want to make "remember me" token, you can set the second boolean argument about both of them.
attempt($credentials, $remember_me) will attempt to log the user in if the login credentials are correct. If they are not, then the user is not logged in. This method returns a boolean so you can check success.
login($user_id, $remember_me) will log the user in, without checking any credentials.
The remember me specifys if the user login should persist across browser sessions without needing to re-auth.
In your example I see your calling login(...) within your attempt(...). This shouldn't be needed. You can remove the login(...) line.
Example:
if(Auth::attempt($credentials, $remember_me)){
return redirect()->route('mypage');
}
Can't...Log users... in... Angry.
Basically, I'm running users through the regular login, with my own authenticated method defined to return a json object instead of being redirected. For the moment, I'm returning the results of this function
Auth::check();
Which returns true when all is said and done.
The problem is that my logins don't appear to exist beyond this single call. If I make anymore ajax calls, Auth::check() fails every time. If I refresh the page, auth::check() fails.
Please god help me.
PS. My session driver is current set to cookies.
//EDIT TO SHOW THE PEOPLE I'M NOT CRAZY
public function ajaxLogin(Request $request)
{
if(Auth::check()){
return response()
->json('loggedIn');
}
// 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.
$throttles = $this->isUsingThrottlesLoginsTrait();
if ($throttles && $lockedOut = $this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$credentials = $this->getCredentials($request);
if (Auth::guard($this->getGuard())->attempt($credentials, $request->has('remember'))) {
return $this->handleUserWasAuthenticated($request, $throttles);
}
// 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.
if ($throttles && ! $lockedOut) {
$this->incrementLoginAttempts($request);
}
return response()
->json([
$this->loginUsername() => $this->getFailedLoginMessage(),
]);
}
protected function authenticated($request, $user){
Auth::login($user);
return response()->json( 'yo');
}
If I send the right credentials, I get back yo. If I send the right credentials again (another ajax call) I get yo. I can never get logged in.
From what I can tell, for some reason, my sessions are being destroyed with every request? I don't know why, but this does appear to be why I'm never logged in.
//EDIT ADD SITUATIONAL CLARITY
laravel -v 5.2
this is all through the auth controller, so just the default:
Route::group(['middleware' => 'web'], function () {
Route::auth();
Route::get('/home', 'HomeController#index');
});
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