Паспорт Laravel и вход в Ionic2 через Facebook

Я разрабатываю мобильное приложение, которое должно выполнять вызовы API к собственному бэкэнду laravel.

Интерфейс: Ionic 2 + Angular2

Серверная часть: Laravel 5.3 + Laraval Passport + MySQL

У пользователя есть возможность авторизоваться с помощью предоставления пароля (имя пользователя + пароль).

Теперь я хочу предложить вход через Facebook.

Я реализовал в приложении кнопку «Войти через Facebook». Это нормально работает. Я получаю информацию о профиле из API Facebook: идентификатор, адрес электронной почты, имя

Теперь этот пользователь (не имеющий комбинации электронной почты и пароля от нашего сервера) должен иметь возможность выполнять вызовы API на наш сервер Laravel и должен быть связан с пользователем в таблице пользователей базы данных MySQL за серверной частью laravel. Пользователи, которые входят в систему через Facebook, не должны нуждаться в имени пользователя или пароле для входа. Просто Facebook.

Я хочу создать нового пользователя в базе данных для каждого пользователя facebook (просто со столбцом facebook_id). Но как дать таким пользователям access_token?

Принятие только идентификатора Facebook, сопоставление этого (или создание нового) пользователя в базе данных и создание access_token было бы очень небезопасным, потому что идентификатор Facebook является общедоступным ...


person rakete    schedule 22.01.2017    source источник
comment
Пожалуйста, еще раз объясните, чего вы пытаетесь достичь. Вы хотите, чтобы пользователь из мобильного приложения авторизовался на сайте?   -  person EddyTheDove    schedule 22.01.2017
comment
Пользователь должен иметь возможность войти в мобильное приложение через Facebook и должен быть сопоставлен с одним пользователем базы данных серверной части. Таким образом, мобильное приложение сначала выполняет вызов API к Facebook (чтобы получить идентификатор / работоспособность учетной записи!), А затем к собственному серверу Laravel, чтобы получить токен доступа для дальнейших вызовов API к серверу Laravel. Веб-сайт (такая же установка) не имеет никакого отношения к этому рабочему процессу ...   -  person rakete    schedule 22.01.2017
comment
OK. Сайт не имеет отношения к рабочему процессу. Тогда что не так?   -  person EddyTheDove    schedule 22.01.2017
comment
Пользователю нужен токен доступа ...   -  person rakete    schedule 22.01.2017
comment
Если идентификатор Facebook является общедоступным (и незащищенным), у вас есть их электронная почта от Facebook (которая не является общедоступной), которая по-прежнему уникальна. Вы можете использовать электронную почту для создания access_token.   -  person EddyTheDove    schedule 23.01.2017
comment
Если пользователь A знает электронную почту пользователя B, он может сгенерировать access_token для пользователя A и опубликовать сообщение от его имени.   -  person rakete    schedule 23.01.2017


Ответы (2)


Должен сказать, что пару недель назад у меня была такая же проблема. Единственная разница, которую я получил, заключалась в том, что у меня есть и приложение ionic2, и веб-сайт. Оба должны поддерживать вход по имени пользователя и паролю в качестве входа в социальную сеть (google, facebook).

Итак, как я это сделал (напишу для фейсбука, гугл немного другой - лучше):

  1. Подготовьте свое приложение facebook, чтобы принимать входы с мобильных и веб-страниц. Вам понадобятся facebook client_id и client_secret.
  2. Установите пакет socialite для laravel. И настроить его для работы с facebook (в app/services.php установить facebook).

Теперь, когда у вас есть это, вы можете начинать кодировать. Вы сказали, что он уже работает над частью Ionic2. Это означает, что вы получаете токен и другие данные из facebook для пользователя.

Я сделал запрос к своему API и отправил этот токен и user_id. Затем на своей стороне API я проверяю, действителен ли токен, регистрирую пользователя и выдаю токен паспорта.

Код Ionic2:

Facebook.login(["public_profile"])
  .then(response => {
    // login success send response to api and get token (I have auth service class to do that)
    this.auth.facebookLogin(response.authResponse).then(
     ...
    );
  }, error => {
    this.showAlert( this.loginFailedTitle, this.loginFailedText );
  });

Теперь расстаемся с Laravel. Я сделал SocialController.php и URL (запрос POST) /api/social-login/facebook:

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Model\SocialLogin;
use App\User;
use Illuminate\Http\Request;
use Socialite;

class SocialController extends Controller    
{    
    public function facebook(Request $request) {    
        $user = Socialite::driver('facebook')->userFromToken( $request->input('accessToken'));

        abort_if($user == null || $user->id != $request->input('userID'),400,'Invalid credentials');

        // get existing user or create new (find by facebook_id or create new record)
        $user = ....

        return $this->issueToken($user);
    }        

    private function issueToken(User $user) {
        $userToken = $user->token() ?? $user->createToken('socialLogin');

        return [
            "token_type" => "Bearer",
            "access_token" => $userToken->accessToken
        ];
    }
}

Теперь это вернет вам токен паспорта, и вы сможете сделать запрос api для защищенных маршрутов.

О паспорте, электронной почте, имени пользователя ... вам придется изменить базу данных и сделать ее доступной для null. И добавьте поле facebook_id.

И обязательно делайте запросы через https, потому что вы отправляете токен.

Надеюсь, это поможет.

person Bostjan    schedule 25.01.2017
comment
У меня отлично работает. :) Также использовался тот же подход для входа в социальную сеть Google. - person Sushant Pimple; 19.06.2018

в дополнение к ответу @ Bostjan, добавившему мою обобщенную реализацию:

SocialAccount вот модель laravel, в которой вы будете поставщиком, provider_user_id и идентификатором пользователя локальной базы данных. Ниже приведен пример social_accounts таблицы

введите здесь описание изображения

И в SocialController:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Http\Request;
use App\User;
use App\SocialAccount;
use Socialite;

class SocialController extends Controller
{
public function social(Request $request) {

    $provider = $request->input('provider');
    switch($provider){
        case SocialAccount::SERVICE_FACEBOOK:
            $social_user = Socialite::driver(SocialAccount::SERVICE_FACEBOOK)->fields([
                'name', 
                'first_name', 
                'last_name', 
                'email'
            ]);
            break;
        case SocialAccount::SERVICE_GOOGLE:
            $social_user = Socialite::driver(SocialAccount::SERVICE_GOOGLE)
            ->scopes(['profile','email']);
            break;
        default :
            $social_user = null;
    }

    abort_if($social_user == null , 422,'Provider missing');

    $social_user_details = $social_user->userFromToken($request->input('access_token'));

    abort_if($social_user_details == null , 400,'Invalid credentials'); //|| $fb_user->id != $request->input('userID')

    $account = SocialAccount::where("provider_user_id",$social_user_details->id)
            ->where("provider",$provider)
            ->with('user')->first();

    if($account){
        return $this->issueToken($account->user);
    }
    else { 
        // create new user and social login if user with social id not found.
        $user = User::where("email",$social_user_details->getEmail())->first();
        if(!$user){  
            // create new social login if user already exist.
            $user = new User;
            switch($provider){
                case SocialAccount::SERVICE_FACEBOOK:
                    $user->first_name = $social_user_details->user['first_name'];
                    $user->last_name = $social_user_details->user['last_name'];
                    break;
                case SocialAccount::SERVICE_GOOGLE:
                    $user->first_name = $social_user_details->user['name']['givenName'];
                    $user->last_name = $social_user_details->user['name']['familyName'];
                    break;
                default :
            }            
            $user->email = $social_user_details->getEmail();
            $user->username = $social_user_details->getEmail();
            $user->password = Hash::make('social');
            $user->save();
        }
        $social_account = new SocialAccount;
        $social_account->provider = $provider;
        $social_account->provider_user_id = $social_user_details->id;
        $user->social_accounts()->save($social_account);
        return $this->issueToken($user);
    }
} 

private function issueToken(User $user) {

    $userToken = $user->token() ?? $user->createToken('socialLogin');

    return [
        "token_type" => "Bearer",
        "access_token" => $userToken->accessToken
    ];
}
}
person Sushant Pimple    schedule 19.06.2018
comment
Вы используете один и тот же пароль «social» для всех учетных записей, созданных методом social (). Это небезопасно, если эти пользователи могут выполнять стандартный (не социальный) вход. - person JakubK; 15.02.2019