Auth0 Laravel SDK Fix

Series: Laravel
Published: Jun 07, 2022
Updated: Jun 08, 2022

Background

I was working on upgrading my company's Laravel application from version 7 to 8 earlier today, and ran into a days worth of issues. While I used Laravel Shift to make the process easier, it wasn't a surprise I encountered the ever unavoidable swath of dependancy issues that required some serious attention. After taking care of the dependancy issues, and being forced to upgrade our PHP implementation of Auth0, `auth0-login`, from version 5.0 to 6.5 I was finally able to launch the application. Only, I wasn't able to login, because of the following error message getting logged:

CustomAuth0UserRepository::getUserByDecodedJWT($jwt) must be compatible with Auth0UserRepository::getUserByDecodedJWT(array $decodedJwt)

The above message was being thrown because of a change in the schema of the Auth0 specific contract we were implementing to introduce custom user processing functionality. The old contract (located in vendor/...) looked like this:

<?php

declare(strict_types=0);

namespace Auth0\Login\Contract;

use Illuminate\Contracts\Auth\Authenticatable;

interface Auth0UserRepository
{
    /**
     * @param mixed $decodedJwt with the data provided in the JWT
     *
     * @return mixed
     */
    public function getUserByDecodedJWT($decodedJwt);

    /**
     * @param mixed $userInfo representing the user profile and user accessToken
     *
     * @return mixed
     */
    public function getUserByUserInfo($userInfo);

    /**
     * @param mixed $identifier the user id
     *
     * @return mixed|null
     */
    public function getUserByIdentifier($identifier);
}

Whereas the new contract looks like this:

<?php

declare(strict_types=1);

namespace Auth0\Login\Contract;

use Illuminate\Contracts\Auth\Authenticatable;

interface Auth0UserRepository
{
    /**
     * @param array $decodedJwt with the data provided in the JWT
     *
     * @return Authenticatable
     */
    public function getUserByDecodedJWT(array $decodedJwt): Authenticatable;

    /**
     * @param array $userInfo representing the user profile and user accessToken
     *
     * @return Authenticatable
     */
    public function getUserByUserInfo(array $userInfo): Authenticatable;

    /**
     * @param string|int|null $identifier the user id
     *
     * @return Authenticatable|null
     */
    public function getUserByIdentifier($identifier): ?Authenticatable;
}

Notice the newly introduced requirements for both the parameters of the functions as well as their expected outputs. I did not create the original implementation of this contract in our codebase, so I can't speak to the intent behind it, but the reason the update of `auth0-login` 5.X to 6.5 broke our code, was because we weren't returning an instance of `Authenticatable` nor were we passing in arrays of web tokens (we passed in the tokens directly). You can revise your code to follow the above requirements, which is most likely the best solution; however, sometimes your implementation and the new requirements may not be compatible (which was the case for our code).

Try Beforehand

If you're experiencing this issue, there's actually a more common solution that seems to have worked for most people. It did not work for me, but I figured I would post it here to save you time. The solution can be found here. Or, to save you even more time, here's what you'd need to change in your `config/auth.php` file:

'providers' => 
    'users' => 
        'driver' => 'eloquent', //'auth0',
        'model' => App\User::class,
    ],
],

Simply switch your driver from `auth0` to `eloquent`. This didn't work for me, but it's a quick change, so you should try it before moving on. If it doesn't work, keep reading.

The Fix

The fix for the above issue could be considered "hacky" in the sense that we simply overwrite the new contract file with the old one as part of our deployment process. Fortunatley, composer.json files have a way to do this built-in (it's almost like they expected these kinds of issues). To do so, we simply had to make the following changes.

  1. In your Laravel application's `app` folder, add a new folder called `overrides`, with an `auth0` folder inside of it. So from the root of your projects's directory, it would be `./app/overrides/auth0`.
  2. Copy and paste the code from the original contract file I have pasted above, the one without the parameter and output requirements, and save it to a file called `Auth0UserRepository.php` inside of your newly created auth0 folder.
  3. Open up your `composer.json` file, and add the following:
{
    // ... other composer settings ...
    "exclude-from-classmap": [
        // If you're on Windows, switch slashes with opposite direction slashes here:
        "vendor/auth0/login/src/Auth0/Login/Contract/Auth0UserRepository.php"
    ],
    "psr-4": {
        "App/": "app/",
        "Auth0/": "app/overrides/auth0"
    }
}

The above settings tell composer to ignore the `Auth0UserRepository.php` file from the new version of the SDK when you run composer updates. The `psr-4` section then tells it where to find your replacement file and the rest is taken care of. The only detail here to be weary of is whether you're on Windows or Mac/Linux. Just be aware that file paths are written/treated differently between the two.

That's it! Hopefully, you've found this solution helpful.