Tuesday, March 21, 2017

How to prevent Laravel API from processing parameters on query-string?

I would like to restrict my Laravel API from processing parameters as query-string when trying to authenticate the user. I've been trying with POSTMAN and all the time I'm able to get the token from my API whether I put the credentials on the body or as query-string in the url.

As per Laravel documentation I think that this is the behavior I want to avoid:

Retrieving Input Via Dynamic Properties

You may also access user input using dynamic properties on the Illuminate\Http\Request instance. For example, if one of your application's forms contains a name field, you may access the value of the field like so:

$name = $request->name;

When using dynamic properties, Laravel will first look for the parameter's value in the request payload. If it is not present, Laravel will search for the field in the route parameters.

I'm using Laravel 5.3 and PHP 7.1.0

Here is the POST by using query-string:

enter image description here

Here is the POST by using parameters in the body:

enter image description here

I have configured my CORS by using laravel-cors:

<?php

return [
   'defaults' => [
       'supportsCredentials' => false,
       'allowedOrigins' => [],
       'allowedHeaders' => [],
       'allowedMethods' => [],
       'exposedHeaders' => [],
       'maxAge' => 0,
       'hosts' => [],
   ],

   'paths' => [
       'v1/*' => [
           'allowedOrigins' => ['*'],
           'allowedHeaders' => ['Accept', 'Content-Type'],
           'allowedMethods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
           'exposedHeaders' => ['Authorization'],
           'maxAge' => 3600,
       ],
   ],
];

My routes (the relevant ones):

Route::group(
    [
        'domain' => getenv('API_DOMAIN'),
        'middleware' => 'cors',
        'prefix' => '/v1',
        'namespace' => 'V1'
    ],
    function() {
        /* AUTHENTICATION */
        Route::post(
            'signin',
            'AuthenticationController@signin'
        )->name('signin');

        Route::post(
            'signup',
            'AuthenticationController@signup'
        )->name('signup');
    }
);

When listing my routes php artisan route:list I get:

------------------------------------------------------------------------------------------------------------------------------------
| Domain    | Method | URI           | Name       | Action                                                            | Middleware |
| localhost | POST   | api/v1/signin | api.signin | App\Http\Controllers\API\V1\AuthenticationController@signin       | api,cors   |
| localhost | POST   | api/v1/signup | api.signup | App\Http\Controllers\API\V1\AuthenticationController@signup       | api,cors   |
------------------------------------------------------------------------------------------------------------------------------------

My AuthenticationController:

<?php

namespace App\Http\Controllers\API\V1;

use Illuminate\Http\Request;

use App\Http\Controllers\Controller;
use Tymon\JWTAuth\Exceptions\JWTException;
use App\Http\Requests;
use JWTAuth;

class AuthenticationController extends Controller
{
    public function __construct()
    {
        $this->middleware('jwt.auth', ['except' => ['signin', 'signup']]);
    }

    public function signin(Request $request)
    {
        $credentials = $request->only('email', 'password');
        try {
            if (! $token = JWTAuth::attempt($credentials)) {
                return response()->json(
                    [
                        'error' => 'invalid_credentials'
                    ],
                    401
                );
            }
        } catch (JWTException $e) {
            return response()->json(
                [
                    'error' => 'could_not_create_token'
                ],
                500
            );
        }
        return response()->json(compact('token'));
    }

    public function signup(Request $request)
    {
        try {
            $user = User::where(['email' => $request["email"]])->exists();
            if($user)
            {
                return Response::json(
                    array(
                        'msg' => "Email {$request->email} already exists"
                    ),
                    400
                );
            }
            $user = new User;
            $user->create($request->input());
            return Response::json(
                array(
                    'msg' => 'User signed up.'
                )
            );
        } catch (Exception $exception) {
            return Response::json(
                array(
                    'success' => false,
                    'exception' => $exception
                )
            );
        }
    }
}

My Kernel:

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \Barryvdh\Cors\HandleCors::class,
    ];

    /**
     * The application's route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class,
        'jwt.refresh' => \Tymon\JWTAuth\Middleware\RefreshToken::class,
    ];
}

And I placed the respective configuration on config/app.php:

        ...
        /*
         * Package Service Providers...
         */
        Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
        Collective\Html\HtmlServiceProvider::class,
        Laracasts\Flash\FlashServiceProvider::class,
        Prettus\Repository\Providers\RepositoryServiceProvider::class,
        \InfyOm\Generator\InfyOmGeneratorServiceProvider::class,
        \InfyOm\CoreTemplates\CoreTemplatesServiceProvider::class,

        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,

        Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,
        Barryvdh\Cors\ServiceProvider::class,
        Asvae\ApiTester\ServiceProvider::class,
    ],

I don't want to use dingoapi.

I checked these resources:

Last but not least, my composer.json:

{
    "name": "laravel/laravel",
    "description": "The Laravel Framework.",
    "keywords": ["framework", "laravel"],
    "license": "MIT",
    "type": "project",
    "require": {
        "php": "^7.1.0",
        "laravel/framework": "5.3.*",
        "barryvdh/laravel-ide-helper": "v2.2.1",
        "laravelcollective/html": "v5.3.0",
        "infyomlabs/laravel-generator": "5.3.x-dev",
        "infyomlabs/core-templates": "5.3.x-dev",
        "infyomlabs/swagger-generator": "dev-master",
        "jlapp/swaggervel": "2.0.x-dev",
        "doctrine/dbal": "2.3.5",
        "league/flysystem-aws-s3-v3": "1.0.13",
        "tymon/jwt-auth": "0.5.9",
        "barryvdh/laravel-cors": "v0.8.2",
        "fzaninotto/faker": "~1.4",
        "caouecs/laravel-lang": "3.0.19",
        "asvae/laravel-api-tester": "^2.0"
    },
    "require-dev": {
        "fzaninotto/faker": "~1.4",
        "mockery/mockery": "0.9.*",
        "phpunit/phpunit": "~5.7",
        "symfony/css-selector": "3.1.*",
        "symfony/dom-crawler": "3.1.*"
    },
    "autoload": {
        "classmap": [
            "database"
        ],
        "psr-4": {
            "App\\": "app/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    },
    "scripts": {
        "post-root-package-install": [
            "php -r \"file_exists('.env') || copy('.env.example', '.env');\""
        ],
        "post-create-project-cmd": [
            "php artisan key:generate"
        ],
        "post-install-cmd": [
            "Illuminate\\Foundation\\ComposerScripts::postInstall",
            "php artisan optimize"
        ],
        "post-update-cmd": [
            "Illuminate\\Foundation\\ComposerScripts::postUpdate",
            "php artisan optimize"
        ]
    },
    "config": {
        "preferred-install": "dist"
    }
}



via nisevi

Advertisement