Skip to content

Authentication

Basic authentication with any valid token:

routes/api.php
use Illuminate\Support\Facades\Route;
Route::middleware('auth:bearer')->group(function () {
Route::get('/user', function (Request $request) {
return $request->user();
});
});

Require all specified abilities:

Route::middleware(['auth:bearer', 'abilities:users:read,users:write'])->group(function () {
Route::get('/users', [UserController::class, 'index']);
Route::post('/users', [UserController::class, 'store']);
});

Require any of the specified abilities:

Route::middleware(['auth:bearer', 'ability:admin,moderator'])->group(function () {
Route::get('/admin/dashboard', [AdminController::class, 'dashboard']);
});

Only allow secret keys (server-side endpoints):

Route::middleware(['auth:bearer', 'token-type:sk'])->group(function () {
Route::post('/webhooks', [WebhookController::class, 'handle']);
Route::post('/payments', [PaymentController::class, 'charge']);
});

Only allow publishable keys (client-side endpoints):

Route::middleware(['auth:bearer', 'token-type:pk'])->group(function () {
Route::post('/checkout/session', [CheckoutController::class, 'createSession']);
});

Allow multiple token types:

Route::middleware(['auth:bearer', 'token-type:sk,rk'])->group(function () {
Route::get('/internal/status', [StatusController::class, 'index']);
});

Only allow live environment tokens:

Route::middleware(['auth:bearer', 'environment:live'])->group(function () {
Route::post('/payments/charge', [PaymentController::class, 'charge']);
});

Only allow test environment tokens:

Route::middleware(['auth:bearer', 'environment:test'])->group(function () {
Route::post('/test/webhook', [TestController::class, 'simulateWebhook']);
});

Require: valid token + secret key + live environment + payment ability:

Route::middleware([
'auth:bearer',
'token-type:sk',
'environment:live',
'abilities:payments:charge',
])->group(function () {
Route::post('/payments/charge', [PaymentController::class, 'charge']);
});
use Illuminate\Http\Request;
class ApiController extends Controller
{
public function index(Request $request)
{
$user = $request->user();
$token = $user->currentAccessToken();
// Check token type
if ($user->tokenIs('pk')) {
return $this->limitedResponse();
}
// Check abilities
if ($user->tokenCan('admin')) {
return $this->adminResponse();
}
// Check environment
if ($user->tokenEnvironment() === 'test') {
return $this->testResponse();
}
return $this->standardResponse();
}
}
use Cline\Bearer\Bearer;
use Tests\TestCase;
class ApiTest extends TestCase
{
public function test_user_can_access_api(): void
{
$user = User::factory()->create();
// Act as user with specific abilities
Bearer::actingAs($user, ['users:read', 'users:write']);
$response = $this->getJson('/api/users');
$response->assertOk();
}
public function test_secret_key_required(): void
{
$user = User::factory()->create();
// Act as user with specific token type
Bearer::actingAs($user, ['*'], 'sk');
$response = $this->postJson('/api/webhooks');
$response->assertOk();
}
}

Configure stateful domains for session-based auth:

config/bearer.php
'stateful' => ['localhost', 'spa.example.com', '*.example.com']

These domains will use session-based auth (cookies) instead of tokens. Perfect for first-party SPAs where you don’t want to expose tokens.

routes/api.php
Route::middleware('auth:bearer')->group(function () {
// Works with both:
// 1. Bearer tokens (Authorization: Bearer sk_test_...)
// 2. Session cookies (for stateful domains)
Route::get('/user', fn (Request $request) => $request->user());
});