Using dingo/api along with lucadegasperi/oauth2-server-laravel. Authenticating a user is fine and I get an access token back but any time I make another request I get the following error:
call_user_func() expects parameter 1 to be a valid callback, no array or string given
I'm using the Service Provider option listed in the dingo/api docs and it's definitely setting the user resolver (I'd var_dump'd the resolver in the setUserResolver method).
My OauthServiceProvider is below.
<?php namespace App\Providers;
use Dingo\Api\Auth\Auth;
use Dingo\Api\Auth\Provider\OAuth2;
use App\User\User;
use Illuminate\Support\ServiceProvider;
class OAuthServiceProvider extends ServiceProvider
{
public function boot()
{
$this->app[Auth::class]->extend('oauth', function ($app) {
$provider = new OAuth2($app['oauth2-server.authorizer']->getChecker());
$provider->setUserResolver(function ($id) {
return User::first();
// Logic to return a user by their ID.
});
$provider->setClientResolver(function ($id) {
// Logic to return a client by their ID.
});
return $provider;
});
}
public function register()
{
//
}
}
So turns out I was completely off in where I was looking. In config/api.php, in my auth settings I had
'oauth2' => Dingo\Api\Auth\Provider\OAuth2::class,
Should have just been
'oauth' => Dingo\Api\Auth\Provider\OAuth2::class,
Related
I am having some problems with Laravel 6 policies. I get 403 unauthorized all the time even though it should be a non-authenticated request.
Files:
api.php
Route::prefix('v2')
->group(function () {
Route::prefix('agencies')->group(function () {
Route::post('/', 'Api\AgencyController#store');
});
}
AgencyController.php
<?php
namespace App\Http\Controllers\Api;
use App\Entities\Agency;
class AgencyController extends Controller {
public function store(AgencyRequest $request)
{
$this->authorize('create', Agency::class);
// Code that is never executed
}
}
AgencyPolicy.php
class AgencyPolicy
{
public function create(User $user)
{
\Log::info('hello?'); // This Log is never executed
return true;
}
}
AuthServiceProvider.php
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
\App\Entities\Agency::class => \App\Policies\AgencyPolicy::class,
// Other policies
];
public function boot()
{
$this->registerPolicies();
Gate::before(function ($user) {
if ($user->hasRole(SuperAdmin::ROLE_NAME)) {
return true;
}
});
Passport::routes(null, ['prefix' => 'api/oauth']);
}
}
My code is identical to the documentation but nonetheless I keep getting 403 unauthorized, and for the life of me I cannot understand what is going on. All help will be appreciated.
As lagbox thankfully replied, the answer is in the documentation, that states:
By default, all gates and policies automatically return false if the incoming HTTP request was not initiated by an authenticated user. However, you may allow these authorization checks to pass through to your gates and policies by declaring an "optional" type-hint or supplying a null default value for the user argument definition:
Thus, my problem would be solved by using ?User in AgencyPolicy.php:
class AgencyPolicy
{
public function create(?User $user)
{
return true;
}
}
This solves the problem.
I'm new to Laravel (5.5) and want to create an application with a custom authentication mechanism:
If the user is not logged in, he/she should be redirected to /login.
I want to use the session-guard.
I have written and registered custom classes
LdapUser extends Illuminate\Auth\GenericUser and
LdapUserProvider implements Illuminate\Contracts\Auth\UserProvider. Actually both are dummy implementations at the moment that return generic objects. Later, I want to check, if the user exists in my external ldap-directory and if the credentials are valid. Registration, internal user management or password reset are not intended. I just want to use the user's name and some properties in my blade templates.
I modified config/app.php and config/auth.php, so that this provider can be used.
Now, I wonder how to proceed in order to actually use the authentication and access the LdapUser in my blade templates. I tried {{ Auth::user()->name }}, which led to this error message: "Trying to get property of non-object."
How to I tell my app to check, if a valid session exists and otherwise redirect to the login page?
How can I access the user's properties inside the blade templates?
This is my route:
Route::group(['middleware' => 'auth'], function () {
Route::get('/helloworld', function () {return 'Hello World!';});
});
Route::get('/login', 'LdapLoginController#showLoginForm')->name('showLoginForm');
/helloworld now redirects to /login where I see the login-form, which is going to be handled by the LdapLoginController:
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LdapLoginController extends Controller
{
use AuthenticatesUsers;
}
I would now expect that the session guard uses the LdapUserProvider to check the credentials:
namespace App\Providers;
use App\LdapUser;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
class LdapUserProvider implements UserProvider
{
public function retrieveById($id)
{
return $this->dummyUser();
}
public function retrieveByCredentials(array $credentials)
{
return $this->dummyUser();
}
public function validateCredentials(Authenticatable $user, array $credentials)
{
return true;
}
public function retrieveByToken($identifier, $token)
{
return new \Exception('not implemented');
}
public function updateRememberToken(Authenticatable $user, $token)
{
return new \Exception('not implemented');
}
protected function dummyUser()
{
$attributes = array(
'id' => 123,
'username' => 'chuckles',
'password' => \Hash::make('SuperSecret'),
'name' => 'Dummy User',
);
return new LdapUser($attributes);
}
}
Unfortunately, after submitting the login form, I'm always redirected back to /login, no matter what I enter :-(
Any help is greatly appreciated!
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.
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).
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();
//
}]);
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
});
}
I've been reading the documentation up and down now, still not sure what I'm doing wrong. In my opinion the documentation is very difficult to understand for a beginner.
Anyway, I'm trying to make something akin to the Auth::user() method, where it returns additional data about a logged in user that I will be needing for this application.
I have this helper class here:
namespace App\Helpers;
use Auth;
use Illuminate\Http\Request;
use App\Models\Grouping\User;
use App\Models\Grouping\Client;
use App\Models\Grouping\Rank;
class ClientUser {
public function __construct($request) {
$this->request = $request;
}
public function client() {
return Client::find($this->request->session()->get('client_id'));
}
public function auth() {
if (Auth::check()) {
// Get the client
$client = $this->client();
// Get the client's user
$user = $client->users()->find(Auth::user()['id']);
// Get the rank of the logged in user
$rank = Rank::find($user->pivot->rank_id);
return [
'user' => $user,
'rank' => $rank,
'client' => $client
];
}
return null;
}
}
This is responsible for doing what I described, returning additional data that I can't get through Auth::user(). Now I'm trying to register this class in the AuthServiceProvider
public function register()
{
// Register client auth
$request = $this->app->request;
$this->app->singleton(ClientUser::class, function ($app) {
return new ClientUser($request);
});
}
Now what I don't understand is how I'm supposed to make this globally accessible throughout my app like Auth::user() is.
The problem with just making "importing" it is that it needs the request object, which is why I'm passing it through the service container.
Now here's where I'm stuck. I'm not able to access app in my controller or anywhere, and I can't define a Facade because a Facade expects you to return a string of the bound service that it should "alias?"
Change your service provider like this :
$this->app->bind('client.user', function ($app) {
return new ClientUser($app->request);
});
Create another class extended from Illuminate\Support\Facades\Facade.
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class ClientUserFacade extends Facade {
public static function getFacadeAccessor(){
return "client.user";
}
}
Add 'ClientUser => ClientUserFacade::class in alias key of app.php
Is there a package for laravel that ads a unique identifier to each request in order to use it also for logs?
For example: I would know that request-id as12121-1212s-121 had an error and I could look into logs for any errors.
That request-id would be seen in the UI and I could debug when getting a printscreen with the error from the client
You can use $request->fingerprint()
This prints a uniqueid from your request and you can track it
public function fingerprint()
{
if (! $route = $this->route()) {
throw new RuntimeException('Unable to generate fingerprint. Route unavailable.');
}
return sha1(implode('|', array_merge(
$route->methods(),
[$route->getDomain(), $route->uri(), $this->ip()]
)));
}
If you need a unique id per request you can assign a uuid to the request via global middleware.
For example:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
class RequestUniqueId
{
public function handle(Request $request, Closure $next)
{
$uuid = (string) Str::uuid();
$request->headers->set('X-Request-ID', $uuid);
return $next($request);
}
}
This package might help
Laravel Request Logger