I am doing some POSTing to my laravel API.
I have added a unique 'api_token' column to the users table, and I want to retrieve the user from this token.
In a controller I can validate a token - the $apiUserValid is true or false depending on whether or not a row exists with a matching api_token value:
$apiToken = "Y2uibY5MIV";//correct token
$apiCredentials = ['api_token' => $apiToken];
$apiUserValid = \Auth::guard('api')->validate($apiCredentials);
if ($apiUserValid) {
var_dump(\Illuminate\Support\Facades\Auth::user()); // shows "NULL"
var_dump(\Illuminate\Support\Facades\Auth::id()); //shows NULL
var_dump(\Illuminate\Support\Facades\Auth::guard('api')->user()); // shows "NULL"
die();
} else {
die('not valid');// when the token is incorrect this branch is taken
}
However, I want to get the userId of the user with that token.
The SessionGuard has some methods that look relevant - such as login() but these are not present in the TokenGuard.
I am currently simulating this in a controller rather than dealing with a http request object - this could be part of my problem.
How do I get the user id of the person making the POST?
You can use this:
<?php
use Illuminate\Support\Facades\Auth;
$userId = Auth::id();
The relevant documentation is here.
Alternatively, you can use this:
<?php
use App\User;
$user = User::where('api_token', $apiToken)->first();
$userId = $user->id;
Related
My laravel project has an API route by auth.basic middleware which is used id of the authenticated user in the controller. when I call it in postman it works well and I get 401 when the username or password is incorrect, but in laravel APITest which extends from DuskTestCase, authentication does not take place so I get 500 instead of 401 when the user's informations were incorrect. However, by correct information, I have the same error because auth() is null.
it is written like below, which is wrong?
api.php route:
Route::get('/xxxxxx/xxxxxx/xxxxxxx', 'xxxxx#xxxx')->middleware('auth.basic');
APITest:
$response = $this->withHeaders(['Authorization' => 'Basic '. base64_encode("{$username}:{$password}")])->get("/xxxxxx/xxxxxx/xxxxxxx");
You can use actingAs() method for authentication in tests.
An example from docs:
public function testApplication()
{
$user = factory(App\User::class)->create();
$this->actingAs($user)
->withSession(['foo' => 'bar'])
->visit('/')
->see('Hello, '.$user->name);
}
Another example that you can use for an API:
$user = User::factory()->create();
$response = $this->actingAs($user)->json('GET', $this->uri);
$response->assertOk();
For more information: https://laravel.com/docs/5.1/testing#sessions-and-authentication
I have the following middleware in my PostController.
public function __construct()
{
$this->middleware('auth')->except(['index', 'show']);
}
I understand any authenticated user can still edit a post by visiting localhost/posts/{post}/edit so I've protected that by the following code.
public function edit(Post $post)
{
if(auth()->user()->id === $post->user_id){
$categories = Category::all();
return view('edit-post', compact(['post', 'categories']));
} else{
abort(403, 'Unauthorized.');
}
}
Now, I'm wondering is it necessary to protect the destroy method? Is it possible for an authenticated user to delete a post they didn't create in this case? If they can could I kindly know how they can?
My destroy method
public function destroy(Post $post)
{
Storage::disk('public')->delete($post->imagePath);
$post->delete();
return redirect(route('posts.index'))->with('flash', 'Post Deleted Successfully');
}
one easy way to protect all your methods that need authentification is to use relations.
You are sending a post id in the URL by use model injection to preload $post from DB. Avoid that and use the id of the post yourself
public function destroy($postId)
{
$post = auth()->user()->posts()->findOrFail($postId);
Storage::disk('public')->delete($post->imagePath);
$post->delete();
return redirect(route('posts.index'))->with('flash', 'Post Deleted Successfully');
}
The route will return a 404 if the post id is not one of the user owned posts.
Same for the edit
public function edit($postId)
{
$post = auth()->user()->posts()->findOrFail($postId);
$categories = Category::all();
return view('edit-post', compact(['post', 'categories']));
}
As for the change in response code 404 instead of a 403, it is on purpose, since the user is authenticated and you dont want any user to know if another post with random ID that is not his exists or not hence 404. like if he put a non existing post id to delete or edit.
First, you should be read about Laravel policy, which will make your code more clear.
For destroy method,
I will give you an example, you can try it BTW
post_table
id
user_id
title
1
40
Post1
2
50
Post2
If the user id: 40 tries to delete the post witch id is id: 1 there is no problem,
BUT let's say the user knows about the web app and just changes the id in the URL localhost/posts/2/delete, He/She will delete any post without policy.
Top of my controller i have added:
use Auth;
function in my controller
public function details($id)
{
if(Auth::check())
{
$user = Auth::user();
$product = Product::find($id);
$cart->product_id = $product->id;
$cart->category_id = $product->category_id;
$cart->user_id = $user->id;
dd($cart->user_id); //check if its storing the value
}
else {
return redirect()->route('login');
}
}
when I run this i get error:
Creating default object from empty value
If I remove the $user->id line the error goes.
I tried adding constructor also but still got same error
public function __construct() {
$this->middleware('auth');
}
dd is showing other details after it checks if user is logged in.
Is there a way to get user id of logged in user from users table that is created by default.
Thanks!
The issue is you’re calling $cart->product_id, but the $cart variable isn’t defined as far as I can see. PHP doesn’t know what to do, so because you try and assign a property to it, PHP assumes you want $cart to be a class, hence the “Creating default object from empty value” message.
Other than that, you code could be improved by using middleware to authenticate your users, and relations on your Eloquent models so you’re not manually assigning relation IDs.
Try like this,
You forgot to initials cart object
$cart = new Cart;
$cart->product_id = $product->id;
$cart->category_id = $product->category_id;
$cart->user_id = $user->id;
dd($cart->user_id); //check if its storing the value
On the edit profile page for a user, I want to show the existing values of the current logged-in user details like name, email, gender etc. My questions are as follows
Is it recommendable to user Auth::user()->name , Auth::user()->email directly to populate the form fields ? Or shall I create a variable like $user = Auth::user(); in my controller and pass it on to my view to $user like a regular object?
Does using Auth::user(), multiple times on a given view file hit my database each time I use it?
Thanks in advance.
If you look at the SessionGuard.php file in Illuminate\Auth, you'll see the method user() which is used to retrieve the currently authenticated user:
/**
* Get the currently authenticated user.
*
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user()
{
if ($this->loggedOut) {
return;
}
// If we've already retrieved the user for the current request we can just
// return it back immediately. We do not want to fetch the user data on
// every call to this method because that would be tremendously slow.
if (! is_null($this->user)) {
return $this->user;
}
$id = $this->session->get($this->getName());
// First we will try to load the user using the identifier in the session if
// one exists. Otherwise we will check for a "remember me" cookie in this
// request, and if one exists, attempt to retrieve the user using that.
$user = null;
if (! is_null($id)) {
if ($user = $this->provider->retrieveById($id)) {
$this->fireAuthenticatedEvent($user);
}
}
// If the user is null, but we decrypt a "recaller" cookie we can attempt to
// pull the user data on that cookie which serves as a remember cookie on
// the application. Once we have a user we can return it to the caller.
$recaller = $this->getRecaller();
if (is_null($user) && ! is_null($recaller)) {
$user = $this->getUserByRecaller($recaller);
if ($user) {
$this->updateSession($user->getAuthIdentifier());
$this->fireLoginEvent($user, true);
}
}
return $this->user = $user;
}
// If we've already retrieved the user for the current request we can just return it back immediately. We do not want to fetch the user data on every call to this method because that would be tremendously slow.
if (! is_null($this->user)) {
return $this->user;
}
So, calling the user() multiple times won't make multiple calls to the database.
You'll get only 1 request to database, so using Auth::user() multiple times is not a problem.
I recommend you using Laravel Debugbar as the most comfortable way for app optimization.
Ok so I read how to implement Laravel socialite into my application so I can let users log in using Google or Facebook. I read how to do it here. The problem I encounter with this approach is that if a user logs in from say Google with email myemail#example.com then logs in from Facebook using myemail#example.com they are logging into the same account! Security issue right. So I thought that before I let them log in I would check the provider id which you can get, however when I try to compare the provider_id that I store in the database when they create the account with the provider_id stored in the socialite user variable I get this error:
Argument 1 passed to Illuminate\Auth\SessionGuard::login() must implement interface Illuminate\Contracts\Auth\Authenticatable, instance of Illuminate\Http\RedirectResponse given
Here is all the code I am using for Socialite:
<?php
namespace App\Http\Controllers;
use Socialite;
use App\User;
use Auth;
use Illuminate\Support\Facades\Redirect;
use Flash;
use Illuminate\Http\Request;
use App\Http\Requests;
class SocialiteController extends Controller
{
public function redirectToProvider($provider)
{
return Socialite::driver($provider)->redirect();
}
public function handleProviderCallback($provider)
{
try
{
$social_user = Socialite::driver($provider)->user();
}
catch(Exception $e)
{
return Redirect::to('auth/' . $provider);
}
$authUser = $this->findOrCreateUser($social_user);
Auth::login($authUser, true);
flash()->overlay('You have been logged in successfully!', 'Congratulations');
return Redirect::to('/');
}
//create a new user in our database or grab existing user
private function findOrCreateUser($social_user)
{
if ($authUser = User::where('email', $social_user->email)->first()) {
//this creates an error
if($authUser->provider_id == $social_user->id)
return $authUser;
else
{
flash()->overlay('An account for that email already exists!', 'Error');
return Redirect::to('/');
}
}
return User::Create([
'provider_id' => $social_user->id,
'name' => $social_user->name,
'email' => $social_user->email,
'nickname' => $social_user->nickname,
'avatar_img' => $social_user->avatar,
'role_id' => 1, //set role to guest
'social_login' => true //tell the database they are logging in from oauth
]);
}
}
The error message is actually self explained.
When you do User::where('provider_id', $social_user->id) you end up with builder object that implements
Illuminate\Database\Eloquent\Builder.
You can call ->get() on it to get the collection of results
(a collection of objects that implements
Illuminate\Contracts\Auth\Authenticatable
in your case, so you can iterate over them), or as you did, you can get the first match with ->first() (one object that implement
Illuminate\Contracts\Auth\Authenticatable).
You can read more in the Eloquent documentation.
The main point is that until you call ->get() or ->first() you are working with builder object.
There is actually ->find() method also, that is used to retrieve record by pk, you cannot use it to search for records by constraints (where), but it also returns model object.
The problem I encounter with this approach is that if a user logs in
from say Google with email myemail#example.com then logs in from
Facebook using myemail#example.com they are logging into the same
account! Security issue right.
In my opinion, this is an incorrect assumption. It is not a security issue.
OAuth is used as an authentication delegation, if a user controls accounts in different providers with the same email, it simply means he has more 3rd party OAuth services with which to validate control of his identity (email)
I would consider it a bug, if you limited me to only be able to sign in with one OAuth provider, and prohibited me to user another one with the same email.