How to generate an access token in Laravel 9 without using Sanctum or Passport
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
How to Generate Access Tokens in Laravel 9 Without Sanctum or Passport
When building an API, authentication and authorization are non-negotiable. Laravel provides powerful solutions like Sanctum and Passport to handle this complexity efficiently. However, sometimes you need complete control over your token generation mechanism, perhaps for a highly specialized internal system or when adhering to very specific architectural constraints. The question then becomes: how do we generate secure access tokens without relying on these official packages?
The short answer is: No, you should never just generate a random string. A random string is easily guessable and offers no inherent security or association with a specific user. To build a functional system without Sanctum or Passport, you must implement your own token management layer, which involves database storage, strong hashing, and expiration logic.
This post will guide you through the developer's perspective on implementing custom token-based authentication in Laravel 9.
Why Simple Random Strings Fail: The Security Perspective
Generating a random string (like random_string(64)) is simple, but it fails immediately in a security context because it lacks context and integrity. If you simply store this string in the database:
- No Association: You cannot easily tie that token back to a specific user or permission set without complex manual lookups.
- No Revocation: If a token is compromised, there is no easy way to instantly invalidate it across your system unless you manually track every single one in an external table.
- Lack of Integrity: The client could potentially tamper with the token if it’s just a plain string, leading to severe security vulnerabilities.
A proper access token must be opaque (meaning the server knows its existence but doesn't need to parse its contents) or self-contained (like a JWT), and crucially, it must be cryptographically secure.
The Custom Token Strategy: Database-Backed Tokens
Since we are avoiding Sanctum and Passport, the most robust custom approach is to implement an opaque token system where the actual token value is stored in your database, linked directly to the user, along with metadata like creation time and expiry.
Step 1: Database Migration Setup
First, you need a migration to store these tokens. This ensures that every token generated is tracked and manageable within your Laravel application structure.
// database/migrations/..._create_api_tokens_table.php
Schema::create('api_tokens', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade'); // Link to the User model
$table->string('token', 64)->unique(); // The actual unique token string
$table->timestamps();
$table->boolean('expires')->default(false);
});
Step 2: Token Generation and Storage Logic
In your controller or service layer, you handle the generation. Instead of random strings, we use Laravel’s strong cryptographically secure random number generator to create a sufficiently long, unique identifier for the token.
use Illuminate\Support\Str;
use App\Models\User;
class TokenController extends Controller
{
public function generateToken(User $user)
{
// 1. Generate a cryptographically secure random string (the token)
$token = Str::random(64);
// 2. Store the token in the database
\App\Models\ApiToken::create([
'user_id' => $user->id,
'token' => $token,
// Add expiration logic here if desired
]);
// 3. Return the token to the client. The client now holds this secret.
return response()->json([
'message' => 'Token generated successfully.',
'token' => $token,
], 201);
}
}
Step 3: Token Verification (Middleware)
To use this token for subsequent API requests, you need a custom middleware. This middleware will intercept the request, extract the token from the header (e.g., Authorization: Bearer <token>), look it up in your api_tokens table, and verify its existence and validity before allowing access to the route.
This process mirrors how robust Laravel applications handle authorization, ensuring that even when bypassing built-in packages, you maintain the principles of secure data handling advocated by the larger Laravel ecosystem, as discussed on platforms like https://laravelcompany.com.
Conclusion
Implementing custom token generation without Sanctum or Passport is entirely possible, but it shifts the entire burden of security and maintenance onto your team. The key takeaway is that the token itself must be a reference to secure data stored in your persistence layer, not just a random string. By using strong hashing principles internally (even if you aren't using full JWT signing), ensuring unique database linkage, and implementing strict middleware checks, you can successfully build a secure, custom API authentication system.