4
votes

I am attempting to pass certain functions in my Controller through the auth middleware. However, the routes associated with these functions require a subdomain in the URL to work. The functions seem to work when they are not passed through middleware or when they are passed through custom made middleware, but the 'auth' middleware throws the error:

UrlGenerationException in UrlGenerationException.php line 17: Missing required parameters for [Route: login] [URI: login].

This is similar to the error that is thrown when I try to run a function without the $account variable in the parameter list (since it is expecting a subdomain with the route).

Upon looking deeper into the Authenticate class that is being called by the 'auth' middleware, I have found that the line that is causing the error is in public function authenticate(array $guards) as shown below.

protected function authenticate(array $guards)

{
    if (empty($guards)) {
        /** ANYTHING BEFORE THIS RETURN STATEMENT RUNS WITH NO ERRORS **/
        return $this->auth->authenticate();
    }

    foreach ($guards as $guard) {
        if ($this->auth->guard($guard)->check()) {
            return $this->auth->shouldUse($guard);
        }
    }

    throw new AuthenticationException('Unauthenticated.', $guards);
}

Here is some of my code for reference (only relevant snippets of the code will be included):

Routes Files

routes\web.php

Route::group(['domain' => '{account}.'.env('APP_URL')], function () {
  include('appRoutes/bcm.php');
});

routes\appRoutes\bcm.php

/********************************************/
/*********STATIC PAGE ROUTES******************/
/********************************************/

Route::get('/boards-commissions', 'BCM\PagesController@home');


/********************************************/
/*********BOARD ROUTES***********************/
/********************************************/

Route::get('/boards-commissions/boards', 'BCM\BoardsController@index');

Route::get('/boards-commissions/boards/archive/index', 
'BCM\BoardsController@archiveIndex');

Controllers

app\Http\Controllers\BCM\BoardsController.php

namespace App\Http\Controllers\BCM;

use Auth;
use Session;
use Validator;

use Illuminate\Http\Request;
use Illuminate\Foundation\Validation\ValidatesRequests;

//Models
use App\Models\BCM\Board;

class BoardsController extends Controller
{
    public function __construct()
    {
        $this->middleware('bcm_app_access');
        $this->middleware('auth', ['except' => array('index', 'show')]);
        $this->middleware('bcm_admin', ['only' => array('edit', 'update', 
'create', 'store', 'archive', 'showArchive', 'showArchivedBoard', 'restore')]);
    }

    public function index($account, Request $request) {
        $boards = Board::all();
        $user = $request->user();
        return view('bcm.boards.index')->with('boards', $boards)->with('user', $user);
    }

    public function archiveIndex($account, Request $request) {
        $boards = Board::all();
        $user = $request->user();
        return view('bcm.boards.archiveList')->with('boards', $boards)->with('user', $user);
    }
}

It seems to be only the auth middleware that is not working so far. The index function runs just fine (since it is being excluded from the auth middleware in the construct of the controller) even though it is being passed through the bcm_app_access middleware. But archiveIndex will throw the UrlGenerationException error mentioned above when it passes through the auth middleware.

I have a feeling that the $account parameter needs to be added or included somewhere so that the auth middleware knows that it needs to reference it but I'm not sure where to add that in and I don't know what other files are being referenced when $this->auth->authenticate(); is run so I don't know where to add that in. Any thoughts?

3
Having the EXACT same issue. I've even tried using forgetParameter. Did you figure it out?kjdion84
I still have not been able to figure out how to make the default auth middleware work right. The work around that I've settled on for now involves making a new middleware to be used in place of the auth middleware to do the same function (ie check to see if a valid user is logged in or not.) I used Auth::check(); among some other code. I would still like to know how to adapt the default auth middleware to the subdomains if possible.Jake Killpack
I have the same problem! The standard auth middleware doesn't work with subdomains! I get endless loops... @JakeKillpack Can you show us how you created your own middleware for this?mesqueeb

3 Answers

2
votes

a rough laravel7 guide:

the wildcard parameter {brand} of your subdomain:

Route::domain( '{brand}.' . parse_url(config('app.url'), PHP_URL_HOST) )->group(function () {

    // login page
    Route::get('/login', 'Auth\LoginController@showLoginForm')->name('login')->middleware('guest');

    Route::middleware('auth')->group(function () {
        Route::get('/', 'BrandController@showDashboard')->name('dashboard');
        
        ...
    }
});

will be accessible in the $request object, so in:

// app\Http\Middleware\Authenticate.php

protected function redirectTo($request)
{
    if (! $request->expectsJson()) {
        return route('login', $request->brand);
    }
}

just pass your wildcard as parameter to the route function

1
votes

In the original question I wanted to know how to get the 'auth' middleware to work with subdomains. I still haven't figured out to do that but I have established a functional work-around by creating a basic custom middleware. The point of the authenticate middleware was to check whether a user of the site was logged in or running as a guest. To mimic this functionality, I created a middleware by running

php artisan make:middleware SubdomainAuth

in command line. Then I configured the auto-generated middleware file to look as follows:

<?php
namespace App\Http\Middleware;
use Closure;
use Auth;
class SubdomainAuth
{
    /**
     * Handle an incoming request to make sure that a valid user is logged in.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next) {
        $user = Auth::user();
        if ($user == null) {
          return redirect('/login');
        }
        return $next($request);
    }
}

Lastly, I configured my Kernel to recognize the new middleware by adding the line

'sub_auth' => \App\Http\Middleware\SubdomainAuth::class,

to app/Http/Kernel.php. From there I was able to replace the auth middleware in my controllers with the sub_auth middleware that I created. My example from the question above now looks like this:

namespace App\Http\Controllers\BCM;

use Auth;
use Session;
use Validator;

use Illuminate\Http\Request;
use Illuminate\Foundation\Validation\ValidatesRequests;

//Models
use App\Models\BCM\Board;

class BoardsController extends Controller
{
    public function __construct()
    {
        $this->middleware('bcm_app_access');
        $this->middleware('sub_auth', ['except' => array('index', 'show')]);
        $this->middleware('bcm_admin', ['only' => array('edit', 'update', 
'create', 'store', 'archive', 'showArchive', 'showArchivedBoard', 'restore')]);
    }

    public function index($account, Request $request) {
        $boards = Board::all();
        $user = $request->user();
        return view('bcm.boards.index')->with('boards', $boards)->with('user', $user);
    }

    public function archiveIndex($account, Request $request) {
        $boards = Board::all();
        $user = $request->user();
        return view('bcm.boards.archiveList')->with('boards', $boards)->with('user', $user);
    }
}

This work-around successfully screens those who are not logged in and redirects them to the login page. However, I am not familiar enough with how the Authenticate middleware works to know if there is any added security that my custom class is missing in the SubdomainAuth class I created.

0
votes

I've faced the same problem. Here's my solution:

Laravel's Auth Middleware handles it with its default route('login') which is in \Illuminate\Foundation\Exceptions\Handler.php

protected function unauthenticated($request, AuthenticationException $exception)
{
    return $request->expectsJson()
                ? response()->json(['message' => $exception->getMessage()], 401)
                : redirect()->guest(route('login'));
}

You could modify here to change route('login', 'subdomain') to make it work. But personally, I'd like to leave the framework untouched. I've modified the Exceptions Handler instead to overwrite the "unauthenticated" function.

Add a new function in \app\Exceptions\Handler.php

protected function unauthenticated($request, AuthenticationException $exception)
{
    $subdomain = explode("//",explode('.', $request->url())[0])[1];
    return $request->expectsJson()
                ? response()->json(['message' => $exception->getMessage()], 401)
                : redirect()->guest(route('login',$subdomain));
}

I've used $request to obtain the url and extract it. I think if you could go deeper and look into userResolver, you'd be able to extract the subdomain variable that's defined in your routes/web.php but I'm happy with this now.