skip to Main Content

I am developing a mobile app which should do API calls to an own laravel backend.

Frontend: Ionic 2 + Angular2

Backend: Laravel 5.3 + Laraval Passport + MySQL

At the user can log in with password grant (username + password).

Now I want to offer a login via Facebook.

I’ve implemented a Login with Facebook-button in the app. This works fine. I get the profile information from the Facebook API: id, email, name

Now this user (has no email + password combination from our server) should be able to make API calls to our Laravel server and should be linked to user in the users-table of the MySQL-database behind the laravel backend. Users which login with Facebook shouldn’t need any username or password to login. Just Facebook.

I want to generate a new user in the database for each facebook user (simply with a column facebook_id). But how to give such users an access_token?

Accepting just the Facebook ID, match this (or create new) user in the database and create an access_token would be very unsecure because Facebook ID is public…

2

Answers


  1. I must say I have same problem couple of weeks ago. Only difference I got was that I have both, ionic2 app and website. Both must support username/password login as social login (google, facebook).

    So how did I did that (I will write for facebook, google is slightly different – better):

    1. Prepare your facebook app to accept logins from mobile AND webpage. You will need facebook client_id and client_secret.
    2. Install socialite package for laravel. And set it up to work with facebook ( in app/services.php set facebook ).

    Now when you got this you can start coding. You said you already have it working on Ionic2 part. So that means you get token and other data from facebook for user.

    What I did is I make request to my api and send this token and user_id. Then on my API side I check if token is valid, login user and issue passport token.

    Ionic2 code:

    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 );
      });
    

    Now Laravel part. I made SocialController.php and url (POST request) /api/social-login/facebook:

    <?php
    
    namespace AppHttpControllersApi;
    
    use AppHttpControllersController;
    use AppModelSocialLogin;
    use AppUser;
    use IlluminateHttpRequest;
    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
            ];
        }
    }
    

    Now this will return you passport token and you can make api request to protected routes.

    About passport, email, username, ….. you will have to change database and make it nullable. And add facebook_id field.

    And be sure to make requests over https, because your are sending token.

    Hope it helps.

    Login or Signup to reply.
  2. in addition to @Bostjan’s answer adding my generalised implementation :

    SocialAccount here is a laravel model where you’ll provider and provider_user_id and local database user id. Below is the example of social_accounts table

    enter image description here

    And in SocialController :

    <?php
    
    namespace AppHttpControllers;
    
    use AppHttpControllersController;
    use IlluminateSupportFacadesHash;
    use IlluminateHttpRequest;
    use AppUser;
    use AppSocialAccount;
    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
        ];
    }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search