After releasing the Authenticated API Builder provided by Laravel Passport, some of our customers were asking us how to remove it, for some public API endpoints, which do not require authentication. Here is the article for you.

Warning: Be very careful with this approach. This article will show you how to make the API public, meaning ANYONE can access it from anywhere. Please make sure you really want to do this.


By default, here is our routes/api.php:


Route::group([
    'prefix' => 'v1', 
    'as' => 'api.', 
    'namespace' => 'Api\V1\Admin', 
    'middleware' => ['auth:api']
], function () {
    Route::apiResource('permissions', 'PermissionsApiController');
    Route::apiResource('roles', 'RolesApiController');
    Route::apiResource('users', 'UsersApiController');

    // ... more API endpoints
});

The most important part here is authentication: API middleware, which is defined in config/auth.php as API driver ‘passport’.

And if you try to GET /api/v1/users without any authentication you will get the error “The key path…/storage/oauth-public.key does not exist or is not readable”:

If you want to remove this Auth from some of your routes, simply create a separate route group that does not have this middleware. Let’s say we want to make the user list public. THE routes/api.php seen from above, it might look like this:


Route::group([
    'prefix' => 'v1',
    'as' => 'api.',
    'namespace' => 'Api\V1\Admin',
], function () {

    // These routes will still be secured by Laravel Passport
    Route::group([
        'middleware' => ['auth:api']
    ], function() {
        Route::apiResource('permissions', 'PermissionsApiController');
        Route::apiResource('roles', 'RolesApiController');

        // ... Other secured API endpoints
    });

    // This route will be public
    Route::apiResource('users', 'UsersApiController');

    // ... Other public API endpoints
});

We create a group of routes within a group. So all API endpoints will have the same prefix and namespace, but only one subgroup will have authentication: API middleware.

So we did our /api/v1/users accessible without login. But we will still get a different error:

“403 Forbidden”

Laravel API 403 prohibited

And that’s because our request is authenticated but not allowed for this particular method.


Authentication (401) and authorization (403)

There is a difference:

  • Authentication means if someone is logged in or not for the whole system (if it fails it should return HTTP status code 401 unauthorized);
  • Means of authorization if the logged in user has permissions for a specific area of ​​the system (if it fails it should return HTTP status code 403 Forbidden).

Now 403 Forbidden happens because in our controllers we check permissions and expect the user to be logged in. For example, here is app/Http/Controllers/Api/V1/Admin/UsersApiController:


class UsersApiController extends Controller
{
    public function index()
    {
        abort_if(Gate::denies('user_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');

        return new UserResource(User::with(['roles'])->get());
    }

    // ... other methods

See the abortive() statement? This is where we check permissions.
Now if you want to skip this check just delete this line in the controller.

And then… you will manage to obtain your list of users, without any authentication or authorization. In public:

Laravel 200 API Status


Restricting POST/PUT methods

So far I have shown you how to make the GET request public. But for POST or PUT requests we generate a slightly different code. We place Auth checking in the FormRequests classes instead of the Controller.

So if you want to create a new user, here is your method in UsersApiController.php:


public function store(StoreUserRequest $request)
{
    $user = User::create($request->all());
    $user->roles()->sync($request->input('roles', []));

    return (new UserResource($user))
        ->response()
        ->setStatusCode(Response::HTTP_CREATED);
}

Looks like there’s no permission check, right? But it’s actually inside that app/Http/Requests/StoreUserRequest.php:


class StoreUserRequest extends FormRequest
{
    public function authorize()
    {
        abort_if(Gate::denies('user_create'), Response::HTTP_FORBIDDEN, '403 Forbidden');

        return true;
    }

    public function rules()
    {
        return [
            'name' => 'required',
            // ... other validation rules
        ];
    }
}

So method authorize() returns true or false, depending on a user’s permissions.
Learn more about Laravel form requests in the official documentation.

If you want to make this POST public (I can’t imagine why, but still), just delete that abortive() from here.

Notice: This FormRequest class is reused for both web controller and API controller, so if you want to change something, it will be affected in both cases.

That’s it, that’s all. This is how you can remove authentication: API and Laravel Gates to make your API endpoints public. But again, be careful with this!



Technology

Another Tech Information

Similar Posts