Download the PHP package baconfy/secure-tokens without Composer
On this page you can find all versions of the php package baconfy/secure-tokens. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package secure-tokens
API key authentication for Laravel using Ed25519 asymmetric cryptography. A secure alternative to Sanctum with clean token formats and optional request signing.
Table of Contents
- Features
- Requirements
- Installation
- Configuration
- Usage
- Setup the User Model
- Creating API Keys
- Protecting Routes
- Making Authenticated Requests
- Checking Abilities
- Revoking Keys
- Listing Keys
- Signed Requests (Ed25519)
- Route Setup
- Client-side Signing (PHP)
- Client-side Signing (Node.js)
- Client-side Signing (cURL)
- API Reference
- Ed25519Service
- ApiKey Model
- HasApiKeys Trait
- NewApiKey DTO
- ApiKeyGuard
- AuthenticateApiKey Middleware
- ValidateSignature Middleware
- ApiKeysServiceProvider
- HasApiKeys Contract
- Request Macro
- Database Schema
- Configuration Reference
- Architecture
- Token Format
- Authentication Flow
- Signature Validation Flow
- Storage Strategy
- Comparison with Sanctum
- Testing
- License
Features
- Ed25519 keypair generation (
sk_live_xxx/pk_live_xxx) - Secret key encrypted at rest, public key for lookups
- Bearer token authentication (simple mode)
- Ed25519 request signature validation (signed mode)
- Abilities/scopes per token
- Token expiration
- Multiple tokens per user
last_used_attracking- Polymorphic relationship (works with any Eloquent model)
- Configurable prefixes and environments
Requirements
- PHP 8.2+
- Laravel 11+
sodiumPHP extension (built-in since PHP 7.2)
Installation
Publish the config and migration:
Add the guard to config/auth.php:
Configuration
The configuration file is published to config/api-keys.php:
Usage
Setup the User Model
Add the trait and interface to any Eloquent model that should own API keys:
Creating API Keys
Important: The full prefixed secret key (
sk_live_xxxxx) is only returned at creation time. It cannot be retrieved later because only the raw key (without prefix) is stored encrypted in the database.
Protecting Routes
Option 1 — Laravel's built-in auth middleware (uses the guard name):
Option 2 — Package middleware alias (standalone, does not rely on config/auth.php):
Both approaches return a 401 Unauthenticated JSON response for invalid or missing tokens.
Making Authenticated Requests
Checking Abilities
Revoking Keys
Listing Keys
Signed Requests (Ed25519)
For higher security, require clients to sign the request body with their Ed25519 secret key and include the signature in the X-Signature header.
Route Setup
Note: Always place
auth:api-keybeforeverify-signatureso the API key is resolved before the signature is validated.
Client-side Signing (PHP)
Client-side Signing (Node.js)
Client-side Signing (cURL)
API Reference
Ed25519Service
Namespace: Baconfy\ApiKeys\Services\Ed25519Service
Service for Ed25519 cryptographic operations. Registered as a singleton in the container.
| Method | Parameters | Returns | Description |
|---|---|---|---|
generateKeypair() |
— | array{secret_key: string, public_key: string} |
Generate a raw Ed25519 keypair. Keys are URL-safe Base64 encoded. |
generatePrefixedKeypair() |
string $secretPrefix, string $publicPrefix, string $environment |
array{secret_key: string, public_key: string, raw_secret_key: string, raw_public_key: string} |
Generate a keypair with human-readable prefixes (e.g., sk_live_xxx). |
sign() |
string $message, string $base64SecretKey |
string |
Sign a message, returns Base64 detached signature. |
verify() |
string $message, string $base64Signature, string $base64PublicKey |
bool |
Verify a detached signature. Returns false on any failure. |
Example:
ApiKey Model
Namespace: Baconfy\ApiKeys\Models\ApiKey
Eloquent model representing an API key in the api_keys table.
Properties
| Property | Type | Description |
|---|---|---|
$id |
int |
Primary key. |
$tokenable_type |
string |
Polymorphic owner class (e.g., App\Models\User). |
$tokenable_id |
int |
Polymorphic owner ID. |
$name |
string |
Human-readable label. |
$prefix |
string |
Token prefix with environment (e.g., sk_live_). |
$secret_key |
string |
Ed25519 secret key (auto-encrypted/decrypted via Laravel). |
$public_key |
string |
Ed25519 public key (plain text, unique). |
$abilities |
array\|null |
JSON array of granted abilities. null or ["*"] = all. |
$last_used_at |
Carbon\|null |
Last authenticated request timestamp. |
$expires_at |
Carbon\|null |
Expiration timestamp. null = never. |
$created_at |
Carbon |
Creation timestamp. |
$updated_at |
Carbon |
Last update timestamp. |
Casts
| Attribute | Cast |
|---|---|
abilities |
array (JSON) |
secret_key |
encrypted |
last_used_at |
datetime |
expires_at |
datetime |
Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
tokenable() |
— | MorphTo |
Polymorphic relationship to the owning model. |
isExpired() |
— | bool |
true if expires_at is set and in the past. |
can() |
string $ability |
bool |
true if the ability is granted (or wildcard). |
cant() |
string $ability |
bool |
Inverse of can(). |
Example:
HasApiKeys Trait
Namespace: Baconfy\ApiKeys\HasApiKeys
Trait to be used on Eloquent models to enable API key management.
| Method | Parameters | Returns | Description |
|---|---|---|---|
apiKeys() |
— | MorphMany |
Polymorphic relationship returning all API keys for the model. |
createApiKey() |
string $name, string $environment = 'live', array $abilities = ['*'], ?DateTimeInterface $expiresAt = null |
NewApiKey |
Generate an Ed25519 keypair, persist it, and return the DTO. |
createApiKey() Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
$name |
string |
(required) | Human-readable label (e.g., "Production"). |
$environment |
string |
'live' |
Environment identifier (e.g., live, test). |
$abilities |
array |
['*'] |
Granted abilities. ['*'] grants all. |
$expiresAt |
?DateTimeInterface |
null |
Expiration datetime. Falls back to config('api-keys.expiration'). |
Example:
NewApiKey DTO
Namespace: Baconfy\ApiKeys\NewApiKey
Read-only Data Transfer Object returned by createApiKey().
| Property | Type | Description |
|---|---|---|
$apiKey |
ApiKey |
The persisted Eloquent model. |
$secretKey |
string |
Full prefixed secret key (e.g., sk_live_xxxxx). Only available at creation time. |
$publicKey |
string |
Full prefixed public key (e.g., pk_live_xxxxx). |
ApiKeyGuard
Namespace: Baconfy\ApiKeys\Guards\ApiKeyGuard
Custom authentication guard implementing Laravel's Illuminate\Contracts\Auth\Guard interface.
| Method | Parameters | Returns | Description |
|---|---|---|---|
check() |
— | bool |
true if a valid API key was provided. |
guest() |
— | bool |
true if no valid API key was provided. |
user() |
— | ?Authenticatable |
Resolve and return the authenticated user, or null. |
id() |
— | int\|string\|null |
Get the authenticated user's ID. |
validate() |
array $credentials |
bool |
Always returns false (not supported). |
hasUser() |
— | bool |
true if a user has been resolved or manually set. |
setUser() |
Authenticatable $user |
static |
Manually set the authenticated user. |
getApiKey() |
— | ?ApiKey |
Get the resolved ApiKey model for the current request. |
Accessing the guard:
AuthenticateApiKey Middleware
Namespace: Baconfy\ApiKeys\Middleware\AuthenticateApiKey
Alias: auth.api-key
| Method | Parameters | Returns | Description |
|---|---|---|---|
handle() |
Request $request, Closure $next |
Response |
Authenticate via api-key guard. Returns 401 JSON on failure. |
Response on failure:
ValidateSignature Middleware
Namespace: Baconfy\ApiKeys\Middleware\ValidateSignature
Alias: verify-signature
| Method | Parameters | Returns | Description |
|---|---|---|---|
handle() |
Request $request, Closure $next |
Response |
Validate X-Signature header against request body using Ed25519. |
Response on failure:
| Scenario | Status | Body |
|---|---|---|
Missing X-Signature header |
401 | { "message": "Missing X-Signature header." } |
| No authenticated API key | 401 | { "message": "Unauthenticated." } |
| Invalid signature | 401 | { "message": "Invalid signature." } |
ApiKeysServiceProvider
Namespace: Baconfy\ApiKeys\ApiKeysServiceProvider
Auto-discovered via composer.json extra.laravel.providers.
Registration Phase (register())
- Merges default config from
config/api-keys.php. - Binds
Ed25519Serviceas a singleton.
Boot Phase (boot())
| Internal Method | Description |
|---|---|
registerMigrations() |
Auto-loads migrations from database/migrations/. |
registerPublishing() |
Registers api-keys-config and api-keys-migrations publish tags. |
registerGuard() |
Extends Laravel Auth with the api-key driver. |
registerMiddleware() |
Registers auth.api-key and verify-signature middleware aliases. |
registerRequestMacro() |
Adds the apiKey() macro to Illuminate\Http\Request. |
Publish Tags
| Tag | Description | Command |
|---|---|---|
api-keys-config |
Configuration file | php artisan vendor:publish --tag=api-keys-config |
api-keys-migrations |
Migration files | php artisan vendor:publish --tag=api-keys-migrations |
HasApiKeys Contract
Namespace: Baconfy\ApiKeys\Contracts\HasApiKeys
Interface that models should implement alongside the HasApiKeys trait.
| Method | Parameters | Returns |
|---|---|---|
apiKeys() |
— | MorphMany |
createApiKey() |
string $name, string $environment, array $abilities = ['*'], ?DateTimeInterface $expiresAt = null |
NewApiKey |
Request Macro
The package registers an apiKey() macro on Illuminate\Http\Request:
Returns the ApiKey model for the current authenticated request, or null if unauthenticated.
Database Schema
The api_keys table:
| Column | Type | Nullable | Description |
|---|---|---|---|
id |
bigint (auto-increment) |
No | Primary key. |
tokenable_type |
string |
No | Polymorphic model class. |
tokenable_id |
bigint |
No | Polymorphic model ID. |
name |
string |
No | Human-readable label. |
prefix |
string(10) |
No | Token prefix (e.g., sk_live_). |
secret_key |
text |
No | Encrypted Ed25519 secret key. |
public_key |
string (unique) |
No | Plain text Ed25519 public key. |
abilities |
text (JSON) |
Yes | Array of granted abilities. |
last_used_at |
timestamp |
Yes | Last usage timestamp. |
expires_at |
timestamp |
Yes | Expiration timestamp. |
created_at |
timestamp |
No | Creation timestamp. |
updated_at |
timestamp |
No | Update timestamp. |
Indexes:
- Primary key on
id. - Composite index on
(tokenable_type, tokenable_id)(viamorphs()). - Unique index on
public_key.
Configuration Reference
| Key | Type | Default | Env Variable | Description |
|---|---|---|---|---|
prefix.secret |
string |
'sk' |
API_KEYS_SECRET_PREFIX |
Prefix for secret keys. |
prefix.public |
string |
'pk' |
API_KEYS_PUBLIC_PREFIX |
Prefix for public keys. |
environments |
array |
['live', 'test'] |
— | Valid environment identifiers. |
expiration |
int\|null |
null |
— | Default expiration in minutes. null = never. |
require_signature |
bool |
false |
— | Global signature requirement toggle. |
Architecture
Token Format
Tokens follow the format {prefix}_{environment}_{base64_key}:
- Secret key (
sk): Used by the client for authentication (Bearer token) and request signing. - Public key (
pk): Used for signature verification. Stored in plain text for database lookups.
Authentication Flow
Signature Validation Flow
Storage Strategy
| Component | Storage | Rationale |
|---|---|---|
| Secret key | Encrypted (encrypted cast) |
Protects against database leaks. |
| Public key | Plain text (unique index) | Enables fast lookups without decryption. |
| Abilities | JSON text | Flexible, schema-less permission model. |
The secret key comparison happens in PHP (not SQL) because the value is encrypted at rest. The guard queries by prefix first, then iterates results to compare decrypted values.
Comparison with Sanctum
| Feature | Laravel API Keys | Sanctum |
|---|---|---|
| Token format | sk_live_xxxxx (clean, prefixed) |
1\|xxxxx (ID-prefixed) |
| Cryptography | Ed25519 (asymmetric) | SHA-256 (hash) |
| Storage | Secret encrypted, public plain | Hashed token |
| Request signing | Built-in Ed25519 signatures | Not available |
| Token types | Secret + Public keypair | Single token |
| Lookup strategy | Query by prefix, compare in PHP | Hash input, query by hash |
| Abilities | Yes | Yes |
| Expiration | Yes | Yes |
| SPA auth | No (API-only) | Yes (cookie-based) |
| Polymorphic | Yes | Yes |
When to use Laravel API Keys
- Building APIs consumed by external services or partners.
- Need request signing for financial, webhook, or sensitive operations.
- Want clean, typed token formats with environment awareness.
- Don't need SPA cookie authentication.
When to use Sanctum
- SPA authentication with cookies.
- Simple first-party API tokens.
- Don't need request signing or asymmetric cryptography.
Testing
Test Suite Overview
| File | Tests | Description |
|---|---|---|
tests/Unit/Ed25519ServiceTest.php |
7 | Keypair generation, signing, verification, prefixes. |
tests/Feature/CreateApiKeyTest.php |
7 | Key creation, prefixes, abilities, expiration, config. |
tests/Feature/AuthenticateApiKeyTest.php |
11 | Bearer auth, rejection, expiration, guard methods. |
tests/Feature/RevokeApiKeyTest.php |
2 | Revocation and isolation between keys. |
tests/Feature/AbilitiesTest.php |
3 | Scoped abilities, wildcards, can()/cant(). |
tests/Feature/ValidateSignatureTest.php |
5 | Signature validation, rejection, edge cases. |
Total: 35 tests, 82 assertions, 100% code coverage.
License
Licensed under the GNU General Public License v3.0 or later (GPL-3.0-or-later). See LICENSE for details.
All versions of secure-tokens with dependencies
illuminate/contracts Version ^11.0|^12.0|^13.0
illuminate/database Version ^11.0|^12.0|^13.0
illuminate/support Version ^11.0|^12.0|^13.0
ext-sodium Version *