Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

NickSmithTech's avatar

Sanctum for a REST API?

My application has a need to issue auth tokens per company so our customers can consume a REST api.

This cannot be related to the user accounts (which seems to the main drive of this package), as those may change over time and we can't have the api break when they have a change in the sales staff.

I've looked into using Sanctum instead of rolling our own system, but there are a few items of confusion. I can add HasApiTokens to our Company model, and I've gotten as far as successfully authenticating with the token, but to get the name of the company, I've had to use auth()->user()->name which is semantically confusing. Is there a better way of handling this?

Plus, if the bearer token is wrong, instead of the 403 I would expect, I get "Route [login] not defined" which makes no sense at all.

The documentation for this is not clear on how I fix this obvious use for an api package.

0 likes
9 replies
martinbean's avatar

@nicksmithtech You’d be better off using Passport, and creating an OAuth client for each “company” that is going to use your API. Each company would then obtain access tokens via their client using their client’s client ID and secret.

NickSmithTech's avatar

The direction we were given is that a company will be issued an api token in the user interface and we are sticking with that.

Jonjie's avatar

With your case, it is recommended for you to use Passport. It will be the best option, IMO.

NickSmithTech's avatar

Two suggestions to use Passport instead so far....

Forgive me as I am not in the office for a few hours, but why would I not use a system that provides for "simple, token based APIs" in this case?

OussamaMater's avatar
Level 37

Hello bud, just to clear the confusion:

  • If the token is incorrect, you should expect a 401 Unauthorized response, not a 403 Forbidden response.
  • You are getting Route [login] not defined., which is an expected behaviour. By default, Laravel attempts to redirect the user to the login route, which in your case is not defined (you don't have any route with ->name('login')). To get a JSON response instead, you need to specify the header Accept: application/json, this will return the message {"message": "Unauthenticated."} instead of a redirect. Below is what the docs state here.

Additionally, you should ensure that you send the Accept: application/json header and either the Referer or Origin header with your request.

Now regarding the choice between Passport and Sanctum: If your application requires OAuth 2, or at any point you will have some kind of a centralised Authserver, where all your services are redirected, or you are building an API that you would love to be integrated with other systems without problems Passport is the way to go, this is because most of the devs are familiar with certain OAuth2 grant flows, when authenticating against 3rd party APIs and it helps maintain a standardized approach.

If your API is simple enough and is focused on issuing tokens, Sanctum is the way to go.

And please keep in mind, this is my opinion, others definitely have their reasons for suggesting Passport.

1 like
NickSmithTech's avatar

@OussamaMater Thank you for this. You actually answered my question. Yes, 401 not 403 and the Accept header (danmit it lol).

1 like
Lumethys's avatar

@NickSmithTech you can actually force all /api path to return JSON if you want.

In the ExceptionHandler, there is a method shouldReturnJson()

By default, it will return $request->expectsJson(), which is a utility method to check the Accept header

But you can add "if request comes from API path, return JSON also":

public function shouldReturnJson(Request $request)
{
     return $request->expectsJson || Str::of($request->path())->startsWith('/api');
}

Be aware that the auth middleware actually throw a Unauthenticated Exception. The Handler catch this Exception and redirect to login if request doesnt expects JSON.

Please or to participate in this conversation.