2

I'm working on back-end part for mobile application. The back-end will be a RESTful API. The main question for me - it's authorization/authentication users.

We will use the Facebook authorization (Facebook SDK for iOS) on the mobile application side. The main question here is: how to implement authorization on back-end side using only data we can get from Facebook?

Maybe someone already got some solutions or can provide some examples for this task?

I can imagine this process as:

  1. User press the "Login with FB" button in the application and getting the redirect to FB, where he approve the connect

  2. User returns to the application with some data from FB (user Facebook ID, some user data and authorization token)

  3. Application sends this data to the API and trying to register/authenticate the user

  4. API save the authorization token and then use this token for check the user when application send the requests to API

Am I correct or this logic is wrong? Please advise, and if possible provide some examples.

Also I'm found this guy, but not sure it will be helpful in my situation...

Cœur
  • 37,241
  • 25
  • 195
  • 267
Dmytro Medvid
  • 4,988
  • 3
  • 25
  • 29

2 Answers2

4

So, done some research about the issue and now have some results. The authentication process now looks like this:

On the CLIENT:

  • Use the Facebook API to login and get an OAUTH2 code.
  • Exchange this code for an access token.
  • Request an access token from my API, including the Facebook token as a parameter

On the API

  • Receive access token request.

  • Make a request to the /me Facebook graph using the facebook access token

  • Verify that the Facebook user exists and match to a user in my database

  • Create my own access token, save it and return it to the client to be used from this point forward

First of all I created a migration for user table:

<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id')->unique();
            $table->bigInteger('facebook_id')->unique();
            $table->string('name')->unique();
            $table->string('email')->unique();
            $table->string('password')->nullable();
            $table->string('accessToken')->nullable();
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('users');
    }
}

Then I added LaravelFacebookSdk to the project which is fully unit-tested package for easily integrating the Facebook SDK v5 into Laravel and Lumen.

Then I added some routs for authentication:

Route::group(['prefix' => '/auth' /*, 'middleware' => 'throttle:10,5'*/], function () {
    Route::get('/', 'ApiAuthController@index');
    Route::get('/base', 'ApiAuthController@baseAuth');
    Route::get('/fb', 'ApiAuthController@facebookAuth');
    Route::get('/vk', 'ApiAuthController@vkAuth');
});

And created a controller to handle these routes:

namespace App\Http\Controllers;

use App\Http\Requests;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use SammyK\LaravelFacebookSdk\LaravelFacebookSdk;
use App\User;

class ApiAuthController extends Controller
{
    protected $baseAuthFailedResponse = [
        'status' => false,
        'message' => 'Base authentication failed'
    ];
    protected $facebookAuthFailedResponse = [
        'status' => false,
        'message' => 'Facebook authentication failed'
    ];
    protected $vkAuthFailedResponse = [
        'status' => false,
        'message' => 'VK authentication failed'
    ];

    /**
     * Echo function
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function index(Request $request){
        return response()->json($request);
    }

    /**
     * Authorise user with base authorisation using email and password
     *
     * @param Request $request - expects fields: email, password
     * @return \Illuminate\Http\JsonResponse
     */
    public function baseAuth(Request $request){
        $isAuthorised = Auth::attempt(
            array(
                'email' => $request->input('email'),
                'password' => $request->input('password')
            )
        );
        if ($isAuthorised){
            return response()->json(Auth::user());
        }

        return response()->json($this->baseAuthFailedResponse);
    }

    /**
     * Authorise user using facebook accessToken received in the request
     *
     * @param Request $request - expects fields: accessToken, username, fullName, email
     * @return \Illuminate\Http\JsonResponse
     */
    public function facebookAuth(Request $request, LaravelFacebookSdk $fb){
        if(!Auth::check()){
            // Receive access token request.
            $accessToken = $request->input('accessToken');
            // Make a request to the /me Facebook graph using the facebook access token
            try {
                $response = $fb->get('/me?fields=id,name,email', $accessToken);
            } catch(\Facebook\Exceptions\FacebookSDKException $e) {
                $this->facebookAuthFailedResponse['details']['message'] = $e->getMessage();
                $this->facebookAuthFailedResponse['details']['error_code'] = $e->getCode();

                return response()->json($this->facebookAuthFailedResponse);
            }

            // Verify that the Facebook user exists and match to a user in my database or create new one

            // Convert the response to a `Facebook/GraphNodes/GraphUser` collection
            $facebookUser = $response->getGraphUser();

            // Create the user if it does not exist or update the existing entry.
            // This will only work if you've added the SyncableGraphNodeTrait to your User model.
            $user = User::createOrUpdateGraphNode($facebookUser);

            Auth::login($user, true);
        }

        return response()->json(Auth::user());
    }

    public function vkAuth(Request $request){
        return response()->json($this->vkAuthFailedResponse);
    }
}

Also, as you can see I used a function $user = User::createOrUpdateGraphNode($facebookUser); which provided by LaravelFacebookSdk. To use it in our model we should implement the SyncableGraphNodeTrait. This method really makes it easy to take data that was returned directly from Facebook and create or update it in the local database.

<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;
use SammyK\LaravelFacebookSdk\SyncableGraphNodeTrait;

class User extends Authenticatable
{
    use SyncableGraphNodeTrait;

    /**
     * The keys of the array are the names of the fields on the Graph node.
     * The values of the array are the names of the columns in the local database.
     */
    protected static $graph_node_field_aliases = [
        'id' => 'facebook_id'
    ];

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

If someone have any notes about solution, please provide it in comments.

UPD: The auth process description I'm got from this topic. And also this topic was very useful. An API description from the official site. How to easy remember the user I found in this topc. And there I found some useful information about API servers on Laravel. And good description about authentication with tokens

Community
  • 1
  • 1
Dmytro Medvid
  • 4,988
  • 3
  • 25
  • 29
1

The process looks fine. Only the one thing you have to add - when you make API calls do include Laravel CSRF token.

AlexLV
  • 11
  • 1
  • Good advice. I'm also found some information about the CSRF protection on the official site here: https://laravel.com/docs/master/routing#csrf-protection – Dmytro Medvid May 05 '16 at 18:15