Laravel 5.1 and Socialite Questions - php

This isn't a post about a problem I'm having right now. I'm planning on using the Laravel Socialite plugin in my App, I already have a fully working login/register system using Laravel's built in Auth system but I'm wondering if there is a good way to allow users that already have their accounts in my app to link their social profiles to the already existing account?
The plan would be that they can login with their username and password or any of their social profiles. All the research I've done so far explains how to set up Socialite and utilise that data for registering/logging in new users, however I can't find anything to marry the two together.
I'm not looking for anyone to write this for me so please don't get confused by this post.
Thanks for any info,
Andy

Absolutely.
I have FB and Twitter OAuth on one site. The way I do it is by adding oauth_facebook_id and oauth_twitter_id columns to the users table (as well as an avatar column, if you want that).
I have the routes set up as /auth/{provider} and /auth/{provider}/callback, so /auth/facebook and /auth/facebook/callback for example.
In my handleProviderCallback($provider) method on my AuthController, I grab the returned user's details and check to see if they already exist in my database with their email, Facebook OAuth ID or Twitter OAuth ID. If they exist, I set their email, OAuth IDs and their avatar, if they don't exist I create them. Then I log them in.
$user = Socialite::driver($provider)->user();
$user_query = User::where('email', $user->email)
->orWhere('oauth_facebook_id', $user->id)
->orWhere('oauth_twitter_id', $user->id)
->get();
if($user_query->count() > 0) {
$the_user = $user_query->first();
if($provider === 'facebook') {
$the_user->oauth_facebook_id = $user->id;
}
if($provider === 'twitter') {
$the_user->oauth_twitter_id = $user->id;
}
$the_user->avatar = $user->avatar;
$the_user->save();
\Auth::login($the_user, true);
return redirect('/');
}
$new_user = User::create([
'name' => $user->name,
'email' => $user->email,
'oauth_facebook_id' => $provider === 'facebook' ? $user->id : NULL,
'oauth_twitter_id' => $provider === 'twitter' ? $user->id : NULL,
'avatar' => $user->avatar
]);
\Auth::login($new_user, true);
return redirect('/');
Hopefully this helps you make sense of how to do it.

Related

Login on webapp by token sent by e-mail

I am building a webapp that is supposed to be hosted in my company servers and used through the intranet. The requirements are:
The user accesses the webapp.
The app requests an e-mail address.
An e-mail containing a unique link (token) is sent to the address.
The user clicks on the link to log in without a password.
I am developing the webapp using Symfony3 and I thought of using the FriendsOfSymfony User bundle. How can I acheive that? FOSUserBundle is not mandatory.
The login functionalities you want to achieve do not diver that much from e.g. resetting a password by email. Except the temporary token in your use case is used to authenticate the user instead of authenticating a password reset.
A very simple explanation
You should create an entity that stores the authentication token, e.g. AutoLogin that has user, token and a timestamp property.
On the submit of your 'authentication form' a new AutoLogin record gets stored with a relationship towards the user and the user gets notified by email.
Whenever the user clicks the link you should have a method that validates the timestamp for a timeframe and authenticate the user by your user provider.
Examples
Symfony 2: AutoLogin
I think after you accepted the email this is what you can do:
sent url to email like this
<?php
$url = "http://example.com/login.php?token=$token";
?>
Then you login page
<?php
// retrieve token
if (isset($_GET["token"]) && preg_match('/^[0-9A-F]{40}$/i', $_GET["token"]))
{
$token = $_GET["token"];
}
else {
throw new Exception("Valid token not provided.");
}
// verify token
$query = $db->prepare("SELECT username, tstamp FROM pending_users WHERE token = ?");
$query->execute(array($token));
$row = $query->fetch(PDO::FETCH_ASSOC);
$query->closeCursor();
if ($row) {
extract($row);
}
else {
throw new Exception("Valid token not provided.");
}
// do action here, like activating a user account/redirect
// ...
// delete token so it can't be used again
$query = $db->prepare(
"DELETE FROM pending_users WHERE username = ? AND token = ? AND tstamp = ?",
);
$query->execute(
array(
$username,
$token,
$tstamp
)
);
Assuming you have tables like the ones in my queries. Hope i answered you well
There is a service called fos_user.security.login_manager that can help:
public function loginByTokenAction($token)
{
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('AppBundle:User')->findOneByToken($token);
$this->container->get('fos_user.security.login_manager')->loginUser('firewall_name', $user);
// ...
}
source : https://github.com/symfony/symfony/pull/13062

What is 'The right way' to authorize this REST API request?

I'm building a REST Api and I'm sitting here with a problem. By no means I am an expert on this subject, so I want to learn how to address REST architecture the 'right way' (or at least in a way that makes sense).
I'm building a web application with a Angular Frontend and a laravel-based, RESTfull backend API. The app has these 3 tables: Albums, Posts and Comments. A user can write a post in an album if he/she is a member of that album.
A user can be invited to become member of an album and then see all it's posts and the comments for those posts. If an user isn't (invited to be) a member of an album it can't comment on posts in that album.
In other words: if a user comments on a post, the post has to be from an album the user is a member of.
My dilemma is: how do I check/authorize this request?
My Eloquent relationships are as follows:
The user table has a many to many relationship with albums
Albums have many posts
Posts have many comments
The incoming request is a POST request that has 2 parameters:
album_id (the album that the post is in)
post_id (for the post that is being commented on)
body (The actual comment itself)
The author for the post is retrieved via Auth::user();
My initial thoughts for addressing this problem are:
I check for all the albums a user is a member of
Build an array of al the ID's of the found albums the user is a member of
Check if the post_id parameter is in that array
If it's not, the user can't comment and if it is, the user can comment
My code so far:
// Validate the Request Body
$input = $this->request->only([ 'album_id', 'post_id', 'comment' ]);
$validator = Validator::make( $input, [
'album_id' => 'required|integer',
'post_id' => 'required|integer',
'comment' => 'required',
]);
// Authorize the Request
$albumIdList = Auth::user()->albums()->get()->pluck('id')->toArray();
$postIdList = Post::whereIn( 'album_id', $albumIdList )->select('id')->get()->toArray();
if( ! in_array($this->request->get('post_id'), $albumIdList))
return abort(403);
// Store the new comment
$comment = Comment::create([
'user_id' => Auth::user()->id,
'post_id' => $input['post_id'],
'comment' => $input['comment'],
]);
return $comment;
I think this is working properly, but what if a album has 1000 posts? Building the array wit all post ID's becomes really intensive for the server... How would a professional company (like Facebook, Twitter, Pinterest) tackle this in their web application?
Thanks in advance!
You're looking for the whereHas and exists methods:
$allowed = Auth::user()->albums()->whereHas('post', function ($query) {
$query->where($this->request->only('post_id'));
})->exists();
Also, there's no reason to pass in the album_id. Your code can be reduced to this:
$this->validate($this->request, [
'post_id' => 'required|integer',
'comment' => 'required',
]);
$allowed = Auth::user()->albums()->whereHas('posts', function ($query) {
$query->where($this->request->only('post_id'));
})->exists();
if (! $allowed) return abort(403);
$input = $this->request->only('post_id', 'comment');
return Comment::create($input + ['user_id' => Auth::id()]);
If you want to clean this up further, you should look into Laravel's authorization facilities.

Get a user by username and password with Laravel

I need to do some extra checks on a user, I would like to get the user by username and password.
Firstly:
Is there a built in function that gets a user by username and password without authenticating them?
Secondly:
If the above is no, then how do I correctly hash the password, because if I use Hash::make( $password ) and then compare to the database, it is not the same... You would usually use Hash::check but I need to actually get the user by username and password.
In Laravel 5.2
You can use Auth::once($credentials) to validate credentials and thereafter Auth::getUser(); to get the user.
$credentials = Request::only('username', 'password');
if(!Auth::once($credentials)) {
// Invalid user credentials; throw Exception.
}
$user = Auth::getUser();
First:
If you want to check if user data to authentication is correct you can use:
if (Auth::validate($credentials))
{
//
}
But if you want to get user from database with user and password, you can use:
$user = User::whereName($username)->wherePassword(Hash::make($password))->first();
Second
To store password in database you should use Hash::make($password) as you showed and it works without any problems. Using Auth::validate should solve the issue.
Yes, there is a built in function you should use. I recommend you to read the docs. But here's a good example, it's pretty self-evident:
$input = array(
'username' => Input::get('username'),
'password' => Input::get('password'),
);
$remember = (boolean)Input::get('remember'); //'Remember me' checkbox
if (Auth::attempt($input, $remember)) {
return Redirect::intended('dashboard')->with("success", "You're logged in!"); //Redirect the user to the page intended to go to, with the dashboard page as default
}
Registering a user looks something like this:
$input = array(
'username' => Input::get('username'),
'email' => Input::get('email'),
'password' => Hash::make(Input::get('password')) //Encrypt password
);
$user = User::create($input);
I also recommend you to read about input validation. I hope this helps, good luck.
Edit: I didn't read the "without authenticating them" part. You should use Auth::validate($input) as Marcin already explained.
Laravel 5.7
To check a users credentials without logging them in, I had to do this:
$user = User::whereEmail($request->email)->first();
$user = password_verify($request->password, optional($user)->getAuthPassword()) ? $user : false;
Laravel auth validation makes use of https://www.php.net/manual/en/function.password-verify.php

Laravel API Auth class usage

Trying to build and API based on laravel that aims to grow to intense usage by lots of clients. My question is whether there are serious drawbacks of using Auth class in my code ?
I have implemented the OAuth2 authorization, and to get info about the user that is making the request I have a filter :
Route::filter('hasAccess', function($request)
{
//get the cleaned token string
$auth_code = Request::header('Authorization');
$auth_code = trim(preg_replace('/Bearer/sui', "", $auth_code));
//get the stored session and put the query in cache for 10 minutes
$ts = DB::table('sessions as s')
->leftJoin('oauth_session_access_tokens as osat', 's.token', '=', 'osat.id')
->select('s.*')
->where('osat.access_token', '=', $auth_code)
->remember(10, $auth_code)
->first();
//Auth user cross-app
Auth::onceUsingId($ts->user);
//Extract the requested action
$request = $request->getAction();
$request = $request['controller'];
$parts = explode('#', $request);
$required = strtolower($parts[0]).'.'.$parts[1];
$required = preg_replace('/controller/sui', "", $required);
//Get the permissions
$permissions = json_decode($ts->permissions, true);
$permissions = array_fetch($permissions,'name');
if (!in_array($required,$permissions))
{
return Response::json([
'error' => true,
'dataset' => 'You don\'t have rights to access this url'
]);
}
});
It validates the user access rights to the controller action, but the most interesting in it is the row with Auth::onceUsingId($ts->user);. This rows authorizez the user for only 1 request. Also if any other ways to get info about user exist, please mention them. Thanks
You talk about 'serious drawbacks' of using Auth class code - but you dont really explain drawbacks compared to what? Just manually looking in the database yourself for the user?
All the Auth::onceUsingId() is doing is logging your user into the application without a session or cookie. This is perfect for an API - as you dont normally have persistence between requests.
You can then do Auth::user() to get data about the user, such as Auth::user()->name.

CakePHP: How to login using only barcode?

I started to learn CakePHP about month ago, so I'm pretty new to it and I would like to ask a question: How can I login using only barcode?
My plan is to make a login only with barcode for regular users and for admin login they would have regular username and password. I have searched many pages but I haven't yet find any solutions for this.
For the example QR-Code:
http://www.example.com/User/login?username=simon&password=12345
In Controller use this in login-action:
if ($this->request->is('get'))
{
$user = $this->User->find('first', array(
'conditions' => array(
'User.name' => $this->request->query['username'],
'User.password' => $this->request->query['password'] // donĀ“t forget to hash password here
)));
if ($user != null)
{
$this->Auth->login($user);
$this->redirect('/admin/index');
}
}

Categories