sdwru/laravel-firebase-auth-plus

Secure your laravel API with Google Firebase Auth and also get a full Firebase Admin SDK

1.0 2020-06-02 21:37 UTC

This package is auto-updated.

Last update: 2024-04-22 22:39:56 UTC


README

Secure your laravel API with Google Firebase Auth

Adding the Middleware to your Laravel API will ensure that access is only granted by using a valid bearer token issued by Goggle Firebase Auth.

The main difference between this package and the package we forked it from is that we are using laravel-firebase as a dependency. Using laravel-firebase instead of firebase-tokens removes the need for a service provider since it is already included in laravel-firebase. Since that package depends on firebase-php, you can also use all the feature firebase-php provides.

Role middleware

This package includes optional role middleware for more granular access.

Install

composer require sdwru/laravel-firebase-auth-plus

laravel-firebase

Publish the laravel-firebase ServiceProvider (Provider: Kreait\Laravel\Firebase\ServiceProvider) if not already done so.

php artisan vendor:publish

Configure laravel-firebase according to their instructions and also explained in the official firebase documentation at this link.

Those instructions make it sound more complicated than it is. All we need to do is generate a JSON file as follows:

  1. In the Firebase console, open Settings > Service Accounts.
  2. Click Generate New Private Key, then confirm by clicking Generate Key.
  3. Securely store the generated JSON file and add a reference to that file in your laravel .env file. The following example assumes we are storing the file in the root folder of our laravel installation. Rename it to whatever you want.
FIREBASE_CREDENTIALS=myproject-firebase-adminsdk.json

How to use

There are two ways to use this.

Method 1. Lock all access without JWT token

Add the Middleware on your app/Http/Kernel.php file.

\sdwru\LaravelFirebaseAuth\Middleware\JWTAuth::class,

Refer to the Laravel Middleware documentation on where you can put this in your Kernel.php file and how it can be used in routes.

Method 2 (recommended) using an authentication guard.

Add the Guard to app/Providers/AuthServiceProvider.php in the boot method.

public function boot()
{
   $this->registerPolicies();

   $this->app['auth']->viaRequest('firebase', function ($request) {
       return app(\sdwru\LaravelFirebaseAuth\Guard::class)->user($request);
   });
}

In config/auth.php set your api guard driver to firebase and the model to LaravelFirebaseAuth\User::class

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'firebase',
            'provider' => 'firebase',
        ],
    ],
    
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],

        'firebase' => [
            'driver' => 'firebase',
            'model' => \sdwru\LaravelFirebaseAuth\User::class,
        ],
],

Add authentication to api routes in routes/api.php.

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
    //return true;
});

Route::middleware('auth:api')->apiResource('some_endpoint', 'API\SomeEndpointController');

Example: Retrieve uid (For method #2 only) from API UserController

<?php
namespace App\Http\Controllers\API;

use Illuminate\Http\Request;
use Illuminate\Contracts\Auth\Guard;

class UserController extends Controller
{
    public function foo(Request $request, Guard $guard)
    {
        
        // Retrieve Firebase uid from id token via request
        $user = $request->user();
        $uid = $user->getAuthIdentifier();
        
        // Or, do the same thing using Laravel auth guard instead
        $user = $guard->user();
        $uid = $user->getAuthIdentifier();
        
        
        // Do something with the request for this user
    }
}

Example: Check if logged in and retrieve firebase user object and uid (For method #2 only) from almost anywhere inside Laravel

use Illuminate\Support\Facades\Auth;

class SomeClass
{
  public function bar()
  {
     //Check if logged in and retrieve user object and uid using Laravel Auth Facade
     $isLoggedIn = Auth::guard('api')->check();
     $userObject = Auth::guard('api')->user();
     $uid = Auth::guard('api')->id();
     
     //Alternatively, use Laravel auth() helper
     $isLoggedIn = auth('api')->check();
     $userObject = auth('api')->user();
     $uid = auth('api')->id();
     
  }
}

Role Middleware

To use this optional feature add the following to app/Http/Kernel.php.

protected $routeMiddleware = [

...
...

'role' => \sdwru\LaravelFirebaseAuth\Middleware\Role::class,

];

Add role to user example

Please note, the client needs to be issued a new token for the new role to take effect. This can happen in one of 3 ways according to the documentation. The user signs in or re-authenticates, the user session gets it's ID token refreshed after an older token expires, and ID token is force refreshed by calling currentUser.getIdToken(true) on the client end in Javascript/Vue/React etc.

<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use \Kreait\Firebase\Auth;

class UserController extends Controller
{
  public $auth;
  
  public function __construct(Auth $auth)
  {
      $this->auth = $auth;
  }
   
  public function index(Request $request)
  {
      $users = $this->auth->listUsers($defaultMaxResults = 1000, $defaultBatchSize = 1000);
 
      foreach ($users as $k => $v) {
          $response[$k] = $v;
      }
      echo json_encode($response);
   }
   
   public function update(Request $request, $uid)
   {   
       $this->validate($request, [
           'role' => 'present|string|max:20',
       ]);
       
       $customAttributes = [
         'role' => $request->role,
       ];
       
       $updatedUser = $this->auth->setCustomUserAttributes($uid, $customAttributes);
       
       
       
       return $this->auth->getUser($uid);
   }
}
Routes

After assigning roles on the firebase end, add them to routes/api.php.

// Allow any authenticated user
Route::middleware('auth:api')->apiResource('users', 'API\UserController');

// Only allow users with admin and foo roles
Route::middleware('auth:api', 'role:admin, foo')->apiResource('users', 'API\FooController');

// Allow users with admin role only
Route::middleware('auth:api', 'role:admin')->apiResource('users', 'API\AdminController');
Role references

The property where we assign roles is referred to as custom claims in the following documentation.

https://firebase.google.com/docs/auth/admin/custom-claims

https://firebase.google.com/docs/firestore/solutions/role-based-access

https://firebase-php.readthedocs.io/en/5.x/user-management.html#custom-user-claims

https://www.toptal.com/firebase/role-based-firebase-authentication

Support

Feel free to open issues and provide feedback.