29

I'm using Laravel passport for API authentication, it works perfectly when I use it with one DB, but gives 401 when using multiple databases,

What I'm doing:

  • I have a multi-tenant DB, master DB have users, roles and all OAuth tables.
  • When I create a user with admin role it will create new DB with admin name, it will create sub DB with users, roles and all OAuth table. oauth_clients of sub DB will copy Password Grant Token and Personal Access Token from master DB and insert in sub DB, and also insert client_id in oauth_personal_access_clients.
  • I'm doing all the procedures which passport:install command does. (If I'm not missing something).

  • When I login with credentials from master DB it works perfectly, the real problem starts when I login with credentials from sub-database, I can get sub DB from a param client_code which I input with email,password while login.

  • It allows me login from sub DB but I get 401 Unauthenticated error, get Access token while login and I pass Authentication Header with Bearer on every request after login from Angular front.

  • Don't know what I'm missing here.

DBConnection Middleware

DBConnection middleware sets connection on every request after login,

public function handle($request, Closure $next)
    {
        if ( $request->method() != 'OPTIONS' ) {            
            $this->access_code = $request->header('access-code'); 
            if ( $this->access_code != '' && $this->access_code != 'sa'  ) {
                app('App\Http\Controllers\Controller')->setDB(AppHelper::DB_PREFIX.$this->access_code);
            } else {
                app('App\Http\Controllers\Controller')->setDB(AppHelper::DB_DEFAULT);
            }
        }
        return $next($request);
    }

DBConnection sets default DB in database.php dynamically, for that, I'm calling setDB method created on Controller.php

setDB Controller.php

public function setDB($database='') {
      $config = app()->make('config');
      $connections = $config->get('database.connections');
      $default_connection = $connections[$config->get('database.default')];
      $new_connection = $default_connection;
      $new_connection['database'] = $database;
      $config->set('database.connections.'.$database, $new_connection);
      $config->set('database.default', $database);
  }

Is it possible to use passport with 2 different DB for same code?

Laravel 5.4 Passport 4.0 Angular 4.4 in front-end

Gufran Hasan
  • 8,910
  • 7
  • 38
  • 51
Nikhil Radadiya
  • 1,995
  • 2
  • 20
  • 43
  • How do you switch between databases and what is inside your header exactly? It sounds like an issue with one of these. – tprj29 Dec 01 '17 at 14:46
  • @tprj29 I created a middleware which sets db config, I am passing a db name in route params, I don't think its problem with that as login works perfectly, but problem arise after login, it starts giving 401 error, using Angular 4 on front – Nikhil Radadiya Dec 01 '17 at 14:54
  • This really sound like a header issue, because the header `Authentication` is only needed after login. And since login and getting the bearer token works perfectly according to you. Can you show us what value your header `Authentication` has. – tprj29 Dec 01 '17 at 15:00
  • @tprj29 Authorization is same as passed from login – Nikhil Radadiya Dec 01 '17 at 15:23
  • code would help because it is hard to see where it goes wrong without anny codesnips – tprj29 Dec 01 '17 at 15:42
  • @tprj29 Code added to question, can you please check? – Nikhil Radadiya Dec 04 '17 at 06:06
  • try adding a search_path query see my answer for details. – tprj29 Dec 05 '17 at 09:51
  • Laravel passport is only working with User as a provider. Even if you fetch token by adding above changes with PasswordGrant & UserRepository, while you go for API call for Post and get requests are not working with changed passport provider other than User. Better you create multi auth with session driver if must needed as Vendors and Customers. let 'User' model only for passport whose table columns supports admin, API, vendor, etc. Repo here https://github.com/storesbuzz/laravel-multiAuth – Anand Pandey Dec 08 '17 at 10:16
  • https://github.com/jsdecena/laravel-passport-mutiauth – Anand Pandey Dec 08 '17 at 10:17
  • Perhaps you need use an User that extends from Authenticatable and use traits HasApiTokens. Also, it's probably you need develop your own login authentication. – David L Mar 02 '18 at 19:02
  • This issue could be the order of the middleware, check to make sure the DBConnectionMiddleware is before the passport auth middleware – Mike Harrison Jan 08 '19 at 12:38
  • If you use Auth::attempt works fine? – Fabio William Conceição May 29 '19 at 19:01

2 Answers2

1

To answer your question: Yes you can!

In our middleware we do some like this:

config([
  'database.connections.tenant.schema' => $tenant
]);

DB::connection('tenant')->statement("SET search_path = $tenant");

It really sounds to me that your search_path is not set up in properly. This would explain why you get a 401. Because Laravel Passport is searching in the wrong database in which it can't find the right token in your users table.

From PostgreSQL docs (https://www.postgresql.org/docs/9.1/static/runtime-config-client.html):

search_path (string)

This variable specifies the order in which schemas are searched when an object (table, data type, function, etc.) is referenced by a simple name with no schema specified. When there are objects of identical names in different schemas, the one found first in the search path is used. An object that is not in any of the schemas in the search path can only be referenced by specifying its containing schema with a qualified (dotted) name.

Community
  • 1
  • 1
tprj29
  • 234
  • 1
  • 11
0

This is CORS issue. OPTIONS request does not deliver Authorization headers.

If the origin is different from the host, browser going to send OPTIONS before any other request.

Laravel going to answer with the status 401 if CORS middleware is not set up.

So with RESTful architecture, if the client app host is different from the API's host you have to use CORS middleware.

You may use this one: barryvdh/laravel-cors

$ composer require barryvdh/laravel-cors

Example:

App\Http\Kernel.php

protected $routeMiddleware = [
    ...
    'auth.cors' => \Barryvdh\Cors\HandleCors::class,
    ...
];

web.php

Route::group([
    'prefix' => 'api',
    'middleware' => [
        'auth.cors'
    ]
], function () {
    Route::post('user/authenticate', 'UserController@authenticate');
});

If CORS middleware works properly a browser shall receive status 200 on the OPTIONS request and fire the initial request with a payload.

Emma Paulowicz
  • 316
  • 4
  • 4