3

I'm attempting to rebind what $request->user() returns, and having poked through the built in authentication code, I found a service using app->rebinding to request->setUserResolver is how it's done? I tried it myself, with no luck. I created a service (well, coopted AuthServiceProvider, and changed the register to:

public function register()
{
    $this->app->rebinding('request', function ($app, $request) {
        $request->setUserResolver(function () use ($app) {
            $token = $this->request->bearerToken();
            dd($token);
            // error_log($token);
            return array('user' => 1);
        });
    });
}

Ignoring the dd, which is there to test, how can I find where I'm going wrong? I even found a SO answer that seems to indicate this is the way to go but nothing gets dumped, nothing gets logged (when error log isn't commented out) and dumping $request->user() in my controller just returns null.

I know I can use the built in auth/guard setup, but I figured since I'm not using most of what the auth/guard setup has, why not try to learn and set it up myself? Of course, so far I've gotten nowhere. I'm going to fall back to using the built-in stuff, but I'd like to learn and improve.

As I realized it may make a difference, I'm running Lumen 5.4.

halfer
  • 19,824
  • 17
  • 99
  • 186
Rohit
  • 3,018
  • 2
  • 29
  • 58
  • Did you add your new service provider to `config/app.php` in `'providers'`? – ljubadr Oct 25 '17 at 15:07
  • I actually just deleted the content of the existing `AuthServiceProvider` and wrote that into the `register()`. Didn't think that would be an issue? – Rohit Oct 25 '17 at 15:20
  • 1
    Oh, I thought you copied **AuthserviceProvider** into new provider... Not an issue. also check [How to bind user object to request in a middleware](https://stackoverflow.com/questions/39414776/how-to-bind-user-object-to-request-in-a-middleware). Could point you to right direction – ljubadr Oct 25 '17 at 15:33
  • Thanks. I ran into that earlier, and I guess I should test that out myself. As mentioned, hoping to figure out what I'm doing wrong as much for learning as anything else. – Rohit Oct 25 '17 at 15:35
  • Can you provide minimum code git repo to debug the issue? – Tarun Lalwani Oct 30 '17 at 16:10
  • Sure, though right now it's just a default Lumen 5.4 install with the AuthServiceProvider `register` function replaced with above and the `boot` function cleared. – Rohit Oct 30 '17 at 16:15

3 Answers3

0

In Lumen, your App\Providers\AuthServiceProvider class comes by default with

public function boot()
{
    // Here you may define how you wish users to be authenticated for your Lumen
    // application. The callback which receives the incoming request instance
    // should return either a User instance or null. You're free to obtain
    // the User instance via an API token or any other method necessary.
    $this->app['auth']->viaRequest('api', function ($request) {
        if ($request->input('api_token')) {
            return User::where('api_token', $request->input('api_token'))->first();
        }
    });
}

This is the place to define the user resolution logic. The rebinding you were registering in the register method was being supeseded by this one.

Just uncomment the $app->register(App\Providers\AuthServiceProvider::class); line in bootstrap/app.php to register your provider; don't modify the code in the vendor folder (if I understood correctly you were doing that).


Update

I now see what you mean, although I'm not sure it is really too much "load" for the auth/guard method. However, in the interest of creating a minimal implementation, I think the solution would be overriding the prepareRequest method of the Application class.

In bootstrap/app.php replace

$app = new Laravel\Lumen\Application(
    realpath(__DIR__.'/../')
);

with

$app = new class (realpath(__DIR__.'/../')) extends Laravel\Lumen\Application {
    protected function prepareRequest(\Symfony\Component\HttpFoundation\Request $request)
    {
        if (! $request instanceof Illuminate\Http\Request) {
            $request = Illuminate\Http\Request::createFromBase($request);
        }

        $request->setUserResolver(function () use ($request) {
            return $request->bearerToken();
        })->setRouteResolver(function () {
            return $this->currentRoute;
        });

        return $request;
    }
};

This way you can have the simple resolution logic for getting the bearer token (don't include the AuthServiceProvider then).

(This requires PHP 7 anonymous classes; alternatively just extend to a regular class).

alepeino
  • 9,551
  • 3
  • 28
  • 48
  • Well, `echo`s, `dd`s, `error_log`s inside the function never even trigger, so it's not even at the point of if there is a request object. It never actually gets inside the function. As I mentioned, I copied it from the built in `AuthService` schema. If I can get into the function, even if the request object isn't there, I can extract the header value I need. – Rohit Oct 30 '17 at 18:13
  • Thanks for the feedback, but as I mentioned at the end of the question, I know I can use the built in auth/guard methods, but I'm looking to learn more about how Laravel/Lumen are working under the hood. I traced the `viaRequest` back through vendor, and found that it's rebinding `setUserResolver`, so wanted to try that myself. I'm only changing things in vendor in so much to debug how it's working. The built in auth/guard does far more than I need, so I wanted to learn so I could help reduce the overall load. I'm copying code out of vendor to create my own AuthServiceProvider. – Rohit Oct 30 '17 at 20:02
  • Thanks so much for the expanded answer! I'll definitely take a deep look. You're right in that "reduce load" in this case probably doesn't mean much. I was more interested in the learning. I still would like to figure out why the built in AuthService functionality worked with a rebind that worked while mine didn't, but this is a good chunk to chew on. I'll test at earliest possibility. – Rohit Oct 30 '17 at 20:59
0

You do not need to change the register() function. Just uncomment the following lines in bootstrap/app.php file:

$app->withEloquent();
$app->register(App\Providers\AppServiceProvider::class);
$app->register(App\Providers\AuthServiceProvider::class);
$app->routeMiddleware([
    'auth' => App\Http\Middleware\Authenticate::class,
]);

And in app/Providers/AuthServiceProvider.php->boot(), it has default method to retrieve the authenticated user.

$this->app['auth']->viaRequest('api', function ($request) {
    if ($request->input('api_token')) {
        return User::where('api_token', $request->input('api_token'))->first();
    }
});

You may use an API token in the request headers or query string, a bearer token on the request, or using any other approach your application requires.

After that, you may retrieve the authenticated user like this:

use Illuminate\Http\Request;

$app->get('/post/{id}', ['middleware' => 'auth', function (Request $request, $id) {
    $user = Auth::user();

    $user = $request->user();

    //
}]);
Cong Chen
  • 2,436
  • 12
  • 21
  • Thank you, but as I mentioned in my answer, I already know I can do this through the built in Auth/Guard system; I've been through the docs and already set it up this way. I'm trying to get a deeper understanding of Laravel/Lumen, and so am trying to extrapolate how the Auth system does it, which appears to be how I'm doing it above, except my method doesn't work. – Rohit Oct 31 '17 at 11:52
0

The rebinding method will add an additional reboundCallbacks which this callback will be triggered right after the abstract is rebound. As long as your abstract is not rebound, the reboundCallbacks are not called. So, you can simply rebound your abstract, like so:

$this->app->rebinding('request', function ($app, $request) {
    $request->setUserResolver(function () use ($app) {
        $token = $this->request->bearerToken();

        dd($token);

        // do the rest
    });
});

// REBOUND HERE
$this->app->instance('request', $this->app->make('request'));

// TEST
// $this->app->make('request')->user(); // output is $token

Try uncomment the rebound line above, your dd will not called at all.

Extra

You can use refresh method (to register reboundCallbacks) combined with extend method (to rebound) for cleaner code:

public function register()
{
    parent::register();

    $this->app->refresh('request', $this, 'overrideUserResolver');

    // REBOUND HERE, JUST ANOTHER WAY TO REBOUND
    $this->app->extend('request', function ($request) { return $request; });

    // TEST
    $this->app->make('request')->user();
}

public function overrideUserResolver($request)
{
    $request->setUserResolver(function ($guard = null) use ($request) {
        $token = $request->bearerToken();

        dd($token);

        // do the rest
    });
}
flipbox
  • 83
  • 4