PHPackages                             juststeveking/laravel-bastion - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. juststeveking/laravel-bastion

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

juststeveking/laravel-bastion
=============================

Stripe-inspired API authentication with environment isolation, granular scopes, and built-in security.

0.0.2(7mo ago)107485[2 issues](https://github.com/JustSteveKing/laravel-bastion/issues)[1 PRs](https://github.com/JustSteveKing/laravel-bastion/pulls)MITPHPPHP ^8.4CI passing

Since Oct 9Pushed 7mo ago1 watchersCompare

[ Source](https://github.com/JustSteveKing/laravel-bastion)[ Packagist](https://packagist.org/packages/juststeveking/laravel-bastion)[ Docs](https://github.com/juststeveking/laravel-bastion)[ GitHub Sponsors](https://github.com/sponsors/juststeveking)[ RSS](/packages/juststeveking-laravel-bastion/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (2)Dependencies (6)Versions (3)Used By (0)

 [![Laravel Bastion Logo](art/laravel-bastion-logo.png)](art/laravel-bastion-logo.png)

Laravel Bastion
===============

[](#laravel-bastion)

[![PHP Version](https://camo.githubusercontent.com/afe557c88325dff40aae414ffd4f3d3094e1ade715049fde258149fd475b9f85/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f6a75737473746576656b696e672f6c61726176656c2d62617374696f6e2e7376673f7374796c653d666c61742d737175617265)](https://php.net)[![Latest Version](https://camo.githubusercontent.com/0ed2339ef58f98f70804ab5814b69866799321188c07e202c6b527e43ebab58f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6a75737473746576656b696e672f6c61726176656c2d62617374696f6e2e7376673f7374796c653d666c61742d737175617265266c6162656c3d72656c65617365)](https://packagist.org/packages/juststeveking/laravel-bastion)[![Tests](https://github.com/JustSteveKing/laravel-bastion/actions/workflows/tests.yml/badge.svg)](https://github.com/JustSteveKing/laravel-bastion/actions/workflows/tests.yml)[![Formats](https://github.com/JustSteveKing/laravel-bastion/actions/workflows/formats.yml/badge.svg)](https://github.com/JustSteveKing/laravel-bastion/actions/workflows/formats.yml)[![License: MIT](https://camo.githubusercontent.com/fdf2982b9f5d7489dcf44570e714e3a15fce6253e0cc6b5aa61a075aac2ff71b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e737667)](LICENSE)[![Total Downloads](https://camo.githubusercontent.com/9afad7f0204db4bfc66a0cbb82342603c2e4c8a8a593089237c75946be6e1a05/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6a75737473746576656b696e672f6c61726176656c2d62617374696f6e2e7376673f7374796c653d666c61742d73717561726526636f6c6f72423d6d656469756d76696f6c6574726564)](https://packagist.org/packages/juststeveking/laravel-bastion)

Stripe-inspired API authentication with environment isolation, granular scopes, and built-in security.

Features
--------

[](#features)

- 🔐 **Stripe-style API Tokens** - Prefixed tokens with environment indicators (`app_test_pk_*`, `app_live_sk_*`)
- 🌍 **Environment Isolation** - Separate test and live environments with automatic validation
- 🎯 **Granular Scopes** - Fine-grained permission control with wildcard support
- 🔑 **Token Types** - Public, Secret, and Restricted keys with different access levels
- 📝 **Audit Logging** - Comprehensive activity tracking for compliance and debugging
- 🪝 **Webhook Support** - Built-in webhook endpoints with signature verification
- 🛡️ **Security First** - Expiration dates and secure token hashing
- ⚡ **Laravel Native** - Built with Laravel conventions and best practices

Requirements
------------

[](#requirements)

- PHP 8.4 or higher
- Laravel 12.x

Installation
------------

[](#installation)

Install the package via Composer:

```
composer require juststeveking/laravel-bastion
```

Run the installation command:

```
php artisan bastion:install
```

This will:

1. Publish the configuration file to `config/bastion.php`
2. Publish the database migrations
3. Optionally run the migrations

### Add the Trait to Your User Model

[](#add-the-trait-to-your-user-model)

```
use JustSteveKing\Bastion\Concerns\HasBastionTokens;

class User extends Authenticatable
{
    use HasBastionTokens;

    // ...
}
```

Quick Start
-----------

[](#quick-start)

### Generate a Token

[](#generate-a-token)

```
use JustSteveKing\Bastion\Enums\TokenEnvironment;
use JustSteveKing\Bastion\Enums\TokenType;

$result = $user->createBastionToken(
    name: 'My API Key',
    scopes: ['users:read', 'users:write'],
    environment: TokenEnvironment::Test,
    type: TokenType::Restricted,
);

// Store this securely - it's only shown once!
$token = $result['plainTextToken'];
// Example: app_test_rk_a8Kx7mN2pQ4vW9yB1cD3eF5gH6jK8lM

echo "Token: " . $token;
```

### Protect Routes with Middleware

[](#protect-routes-with-middleware)

```
use JustSteveKing\Bastion\Http\Middleware\AuthenticateToken;

Route::middleware(AuthenticateToken::class)->group(function () {
    Route::get('/api/users', [UserController::class, 'index']);
});

// Require specific scope
Route::middleware([AuthenticateToken::class . ':users:write'])
    ->post('/api/users', [UserController::class, 'store']);
```

### Make Authenticated Requests

[](#make-authenticated-requests)

```
curl -H "Authorization: Bearer app_test_rk_..." \
     https://your-api.com/api/users
```

Token Types
-----------

[](#token-types)

Bastion supports three token types, inspired by Stripe:

### Public Keys (`pk`)

[](#public-keys-pk)

```
TokenType::Public
```

- Prefix: `app_{env}_pk_*`
- Limited access, safe for client-side use
- Ideal for JavaScript/mobile apps
- Cannot perform sensitive operations

### Secret Keys (`sk`)

[](#secret-keys-sk)

```
TokenType::Secret
```

- Prefix: `app_{env}_sk_*`
- Full access to all permitted scopes
- Must be kept secure on the server
- Use for backend integrations

### Restricted Keys (`rk`)

[](#restricted-keys-rk)

```
TokenType::Restricted
```

- Prefix: `app_{env}_rk_*`
- Scoped access with specific permissions
- Best for third-party integrations
- Follows principle of least privilege

Environments
------------

[](#environments)

Bastion isolates test and production data:

### Test Environment

[](#test-environment)

```
TokenEnvironment::Test
```

- For development and testing
- Higher rate limits (default: 100/min)
- Can be used in any environment

### Live Environment

[](#live-environment)

```
TokenEnvironment::Live
```

- For production traffic
- Standard rate limits (default: 60/min)
- Can be restricted from non-production environments (configurable)

Advanced Features
-----------------

[](#advanced-features)

### Token Rotation

[](#token-rotation)

Rotate tokens to create a new token while revoking the old one:

```
$result = $token->rotate();

// Get the new token (store securely)
$newToken = $result['plainTextToken'];
$newTokenModel = $result['token'];

// The old token is automatically revoked
```

You can also rotate via CLI:

```
php artisan bastion:rotate {token-id}
```

### Scopes and Permissions

[](#scopes-and-permissions)

Bastion uses a flexible scope system with wildcard support:

```
// Grant specific permissions
$user->createBastionToken(
    name: 'User Manager',
    scopes: ['users:read', 'users:write'],
);

// Use wildcards for category-level access
$user->createBastionToken(
    name: 'Payment API',
    scopes: ['payments:*'], // All payment operations
);

// Full access
$user->createBastionToken(
    name: 'Admin Token',
    scopes: ['*'], // All scopes
);
```

#### Built-in Scope Examples

[](#built-in-scope-examples)

The package includes example scopes in `ApiScope` enum:

- `users:read`, `users:write`, `users:delete`
- `payments:read`, `payments:create`, `payments:refund`
- `webhooks:read`, `webhooks:write`
- `*` (admin/full access)

You can define your own scopes - they're just strings following the `resource:action` pattern.

### Webhooks

[](#webhooks)

Create webhook endpoints to receive real-time notifications:

```
use JustSteveKing\Bastion\Models\WebhookEndpoint;

$result = WebhookEndpoint::createEndpoint([
    'user_id' => $user->id,
    'url' => 'https://your-app.com/webhooks/bastion',
    'events' => ['token.created', 'token.revoked', 'token.used'],
    'environment' => TokenEnvironment::Live,
    'is_active' => true,
]);

// Store the signing secret securely!
$signingSecret = $result['signingSecret'];
// Example: whsec_a8Kx7mN2pQ4vW9yB1cD3eF5gH6jK8lM
```

#### Verifying Webhook Signatures

[](#verifying-webhook-signatures)

```
use JustSteveKing\Bastion\Models\WebhookEndpoint;

Route::post('/webhooks/bastion', function (Request $request) {
    $endpoint = WebhookEndpoint::where('secret_prefix', '...')->first();

    $signature = $request->header('X-Bastion-Signature');
    $timestamp = $request->header('X-Bastion-Timestamp');
    $payload = $request->getContent();

    if (!$endpoint->verifySignature($payload, $signature, (int)$timestamp)) {
        abort(401, 'Invalid signature');
    }

    // Process webhook...
    $event = $request->input('event');
    $data = $request->input('data');

    return response()->json(['received' => true]);
});
```

### Events

[](#events)

Bastion dispatches events for all token lifecycle actions:

```
use JustSteveKing\Bastion\Events\{
    TokenCreated,
    TokenUsed,
    TokenRevoked,
    TokenRotated,
    TokenExpired
};

// Listen to events in your EventServiceProvider
Event::listen(TokenCreated::class, function (TokenCreated $event) {
    // $event->token - The BastionToken model
    // $event->plainTextToken - The plain text token (only in TokenCreated)
    Log::info('Token created', ['token_id' => $event->token->id]);
});

Event::listen(TokenUsed::class, function (TokenUsed $event) {
    // $event->token
    // $event->ipAddress
    // $event->userAgent
    // $event->endpoint
});

Event::listen(TokenRevoked::class, function (TokenRevoked $event) {
    // $event->token
    // $event->reason
    Mail::to($event->token->user)->send(new TokenRevokedNotification($event));
});
```

### Audit Logging

[](#audit-logging)

Enable comprehensive API request auditing by adding the middleware:

```
use JustSteveKing\Bastion\Http\Middleware\{AuthenticateToken, AuditApiRequest};

Route::middleware([AuthenticateToken::class, AuditApiRequest::class])
    ->group(function () {
        // All requests will be logged
        Route::get('/api/users', [UserController::class, 'index']);
    });
```

Audit logs capture:

- Request method, path, and query parameters
- Response status code and time
- IP address and user agent
- Token and user information
- Request/response bodies (configurable)

Query audit logs:

```
use JustSteveKing\Bastion\Models\AuditLog;

// Get recent activity for a token
$logs = AuditLog::where('bastion_token_id', $token->id)
    ->latest()
    ->take(100)
    ->get();

// Find failed requests
$failures = AuditLog::where('status_code', '>=', 400)
    ->where('created_at', '>=', now()->subDay())
    ->get();
```

CLI Commands
------------

[](#cli-commands)

Bastion provides several Artisan commands for token management:

### Generate Token

[](#generate-token)

```
php artisan bastion:generate {user-id} "Token Name" \
    --environment=test \
    --type=restricted \
    --scopes=users:read --scopes=users:write
```

### Revoke Token

[](#revoke-token)

```
# Revoke by token ID
php artisan bastion:revoke 123 --reason="Security incident"

# Revoke by token prefix
php artisan bastion:revoke abc12345 --reason="No longer needed"

# Revoke all tokens for a user
php artisan bastion:revoke 0 --all-user=456 --reason="User offboarded"
```

### Rotate Token

[](#rotate-token)

```
php artisan bastion:rotate {token-id}
```

### Prune Expired Tokens

[](#prune-expired-tokens)

```
# Prune expired tokens
php artisan bastion:prune-tokens --expired

# Prune tokens unused for 90 days
php artisan bastion:prune-tokens --days=90
```

### Prune Old Audit Logs

[](#prune-old-audit-logs)

```
# Use config default (90 days)
php artisan bastion:prune-logs

# Custom retention period
php artisan bastion:prune-logs --days=30
```

Schedule these commands in your `app/Console/Kernel.php`:

```
protected function schedule(Schedule $schedule): void
{
    $schedule->command('bastion:prune-tokens --expired')->daily();
    $schedule->command('bastion:prune-logs')->weekly();
}
```

Configuration
-------------

[](#configuration)

Publish and edit the configuration file:

```
php artisan vendor:publish --tag=bastion-config
```

### Key Configuration Options

[](#key-configuration-options)

```
return [
    // Table names (customizable)
    'tables' => [
        'tokens' => 'bastion_tokens',
        'audit_logs' => 'bastion_audit_logs',
        'webhooks' => 'bastion_webhook_endpoints',
    ],

    // Token expiration (days)
    'token_expiration_days' => null,

    // Audit log retention (days)
    'audit_log_retention_days' => 90,

    // Rate limits per minute
    'rate_limits' => [
        'test' => 100,
        'live' => 60,
    ],

    // Security settings
    'security' => [
        'prevent_test_tokens_in_production' => true,
        'enable_audit_logging' => true,
        'enable_alerting' => true,
    ],

    // Error response format
    'errors' => [
        'use_rfc7807' => true, // RFC 7807 Problem Details
        'base_url' => 'https://bastion.laravel.com/errors/', // Base for problem type URLs
    ],

    // User model
    'user_model' => App\Models\User::class,
];
```

#### RFC 7807 Base URL

[](#rfc-7807-base-url)

Bastion returns errors in RFC 7807 Problem Details format by default. You can customize the base URL used for the `type` field in error responses:

```
// config/bastion.php
'errors' => [
    'use_rfc7807' => true,
    'base_url' => 'https://bastion.laravel.com/errors/',
],
```

With this configuration, an unauthenticated request will return a `type` like:

- `https://bastion.laravel.com/errors/token_missing`
- `https://bastion.laravel.com/errors/token_invalid`
- `https://bastion.laravel.com/errors/insufficient_scope`

Adjust `base_url` to point to your own error documentation if desired.

Security Best Practices
-----------------------

[](#security-best-practices)

1. **Never log tokens** - Only the HMAC hash is stored in the database
2. **Show tokens once** - Display the plain text token only at creation time
3. **Use HTTPS exclusively** - Always transmit tokens over encrypted connections
4. **Use restricted tokens** - Grant minimum necessary permissions (principle of least privilege)
5. **Set expiration dates** - Especially for temporary integrations
6. **Rotate tokens regularly** - Implement a token rotation policy (e.g., every 90 days)
7. **Monitor audit logs** - Watch for suspicious activity and unusual patterns
8. **Use test tokens in development** - Keep live tokens in production only
9. **Store tokens securely** - Use environment variables or secure vaults (AWS Secrets Manager, HashiCorp Vault)

### Token Security Features

[](#token-security-features)

Laravel Bastion implements multiple security layers:

- **HMAC-SHA256 hashing** - Tokens are hashed with your application key
- **Constant-time comparison** - Prevents timing attacks during token lookup
- **Cryptographically secure RNG** - Uses `random_bytes()` for token generation
- **Environment isolation** - Prevents test tokens in production (configurable)
- **Automatic event dispatching** - Monitor all token lifecycle events

### Community Requests

[](#community-requests)

Have a feature idea? [Open an issue](https://github.com/juststeveking/laravel-bastion/issues/new?template=feature_request.md) with the `enhancement` label.

Out of Scope
------------

[](#out-of-scope)

Bastion focuses on token-based authentication with scopes and environments. It does not implement:

- IP allowlisting or CIDR-based restrictions
- Domain/host origin restrictions

If you need these controls, add them at your application layer (e.g., trusted proxies, firewall/WAF rules, or custom middleware) alongside Bastion.

###  Health Score

38

—

LowBetter than 85% of packages

Maintenance64

Regular maintenance activity

Popularity24

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 57.1% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~4 days

Total

2

Last Release

211d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a87fe82b349821ebd49cf57d65c4cd3e33ae0833222a8438d90fe8921ca899ea?d=identicon)[JustSteveKing](/maintainers/JustSteveKing)

---

Top Contributors

[![JustSteveKing](https://avatars.githubusercontent.com/u/6368379?v=4)](https://github.com/JustSteveKing "JustSteveKing (4 commits)")[![raphaelstolt](https://avatars.githubusercontent.com/u/48225?v=4)](https://github.com/raphaelstolt "raphaelstolt (2 commits)")[![ludoguenet](https://avatars.githubusercontent.com/u/36139526?v=4)](https://github.com/ludoguenet "ludoguenet (1 commits)")

---

Tags

apilaraveltokenssecuritystripeAuthenticationsanctumapi keys

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/juststeveking-laravel-bastion/health.svg)

```
[![Health](https://phpackages.com/badges/juststeveking-laravel-bastion/health.svg)](https://phpackages.com/packages/juststeveking-laravel-bastion)
```

###  Alternatives

[hasinhayder/tyro

Tyro - The ultimate Authentication, Authorization, and Role &amp; Privilege Management solution for Laravel 12 &amp; 13

6712.1k2](/packages/hasinhayder-tyro)[rinvex/laravel-authy

Rinvex Authy is a simple wrapper for Authy TOTP, the best rated Two-Factor Authentication service for consumers, simplest 2fa Rest API for developers and a strong authentication platform for the enterprise.

3376.7k1](/packages/rinvex-laravel-authy)[authlete/authlete-laravel

Authlete Library for Laravel

4226.0k](/packages/authlete-authlete-laravel)[hosseinhezami/laravel-permission-manager

Advanced permission manager for Laravel.

403.3k](/packages/hosseinhezami-laravel-permission-manager)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
