title: Decode JWT and create authenticated User in Laravel microservice, merging with local user data
body: <p><strong>Background</strong></p>
  
  <p>I have a microservice setup the flow is:</p>
  
  <pre><code>client &gt; api gateway &gt; auth server &gt; api gateway &gt; microservice
  </code></pre>
  
  <ol>
  <li>The client has a 'external' JWT from Laravel passport</li>
  <li>Client sends request to the api gateway with the 'external' JWT</li>
  <li>The api gateway sends a request to the auth server (Laravel passport) with the 'external' JWT</li>
  <li>The auth server verifies the user is still active and returns a new 'internal' JWT to the api gateway containing the users profile, groups etc</li>
  <li>The api gateway forwards the request with this new 'internal' JWT to the microservice </li>
  <li><strong>(all fine up to this point)</strong></li>
  <li>The Microservice verifies the 'internal' JWT using the auth servers public key</li>
  <li>The microservice decodes the 'internal' JWT and creates a user object from the profile contained within</li>
  <li>If the microservice has a local users table (e.g. for microservice specific user data), merge the local data with the JWT data</li>
  </ol>
  
  <hr>
  
  <p><strong>Microservice Authentication</strong></p>
  
  <p>I have created a JwtGuard that can decode the JWT and create a user using GenericUser:</p>
  
  <p>auth.php</p>
  
  <pre><code>'guards' =&gt; [
          'web' =&gt; [
              'driver' =&gt; 'session',
              'provider' =&gt; 'users',
          ],
  
          'api' =&gt; [
              'driver' =&gt; 'jwt',
              'provider' =&gt; 'users',
          ],
      ],
  'providers' =&gt; [
      'users' =&gt; [
          'driver' =&gt; 'eloquent',
          'model' =&gt; App\User::class,
      ],
  ],
  </code></pre>
  
  <p>AuthServiceProvider.php</p>
  
  <pre><code>public function boot()
      {
          $this-&gt;registerPolicies();
  
          Auth::extend('jwt', function ($app) {
              return new JwtGuard($app['request']);
          });
      }
  </code></pre>
  
  <p>JwtGuard.php</p>
  
  <pre><code>&lt;?php
  namespace App\Services\Auth;
  
  use Illuminate\Auth\GenericUser;
  use Illuminate\Auth\GuardHelpers;
  use Illuminate\Contracts\Auth\Authenticatable;
  use Illuminate\Contracts\Auth\Guard;
  
  use \Firebase\JWT\JWT;
  use Illuminate\Http\Request;
  
  class JwtGuard implements Guard {
  
      use GuardHelpers;
  
      /**
       * @var Request
       */
      private $request;
  
      public function __construct(Request $request)
      {
          $this-&gt;request = $request;
      }
  
      /**
       * Get the currently authenticated user.
       *
       * @return \Illuminate\Contracts\Auth\Authenticatable|null
       */
      public function user()
      {
          if (!is_null($this-&gt;user)) {
              return $this-&gt;user;
          }
  
          if(!$jwt = $this-&gt;getJwt()) {
              return null;
          }
  
          return $this-&gt;decode($jwt);
      }
  
      /**
       * Validate a user's credentials.
       *
       * @param  array $credentials
       * @return bool
       */
      public function validate(array $credentials = [])
      {
          if(!$jwt = $this-&gt;getJwt()) {
              return false;
          }
  
          return !is_null($this-&gt;decode($jwt))?true:false;
      }
  
      /**
       * Decode JWT and return user
       *
       * @return mixed|null
       */
      private function decode($jwt)
      {
          $publicKey = file_get_contents(storage_path('oauth-public.key'));
  
          try {
              $res = JWT::decode($jwt, $publicKey, array('RS256'));
              return $this-&gt;user = new GenericUser(json_decode(json_encode($res-&gt;user), true));
          } catch (\Exception $e) {
              return null;
          }
      }
  
      private function hasAuthHeader()
      {
          return $this-&gt;request-&gt;header('Authorization')?true:false;
      }
  
      private function getJwt()
      {
          if(!$this-&gt;hasAuthHeader()){
              return null;
          }
  
          preg_match('/Bearer\s((.*)\.(.*)\.(.*))/', $this-&gt;request-&gt;header('Authorization'), $jwt);
  
          return $jwt[1]?$jwt[1]:null;
      }
  
  }
  </code></pre>
  
  <hr>
  
  <p><strong>The problem</strong></p>
  
  <p>This works ok(ish), except that:</p>
  
  <ul>
  <li>I can't use authorization policies properly as GenericUser doesn't have the can() method</li>
  <li>There is no easy way to merge with a local user object</li>
  </ul>
  
  <hr>
  
  <p><strong>What I have so far</strong></p>
  
  <p>I have tried the following to merge the local user data with the JWT profile:</p>
  
  <pre><code>private function decode($jwt)
      {
          $publicKey = file_get_contents(storage_path('oauth-public.key'));
  
          try {
              $res = JWT::decode($jwt, $publicKey, array('RS256'));
              $this-&gt;user = new GenericUser(json_decode(json_encode($res-&gt;user), true));
              $this-&gt;user-&gt;localUser = \App\User::where('user_id', $this-&gt;user-&gt;id)-&gt;first();
              return $this-&gt;user;
          } catch (\Exception $e) {
              return null;
          }
      }
  </code></pre>
  
  <p>but this still leaves GenericUser not having the can() function. </p>
  
  <hr>
  
  <p><strong>Help...please!</strong></p>
  
  <p>I can't help feel there is a better (proper?) way to achieve this using 'User' instead of 'GenericUser' which will allow all the Authentication/Authorization features in Laravel to work properly, and to merge the data easily.</p>