{"id":2710,"date":"2026-08-05T07:54:27","date_gmt":"2026-08-05T07:54:27","guid":{"rendered":"https:\/\/codexprime.cloud\/?p=2710"},"modified":"2026-08-05T08:04:16","modified_gmt":"2026-08-05T08:04:16","slug":"how-to-build-a-secure-rest-api-in-laravel-11-using-sanctum","status":"publish","type":"post","link":"https:\/\/codexprime.cloud\/?p=2710","title":{"rendered":"How to Build a Secure REST API in Laravel 11 Using Sanctum"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">How to Build a Secure REST API in Laravel 11 Using Sanctum<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A few months back, a client asked me to turn their existing Laravel web app into something that could also power a mobile app. Simple enough, right? Just expose some endpoints, slap on some authentication, done.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img fetchpriority=\"high\" decoding=\"async\" width=\"1024\" height=\"562\" src=\"https:\/\/codexprime.cloud\/wp-content\/uploads\/2026\/07\/How-to-Build-a-Secure-REST-API-in-Laravel-11-Using-Sanctum-1024x562.png\" alt=\"\" class=\"wp-image-2711\" srcset=\"https:\/\/codexprime.cloud\/wp-content\/uploads\/2026\/07\/How-to-Build-a-Secure-REST-API-in-Laravel-11-Using-Sanctum-1024x562.png 1024w, https:\/\/codexprime.cloud\/wp-content\/uploads\/2026\/07\/How-to-Build-a-Secure-REST-API-in-Laravel-11-Using-Sanctum-300x165.png 300w, https:\/\/codexprime.cloud\/wp-content\/uploads\/2026\/07\/How-to-Build-a-Secure-REST-API-in-Laravel-11-Using-Sanctum-768x421.png 768w, https:\/\/codexprime.cloud\/wp-content\/uploads\/2026\/07\/How-to-Build-a-Secure-REST-API-in-Laravel-11-Using-Sanctum-1536x843.png 1536w, https:\/\/codexprime.cloud\/wp-content\/uploads\/2026\/07\/How-to-Build-a-Secure-REST-API-in-Laravel-11-Using-Sanctum.png 1693w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Except the first version I shipped had a problem. I used a basic token system I hacked together myself \u2014 stuff a random string in the database, check it on every request. It worked, until it didn&#8217;t. Tokens never expired, there was no way to revoke a single device without logging everyone out, and I genuinely lost a few hours debugging why a user&#8217;s session on their phone kept dying randomly (turns out I was regenerating the token on every login, which invalidated the old one while the app still had it cached).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s when I actually sat down and used Sanctum properly instead of reinventing it. And honestly, once it clicked, I felt a little silly for not using it from day one.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;re building an API for a mobile app, an SPA, or just want third-party apps to talk to your Laravel backend, this is the writeup I wish I&#8217;d had before I started.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why Sanctum and not Passport or plain JWT?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I get this question a lot from other devs on forums and in Discord servers. Here&#8217;s the honest answer based on what I&#8217;ve actually run in production.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Passport is built for full OAuth2 \u2014 think &#8220;Login with Google&#8221; style flows where you&#8217;re issuing tokens to third-party apps you don&#8217;t control. It&#8217;s powerful, but it&#8217;s also heavier than most projects need. I used it once for a project that needed proper OAuth2 grants, and setting it up took an entire afternoon just to get the client credentials flow working correctly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Plain JWT packages are lightweight, but you&#8217;re on your own for revocation, refresh logic, and a dozen little edge cases. I tried this route for a side project and ended up writing my own blacklist table anyway \u2014 at which point, what&#8217;s even the point of avoiding a framework tool?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sanctum sits in the middle. It&#8217;s made specifically for two situations:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A single-page app or mobile app that&#8217;s basically &#8220;first-party&#8221; \u2014 you own both ends.<\/li>\n\n\n\n<li>Simple token-based APIs where you don&#8217;t need the full OAuth2 dance.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">For probably 90% of the APIs I&#8217;ve built for clients, that&#8217;s exactly the situation. So that&#8217;s what we&#8217;re doing here.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What you&#8217;ll need before starting<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Laravel 11 installed (Sanctum ships with the API starter kit now, which is a nice change from Laravel 10 where you had to install it manually every time)<\/li>\n\n\n\n<li>PHP 8.2 or higher<\/li>\n\n\n\n<li>Composer<\/li>\n\n\n\n<li>A tool to test API calls \u2014 I use Postman, but Insomnia or even plain <code>curl<\/code> works fine too<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;re starting a brand-new project, run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>laravel new my-api --api<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That <code>--api<\/code> flag is new-ish and genuinely useful. It scaffolds the project with Sanctum already wired in, instead of you doing it by hand.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;re adding this to an existing app (which is what I usually do), install it manually:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>composer require laravel\/sanctum<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then publish the config and migration:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>php artisan vendor:publish --provider=\"Laravel\\Sanctum\\SanctumServiceProvider\"\nphp artisan migrate<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That migration creates the <code>personal_access_tokens<\/code> table, which is where all your API tokens actually live. Worth peeking at it once just so you know what&#8217;s happening under the hood \u2014 it&#8217;s not magic, it&#8217;s just a database table with a hashed token column.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Prep your User model<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Open up <code>app\/Models\/User.php<\/code> and add the trait:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>use Laravel\\Sanctum\\HasApiTokens;\n\nclass User extends Authenticatable\n{\n    use HasApiTokens, Notifiable;\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This one line is what gives your user model access to methods like <code>createToken()<\/code> and <code>tokens()<\/code>. Skip this step and you&#8217;ll get a confusing &#8220;method does not exist&#8221; error later \u2014 I&#8217;ve done this more than once when copy-pasting between projects.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Build a login endpoint that actually returns a token<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s where a lot of tutorials get lazy and just show you the token part without the login logic. Let&#8217;s do it properly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In <code>routes\/api.php<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>use App\\Http\\Controllers\\AuthController;\n\nRoute::post('\/login', &#91;AuthController::class, 'login']);\nRoute::post('\/register', &#91;AuthController::class, 'register']);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then in your <code>AuthController<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public function login(Request $request)\n{\n    $request-&gt;validate(&#91;\n        'email' =&gt; 'required|email',\n        'password' =&gt; 'required',\n    ]);\n\n    $user = User::where('email', $request-&gt;email)-&gt;first();\n\n    if (! $user || ! Hash::check($request-&gt;password, $user-&gt;password)) {\n        return response()-&gt;json(&#91;\n            'message' =&gt; 'Invalid credentials'\n        ], 401);\n    }\n\n    $token = $user-&gt;createToken('mobile-app-token')-&gt;plainTextToken;\n\n    return response()-&gt;json(&#91;\n        'user' =&gt; $user,\n        'token' =&gt; $token,\n    ]);\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That <code>'mobile-app-token'<\/code> string isn&#8217;t just decoration \u2014 it&#8217;s the token&#8217;s name, and it matters more than you&#8217;d think. If your app is used from multiple devices, name the token something identifiable, like <code>iphone-13-jane<\/code> or based on a device ID you pass in from the client. Later, when a user wants to log out of &#8220;just this device,&#8221; you&#8217;ll be glad you can tell tokens apart.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Protect your routes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Now the actual security part. In <code>routes\/api.php<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Route::middleware('auth:sanctum')-&gt;group(function () {\n    Route::get('\/user', function (Request $request) {\n        return $request-&gt;user();\n    });\n\n    Route::apiResource('posts', PostController::class);\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Anything inside that group now requires a valid token in the <code>Authorization: Bearer {token}<\/code> header. No token, no access. This is the part I initially got wrong \u2014 I forgot to wrap my routes in the middleware group and spent a solid twenty minutes wondering why anyone could hit my &#8220;protected&#8221; endpoints without logging in at all.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: Test it like a real client would<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Fire up Postman. Hit your login endpoint, grab the token from the response, then make a request to <code>\/api\/user<\/code> with the header:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Authorization: Bearer 1|xxxxxxxxxxxxxxxxxxxxx<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If it works, you&#8217;ll get back the user&#8217;s data. If you forget the header, or send a garbage token, you should get a clean 401. This is a good moment to actually test the failure case too \u2014 a lot of devs only test the happy path and never check what happens when the token is wrong or missing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5: Logging out (revoking tokens)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is the part my original homemade system completely botched. With Sanctum, revoking a token is one line:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public function logout(Request $request)\n{\n    $request-&gt;user()-&gt;currentAccessToken()-&gt;delete();\n\n    return response()-&gt;json(&#91;'message' =&gt; 'Logged out']);\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Want to log a user out of every device? That&#8217;s just as simple:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$request-&gt;user()-&gt;tokens()-&gt;delete();<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">I added a &#8220;log out of all devices&#8221; button to one client&#8217;s account settings page using exactly that line, and it took maybe five minutes including the frontend button.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Token abilities: the feature almost nobody uses (but should)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Sanctum lets you scope what a token can actually do, using abilities:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$token = $user-&gt;createToken('mobile-app-token', &#91;'posts:read'])-&gt;plainTextToken;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then in your controller or route middleware, you check for it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if ($request-&gt;user()-&gt;tokenCan('posts:read')) {\n    \/\/ allowed\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">I use this for a client whose app has both a regular user role and a limited &#8220;read-only&#8221; API integration for a partner company. Instead of building a whole separate permissions system, I just issue tokens with different abilities. Way less code, way fewer bugs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">SPA authentication is a different beast<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Quick heads up if you&#8217;re building a single-page app (React, Vue, whatever) that lives on the same domain, or a subdomain, as your Laravel backend \u2014 you don&#8217;t actually want token auth at all. Sanctum has a separate &#8220;SPA mode&#8221; that uses cookies and CSRF protection instead, which is more secure for that specific setup.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This trips people up constantly because Sanctum&#8217;s name gets attached to both use cases. If your frontend and backend share a root domain, go read the SPA authentication section of the docs before you build the token flow \u2014 it&#8217;s genuinely a different setup with <code>EnsureFrontendRequestsAreStateful<\/code> middleware and cookie config, not just tokens in headers.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Mistakes I&#8217;ve made (so you don&#8217;t have to)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Not setting token expiration.<\/strong> By default, Sanctum tokens don&#8217;t expire. For most internal tools that&#8217;s fine, but for anything customer-facing, I now set an expiration in <code>config\/sanctum.php<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>'expiration' =&gt; 60 * 24 * 7, \/\/ 7 days, in minutes<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Storing the token in localStorage on a web app.<\/strong> I did this once early on for a web dashboard and later switched to the cookie-based SPA approach after reading more about XSS risks. For mobile apps, secure device storage (like Keychain on iOS or Keystore on Android) is the right call, not localStorage-style plain storage.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Forgetting rate limiting.<\/strong> Sanctum handles authentication, not throttling. I always add Laravel&#8217;s built-in throttle middleware to login and register routes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Route::post('\/login', &#91;AuthController::class, 'login'])\n    -&gt;middleware('throttle:5,1');<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s five attempts per minute. Without it, your login endpoint is basically an open invitation for brute-force attempts.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Returning the full user model without thinking about it.<\/strong> Early on I returned <code>$user<\/code> directly from several endpoints, which meant password hashes and other fields I didn&#8217;t want exposed were technically in the response (Laravel hides the password field by default, but other sensitive columns aren&#8217;t automatically hidden). Now I use API Resources (<code>php artisan make:resource UserResource<\/code>) to control exactly what gets returned. It&#8217;s a small extra step that&#8217;s saved me from accidentally leaking data more than once.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">A quick real-world example<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For that original client project, the final setup ended up being:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Regular users get a token on login, scoped with abilities based on their subscription tier<\/li>\n\n\n\n<li>A partner integration gets a long-lived token with read-only abilities, generated manually through an admin panel<\/li>\n\n\n\n<li>All login attempts are throttled<\/li>\n\n\n\n<li>Tokens expire after 30 days of inactivity, refreshed automatically when the app makes a request<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">It&#8217;s not fancy. It&#8217;s just Sanctum, used the way it&#8217;s meant to be used, with a few extra guardrails around it. The API has been running for over a year now without a single auth-related incident, which honestly feels like a small miracle given how many ways I managed to mess up my first attempt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;re on the fence about which auth package to reach for in Laravel 11, and you&#8217;re not building a full OAuth2 marketplace of third-party apps, just start with Sanctum. It covers the actual problems you&#8217;ll run into \u2014 logins, tokens, revocation, scoping \u2014 without dragging in complexity you don&#8217;t need yet. You can always layer on something heavier later if your API genuinely outgrows it, but I&#8217;d bet for most projects, it won&#8217;t.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>How to Build a Secure REST API in Laravel 11 Using Sanctum A few months back, a client asked me to turn their existing Laravel web app into something that could also power a mobile app. Simple enough, right? Just expose some endpoints, slap on some authentication, done. Except the first version I shipped had a problem. I used a basic token system I hacked together myself \u2014 stuff a random string in the database, check it on every request. It worked, until it didn&#8217;t. Tokens never expired, there was no way to revoke a single device without logging everyone out, and I genuinely lost a few hours debugging why a user&#8217;s session on their phone kept dying randomly (turns out I was regenerating the token on every login, which invalidated the old one while the app still had it cached). That&#8217;s when I actually sat down and used Sanctum properly instead of reinventing it. And honestly, once it clicked, I felt a little silly for not using it from day one. If you&#8217;re building an API for a mobile app, an SPA, or just want third-party apps to talk to your Laravel backend, this is the writeup I wish I&#8217;d had before I started. Why Sanctum and not Passport or plain JWT? I get this question a lot from other devs on forums and in Discord servers. Here&#8217;s the honest answer based on what I&#8217;ve actually run in production. Passport is built for full OAuth2 \u2014 think &#8220;Login with Google&#8221; style flows where you&#8217;re issuing tokens to third-party apps you don&#8217;t control. It&#8217;s powerful, but it&#8217;s also heavier than most projects need. I used it once for a project that needed proper OAuth2 grants, and setting it up took an entire afternoon just to get the client credentials flow working correctly. Plain JWT packages are lightweight, but you&#8217;re on your own for revocation, refresh logic, and a dozen little edge cases. I tried this route for a side project and ended up writing my own blacklist table anyway \u2014 at which point, what&#8217;s even the point of avoiding a framework tool? Sanctum sits in the middle. It&#8217;s made specifically for two situations: For probably 90% of the APIs I&#8217;ve built for clients, that&#8217;s exactly the situation. So that&#8217;s what we&#8217;re doing here. What you&#8217;ll need before starting If you&#8217;re starting a brand-new project, run: That &#8211;api flag is new-ish and genuinely useful. It scaffolds the project with Sanctum already wired in, instead of you doing it by hand. If you&#8217;re adding this to an existing app (which is what I usually do), install it manually: Then publish the config and migration: That migration creates the personal_access_tokens table, which is where all your API tokens actually live. Worth peeking at it once just so you know what&#8217;s happening under the hood \u2014 it&#8217;s not magic, it&#8217;s just a database table with a hashed token column. Step 1: Prep your User model Open up app\/Models\/User.php and add the trait: This one line is what gives your user model access to methods like createToken() and tokens(). Skip this step and you&#8217;ll get a confusing &#8220;method does not exist&#8221; error later \u2014 I&#8217;ve done this more than once when copy-pasting between projects. Step 2: Build a login endpoint that actually returns a token Here&#8217;s where a lot of tutorials get lazy and just show you the token part without the login logic. Let&#8217;s do it properly. In routes\/api.php: Then in your AuthController: That &#8216;mobile-app-token&#8217; string isn&#8217;t just decoration \u2014 it&#8217;s the token&#8217;s name, and it matters more than you&#8217;d think. If your app is used from multiple devices, name the token something identifiable, like iphone-13-jane or based on a device ID you pass in from the client. Later, when a user wants to log out of &#8220;just this device,&#8221; you&#8217;ll be glad you can tell tokens apart. Step 3: Protect your routes Now the actual security part. In routes\/api.php: Anything inside that group now requires a valid token in the Authorization: Bearer {token} header. No token, no access. This is the part I initially got wrong \u2014 I forgot to wrap my routes in the middleware group and spent a solid twenty minutes wondering why anyone could hit my &#8220;protected&#8221; endpoints without logging in at all. Step 4: Test it like a real client would Fire up Postman. Hit your login endpoint, grab the token from the response, then make a request to \/api\/user with the header: If it works, you&#8217;ll get back the user&#8217;s data. If you forget the header, or send a garbage token, you should get a clean 401. This is a good moment to actually test the failure case too \u2014 a lot of devs only test the happy path and never check what happens when the token is wrong or missing. Step 5: Logging out (revoking tokens) This is the part my original homemade system completely botched. With Sanctum, revoking a token is one line: Want to log a user out of every device? That&#8217;s just as simple: I added a &#8220;log out of all devices&#8221; button to one client&#8217;s account settings page using exactly that line, and it took maybe five minutes including the frontend button. Token abilities: the feature almost nobody uses (but should) Sanctum lets you scope what a token can actually do, using abilities: Then in your controller or route middleware, you check for it: I use this for a client whose app has both a regular user role and a limited &#8220;read-only&#8221; API integration for a partner company. Instead of building a whole separate permissions system, I just issue tokens with different abilities. Way less code, way fewer bugs. SPA authentication is a different beast Quick heads up if you&#8217;re building a single-page app (React, Vue, whatever) that lives on the same domain, or a subdomain, as your Laravel backend \u2014 you don&#8217;t actually want token auth at all. Sanctum has a separate &#8220;SPA mode&#8221; that uses cookies and CSRF protection instead, which is more secure for that specific setup. This trips people up constantly because Sanctum&#8217;s name gets attached to both use cases. If your frontend and backend share a root domain, go read the SPA authentication section of the docs before you build the token flow \u2014 it&#8217;s genuinely a different setup with EnsureFrontendRequestsAreStateful middleware and cookie config, not just tokens in headers. Mistakes I&#8217;ve made (so you don&#8217;t have to) Not setting token expiration. By default, Sanctum tokens don&#8217;t expire. For most internal tools that&#8217;s fine, but for anything customer-facing, I now set an expiration in config\/sanctum.php: Storing the token in localStorage on a web app. I did this once early on for a web dashboard and later switched to the cookie-based SPA approach after reading more about XSS risks. For mobile apps, secure device storage (like Keychain on iOS or Keystore on Android) is the right call, not localStorage-style plain storage. Forgetting rate limiting. Sanctum handles authentication, not throttling. I always add Laravel&#8217;s built-in throttle middleware to login and register routes: That&#8217;s five attempts per minute. Without it, your login endpoint is basically an open invitation for brute-force attempts. Returning the full user model without thinking about it. Early on I returned $user directly from several endpoints, which meant password hashes and other fields I didn&#8217;t want exposed were technically in the response (Laravel hides the password field by default, but other sensitive columns aren&#8217;t automatically hidden). Now I use API Resources (php artisan make:resource UserResource) to control exactly what gets returned. It&#8217;s a small extra step that&#8217;s saved me from accidentally leaking data more than once. A quick real-world example For that original client project, the final setup ended up being: It&#8217;s not fancy. It&#8217;s just Sanctum, used the way it&#8217;s meant to be used, with a few extra guardrails around it. The API has been running for over a year now without a single auth-related incident, which honestly feels like a small miracle given how many ways I managed to mess up my first attempt. If you&#8217;re on the fence about which auth package to reach for in Laravel 11, and you&#8217;re not building a full OAuth2 marketplace of third-party apps, just start with Sanctum. It covers the actual problems you&#8217;ll run into \u2014 logins, tokens, revocation, scoping \u2014 without dragging in complexity you don&#8217;t need yet. You can always layer on something heavier later if your API genuinely outgrows it, but I&#8217;d bet for most projects, it won&#8217;t.<\/p>\n","protected":false},"author":1,"featured_media":2711,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[44],"tags":[],"class_list":["post-2710","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-coding"],"_links":{"self":[{"href":"https:\/\/codexprime.cloud\/index.php?rest_route=\/wp\/v2\/posts\/2710","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codexprime.cloud\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codexprime.cloud\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codexprime.cloud\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codexprime.cloud\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2710"}],"version-history":[{"count":2,"href":"https:\/\/codexprime.cloud\/index.php?rest_route=\/wp\/v2\/posts\/2710\/revisions"}],"predecessor-version":[{"id":2715,"href":"https:\/\/codexprime.cloud\/index.php?rest_route=\/wp\/v2\/posts\/2710\/revisions\/2715"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/codexprime.cloud\/index.php?rest_route=\/wp\/v2\/media\/2711"}],"wp:attachment":[{"href":"https:\/\/codexprime.cloud\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2710"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codexprime.cloud\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2710"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codexprime.cloud\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2710"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}