Skip to main content
Laravel, shipping fast.

Chapter 3

Authentication & Security

Julian Beaujardin

You can't ship an API without thinking about security. And the good news is that security doesn't require complex architecture. It requires consistent, enforced patterns.

This chapter builds a production-grade security system that uses simple, proven patterns applied everywhere. No theories, no complex abstractions. Just practical approaches that have protected billions of API requests in production. You'll understand how stateless Bearer tokens work, why caching them matters for performance, how middleware enforces security at every layer, what makes rate limiting effective, and how to validate requests at the boundary so bad data never reaches your controllers.

One principle guides everything: Security is a default, not an opt-in feature. Authentication isn't something you add later. Rate limiting isn't something you remember. Domain validation isn't something you hope for. They're all built in. Follow the patterns in this chapter and security becomes automatic, embedded in your architecture, enforced everywhere without exception.

Why Bearer Tokens

Before we implement bearer tokens, let's address the obvious question: why this approach?

This API is service-to-service, not user-facing. There's no "user login" happening. There are no sessions, no cookie-based authentication, no user profiles. Instead, backend systems call this API to manage licenses. A deployment system calls it. A billing system calls it. Other microservices call it. These callers need authentication, but a totally different kind than a web user.

OAuth 2.0 and Laravel Sanctum are excellent for user-facing APIs. They handle user impersonation, personal access tokens, SPA authentication, all the complexities of representing individual humans. But we don't need any of that. We need a ultra-simple mechanism: prove you have a valid token, and you get access.

Ryan Chandler's ryangjchandler/bearer package solves exactly this problem. It's minimal: a token model, basic hashing, expiration handling, that's it. No user concepts. No session state. No complicated flows. Just stateless token lookup and validation.

A Framework Consistency Exception: You'll notice throughout this guide we emphasize following Laravel conventions and using the framework's built-in solutions. Authentication is the one intentional exception. Why? Because choosing the right tool matters more than choosing a Laravel tool. Sanctum is built into Laravel and solves important problems. But it solves a different problem than what we have. Using the wrong tool "correctly" is worse than using the right tool well. If we forced Sanctum's user-based authentication model into a service-to-service context, we'd be fighting the framework rather than flowing with it. Instead, we chose a minimal, purpose-built package that fits our actual needs perfectly, and we extend it following Laravel patterns. This is not framework rebellion; it's framework pragmatism.

We chose this package over Sanctum for three reasons:

One: Purpose-built. Sanctum assumes you're building a multi-user application with user authentication. Its complexity reflects that. Bearer tokens from Chandler's package are designed exactly for service-to-service requests where tokens represent systems, not people.

Two: Avoid complexity. Sanctum forces you to think about personal access tokens, user impersonation, SPA sessions. None of that applies here. The fewer concepts you have to carry in your head, the fewer bugs you'll introduce.

Three: Flexibility. We extend Chandler's simple Token model to add our own Bearer model with properties like settings and domains. Sanctum's user model assumptions would fight us at every step.

Here's the brutal truth: most security failures don't come from missing complex infrastructure. They come from inconsistency and forgotten edge cases. A simple system applied everywhere beats a complex system applied inconsistently.