Oct 13, 2016

Create PHP/Laravel authentication APIs with Firebase PHP-JWT

On all PHP/Laravel projects (SPA apps) I always use Firebase/PHP-JWT to create our authentication APIs for mobile apps or javascript apps.
Firstly, we include Firebase/PHP-JWT to our projects by adding & downloading dependencies:
composer require firebase/php-jwt
When user login via AJAX call, we need response to client a token (jwt).

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Validator;
use Config;
use Hash;
use Log;
use App\User;
use \Firebase\JWT\JWT;
use \Firebase\JWT\ExpiredException;

class AuthController extends Controller
{
    protected function jwt(User $user)
    {
        $payload = array(
            'sub'   => $user->id,
            'iat'   => time(),
            'exp'   => time() + 60*60
        );
        return JWT::encode($payload, Config::get('app.jwt-secret-key'));
    }

    public function login(Request $req)
    {
        $validator = Validator::make($req->all(), array(
            'email' => 'required|email|min:4|max:255',
            'password'  => 'required|min:8|max:80'
        ));

        if ($validator->fails()) {
            return response()->json(['error' => $validator->errors()], 400);
        }

        $user = User::where('email', '=', $req->input('email'))->first();

        if (!$user) {
            return response()->json(['error' => 'Your email not exists'], 401);
        }

        if (Hash::check($req->input('password'), $user->password)) {
            return response()->json(['jwt' => $this->jwt($user)], 200);
        } else {
            return response()->json(['error' => 'Wrong passwd'], 401);
        }
    }
}
Method jwt(User $user) return a string called token. Basically on authentication we create and store a token locally on client (browser, smart phone device). Then every time we make a call to the server, we check that token and ensure the user is who they say they are.

The routes:

Route::group(array('prefix' => 'api'), function() {
    Route::post('auth/register', 'AuthController@register');
    Route::post('auth/login', 'AuthController@login');
    Route::post('auth/user', 'AuthController@user'); // response returns logged user
});

Corresponding user() method:

    /**
     * return authenticated user
     * return NULL if not authenticated
    **/
    public function user(Request $req, $nullOnFail = false)
    {
        try {
            list($jwtType, $jwt) = explode(' ', $req->header('Authorization'));
        } catch(\ErrorException $e) {
            if ($nullOnFail)
                return null;
            return response()->json(['error' => 'Can not parse Authorization header'], 400);
        }
        if ($jwt) {
            try {
                $credentials = JWT::decode($jwt, Config::get('app.jwt-secret-key'), ['HS256']);
            } catch(ExpiredException $e) {
                if ($nullOnFail)
                    return $nullOnFail;
                return response()->json(['error' => 'jwt_expired'], 400);
            } catch(\Exception $e) {
                if ($nullOnFail)
                    return $nullOnFail;
                return response()->json(['error' => 'An error while decoding jwt'], 400);
            }

            $user = User::where('id', '=', get_object_vars($credentials)['sub'])->first();
            if ($nullOnFail)
                return $user;
            return response()->json(['user' => $user], 200);
        }
        return response()->json(['error' => 'No jwt attached'], 401);
    }

No comments:

Post a Comment