Zum Hauptinhalt springen

API Security Essentials: OAuth2, JWT, and Rate Limiting Explained

· 6 Minuten Lesezeit

Grid banner

Introduction

APIs are the backbone of modern software. They connect applications, enable integrations, and power everything from mobile apps to AI workflows. But with this power comes risk: exposed APIs are a prime target for abuse, data leaks, and attacks.

This article explores three essential pillars of API security: OAuth2, JWT (JSON Web Tokens), and Rate Limiting. We'll discuss how each works, why they matter, and how to implement them in Laravel, one of the most popular PHP frameworks for building backends.

OAuth2: Delegated Authorization Made Easy

OAuth2 is a widely adopted protocol for delegated authorization. Instead of sharing credentials directly, clients obtain access tokens that represent permissions to act on behalf of a user.

In Laravel, Laravel Passport makes implementing OAuth2 straightforward.

Setup

Install the Laravel installer via Composer:

composer global require laravel/installer
tipp

If you don't have PHP and Composer installed on your local machine, follow the instructions on php.new

Create a new Laravel project without using a starter kit:

laravel new --database sqlite --pest security-essentials
cd security-essentials/

Install Laravel Passport with one command via the install:api Artisan command. The command will set up Passport, enable the API routes, run the database migrations, generate the encryption keys Passport needs in order to generate access tokens, and configure the necessary files.

php artisan install:api --passport
info

You can also set up Passport step by step:

composer require laravel/passport
php artisan migrate
php artisan passport:install
php artisan passport:keys

In case you want to re-generate the encryption keys, run:

php artisan passport:keys

Update the User model

The User model must implement the OAuthenticatable contract. The HasApiTokens trait contains the methods required by the interface:

app/Models/User.php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\Contracts\OAuthenticatable;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable implements OAuthenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}

Set up an API authentication guard

Define an api authentication guard and set the driver option to passport, so that Laravel uses Passport's TokenGuard for incoming API requests:

'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],

Create a test user

php artisan tinker --execute="\\App\\Models\\User::create(['name' => 'Test User', 'email' => 'test@example.com', 'password' => bcrypt('password')]);"

Create a Client Credentials Grant

php artisan passport:client --client

Serve the application on the PHP development server

php artisan serve

Request an access token

curl --location --request POST 'http://localhost:8000/oauth/token' \
--header 'Content-Type: application/json' \
--data-raw '{
"grant_type": "client_credentials",
"client_id": "<your-client-id>",
"client_secret": "<your-client-secret>",
"scope": ""
}'

Create an endpoint that requires authentication

routes/api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Laravel\Passport\Http\Middleware\EnsureClientIsResourceOwner;

Route::get('/client', function (Request $request) {
$client = auth()->guard('api')?->client();

if (! $client) {
return response()->json(['message' => 'Unauthorized'], 401);
}

return [
'id' => $client->getKey(),
'name' => $client->name,
'grant_types' => $client->grant_types,
];
})->middleware(EnsureClientIsResourceOwner::class);

Fetch a protected resource

curl --location --request GET 'http://localhost:8000/api/client' \
--header 'Authorization: Bearer <your-access-token>'
http://localhost:8000/api/client
info

Client credentials tokens are not associated with a user. Therefore, calling GET /api/user with such a token will return 401 Unauthorized by design.

With Passport, tokens can also include scopes, letting you fine-tune which API routes a client can access. Check out the Token Scopes documentation for more details.

JWT: Lightweight and Stateless Authentication

While OAuth2 is powerful, sometimes you just need a lightweight way to verify users. This is where JWT (JSON Web Tokens) shine. A JWT encodes claims (like user ID and roles) and is signed to prevent tampering. APIs can validate the token without storing session state.

For Laravel, the go-to package is tymon/jwt-auth.

Installation

composer require tymon/jwt-auth
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
php artisan jwt:secret

Example: Issue a JWT at Login

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

if (! $token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}

return response()->json(['token' => $token]);
}

Example: Protect Routes with JWT Middleware

Route::middleware(['jwt.auth'])->group(function () {
Route::get('/user', function () {
return auth()->user();
});
});

Now your routes require a valid token, keeping unauthorized requests out.

Rate Limiting: Prevent Abuse and Overload

Even with solid authentication, APIs are vulnerable to brute-force attacks, scraping, or accidental overload. Rate limiting helps protect your service by restricting how often a client can call your API.

Laravel provides this out of the box with the ThrottleRequests middleware.

Simple Example

Route::middleware(['throttle:60,1'])->group(function () {
Route::get('/api-data', [ApiController::class, 'index']);
});

This allows 60 requests per minute per IP address.

Per-User Rate Limits

You can also configure smarter rules in RouteServiceProvider:

RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(30)->by(optional($request->user())->id ?: $request->ip());
});

This ensures authenticated users each get their own quota, while unauthenticated requests fall back to IP-based limiting.

Best Practices

Securing APIs is more than just plugging in tools. Keep these best practices in mind:

  • Use short-lived tokens and refresh tokens for long sessions.
  • Never store tokens in localStorage on the frontend — prefer HTTP-only cookies.
  • Combine multiple layers: authentication + authorization + rate limiting.
  • Log and monitor failed requests and suspicious patterns.
  • Keep dependencies updated and patch vulnerabilities quickly.

Conclusion

API security is non-negotiable in 2025. By combining OAuth2, JWT, and Rate Limiting, you can build APIs that are both powerful and resilient against abuse.

Laravel makes it easier than ever to implement these patterns without reinventing the wheel. Whether you're exposing APIs for mobile apps, microservices, or AI agents, these tools should be part of your default stack.

Security is a journey, not a checkbox — but with the right foundations, your APIs can scale safely and sustainably.