laravel / ideas

Issues board used for Laravel internals discussions.
938 stars 28 forks source link

Laravel Sanctum authenticates without csrf mismatch! Is that okay with this settings? #2321

Open beibl opened 4 years ago

beibl commented 4 years ago

Here is the simple form which doesn't have any csrf tokens to send to the server:

<!DOCTYPE html>
<html>
    <head>
        <title>Test API authentication</title>
    </head>
    <body>
        <form method="post" action="http://localhost:8000/api/login">
            <input name="email">
            <input name="password">
            <button>Submit</button>
        </form>
    </body>
</html>

And here - the login method:

public function login(Request $request)
{
    $credentials = $request->only('email', 'password');

    if (Auth::attempt($credentials)) {
        // Authentication passed...
        $authuser = auth()->user();
        return response()->json(['message' => 'Login successful'], 200);
    } else {
        return response()->json(['message' => 'Invalid email or password'], 401);
    }
}

This is the Kernel.php:

protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        // \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],

    'api' => [
        EnsureFrontendRequestsAreStateful::class,
        'throttle:60,1',
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];

And here is the cors.php:

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Cross-Origin Resource Sharing (CORS) Configuration
    |--------------------------------------------------------------------------
    |
    | Here you may configure your settings for cross-origin resource sharing
    | or "CORS". This determines what cross-origin operations may execute
    | in web browsers. You are free to adjust these settings as needed.
    |
    | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
    |
    */

    'paths' => ['api/*'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,

];

Same thing with Postman - it just gets authenticated without requesting csrf cookies or without any errors!

rs-sliske commented 4 years ago

VerifyCsrfToken middleware is only applied to your web routes, but your login endpoint is in the api routes

beibl commented 4 years ago

@rs-sliske here is the EnsureFrontendRequestsAreStateful class and it has sanctum's VerifyCsrfToken middleware

Show/hide ```php configureSecureCookieSessions(); return (new Pipeline(app()))->send($request)->through(static::fromFrontend($request) ? [ function ($request, $next) { $request->attributes->set('sanctum', true); return $next($request); }, config('sanctum.middleware.encrypt_cookies', \Illuminate\Cookie\Middleware\EncryptCookies::class), \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, config('sanctum.middleware.verify_csrf_token', \Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class), ] : [])->then(function ($request) use ($next) { return $next($request); }); } /** * Configure secure cookie sessions. * * @return void */ protected function configureSecureCookieSessions() { config([ 'session.http_only' => true, 'session.same_site' => 'lax', ]); } /** * Determine if the given request is from the first-party application frontend. * * @param \Illuminate\Http\Request $request * @return bool */ public static function fromFrontend($request) { $referer = Str::replaceFirst('https://', '', $request->headers->get('referer')); $referer = Str::replaceFirst('http://', '', $referer); $referer = Str::endsWith($referer, '/') ? $referer : "{$referer}/"; $stateful = array_filter(config('sanctum.stateful', [])); return Str::is(Collection::make($stateful)->map(function ($uri) { return trim($uri).'/*'; })->all(), $referer); } } ```
paras-malhotra commented 4 years ago

@beibl check out the docs: https://laravel.com/docs/8.x/sanctum#spa-authenticating

The first request must be to the csrf route to set the XSRF-TOKEN, which is then matched in the CSRF middleware

beibl commented 4 years ago

@paras-malhotra Did you understand the issue here?! Am I making myself clear, or should I rephrase?