NKnuelle
NKnuelle

Reputation: 282

Laravel 9: User not authenticated on API routes using custom guard

I need to authenticate temporary users of a third party app. In addition, users only get a signed URL to login (there is no username/password). The temporary users get created on the fly and logged in after verifying the signed URL and some query params. Because I also have "traditional" users in my app I am using an additional database table called "clients", an additional provder 'clients' and an additional guard called 'vcs' for the authentication workflow.

The authentication workflow (user clicks on the signed URL, a new Client is created and saved to the database as well as logged in as new user) is working fine. The session is created correctly and send to the browser in the laravel_session cookie. The problem is, that all subsequent requests to my API seem to be unauthenticated.

config/auth.php:

<?php

return [

    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'vcs' => [
            'driver' => 'session',
            'provider' => 'clients',
        ],
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],

        'clients' => [
            'driver' => 'eloquent',
            'model' => App\Models\Client::class,
        ],
    ],

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],

    'password_timeout' => 10800,

];

My client model:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Foundation\Auth\User as Authenticatable;

class Client extends Authenticatable
{
    use HasFactory, HasApiTokens;

    protected $guard = "vcs";

    /**
     * The primary key associated with the table.
     *
     * @var string
     */
    protected $primaryKey = 'uuid';

    /**
     * Indicates if the model's ID is auto-incrementing.
     *
     * @var bool
     */
    public $incrementing = false;

    protected $keyType = 'string';

    /**
     * Get the route key for the model.
     *
     * @return string
     */
    public function getRouteKeyName()
    {
        return 'uuid';
    }
}

The clients get a signed URL which points to the following controller action. The action checks for a valid query parameter in the URL (simplified for this thread). After that a new Client model gets created and the new Client gets logged in using the 'vcs' guard:

<?php

namespace App\Http\Controllers\VCS;

use Illuminate\Http\Request;
use App\Models\Client;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Auth;

class AuthController extends Controller
{
    public function redirectWithCookie(Request $request)
    {
        // reduced for the sake of simplicity here
        $credential = $request->someURLParameter;
        if ($credential) {
            $client = new Client;
            $client->uuid = Str::uuid()->toString();
            $client->ip = $request->ip();
            $client->status = 'pending';
            $client->save();
            Auth::guard('vcs')->login($client, $remember = true);
            // this logs the authenticated user correctly!
            Log::info('Authenticated User: ' . Auth::guard('vcs')->user());
            $cookieValue = json_encode(array('uuid' => $client->uuid));
            $cookie = cookie('mycookie', $cookieValue);
            $redirectUrl = config('my.redirect.url');
            return redirect()->away($redirectUrl)->withCookie($cookie);
        }
        return response(['message' => 'Invalid URL', 'error' => 'url'], 422);
    }
}

routes/web.php:

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\VCS\AuthController;

Route::get('/', function () {
    return ['Laravel' => app()->version()];
});

Route::get('vcs/auth', [AuthController::class, 'redirectWithCookie'])->name('vcs.auth');

require __DIR__.'/auth.php';

routes/api.php:

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\VCS\RoomController;

Route::middleware(['auth:sanctum'])->get('/user', function (Request $request) {
    return $request->user();
})->name('profile');

Route::middleware(['auth:vcs'])->group(function () {
    Route::get('rooms', [RoomController::class, 'rooms']);
});

After the redirect I get a laravel_session as a cookie which should authenticate my subsequent requests. The problem is that I can't call any API routes with the custom guard and I am not authenticated anymore although the browser is sending my session cookie with the request. For example calling the /api/rooms GET-endpoint defined in the api.php results in a redirect to the login page.

I also see that the user is not authenticated in the auth-middleware:

<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;

class Authenticate extends Middleware
{
    /**
     * Get the path the user should be redirected to when they are not authenticated.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return string|null
     */
    protected function redirectTo($request)
    {
        Log::info('Authenticated User: ' . Auth::guard('vcs')->user());
    }
}

The Log just returns an empty string so the user is not authenticated:

[2022-11-06 13:44:30] local.INFO: Authenticated User:

So my question is: How can I use a custom guard for my API routes after manually logging new users in?

I also tried the same workflow using Insomnia as a REST Client:

Login by URL: enter image description here

whichs gives me a sessions cookie.

Access some API Route: enter image description here

Which results in an Unauthorized-Status-Code..

Upvotes: 0

Views: 1149

Answers (0)

Related Questions