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

yordie's avatar

API Authentication and Airlock/Passport

Hi,

I have a few questions on API's and authentication, possibly to do with the new Airlock package, but I'm really just looking for some general advice on the best way to authenticate my API, I'm new to this, so bare with me!

I have a platform where my customers sign in at clubs.domain.test, and their customers sign in at my.domain.test. Both subdomains are going to start to utilise an API at api.domain.test, neither of the apps on clubs. and my. are SPAs, they are good ol' server apps routed through and view rendered by laravel, and you sign in the traditional way. However, some of the resource index pages will use Vue to call on the API and gather data to be displayed on these pages.

I've currently implemented authentication of the API requests using the really basic way outlined at https://laravel.com/docs/6.x/api-authentication. All users have a pre-generated API token that is un-hashed. When a user is signed in their token is passed into a javascript object that's placed on the window so it can be assigned to a default axios header as the "Bearer" of each request. On the backend I just use the auth:api middleware provided by laravel to authenticate the routes, as well as the fruitcake/laravel-cors package with default config for CORS. I'll be honest, this works fine, but it niggles me that having API tokens feels overkill for my situation and I'd prefer to not expose the users API token client side.

Couple of initial questions:

  1. Are there any major security vulnerabilities with storing un-hashed tokens and passing these client side the way I've described above?
  2. And is there a better way I could be authenticating my API for such a basic basic set-up or should I be satisfied with this implementation?

These thoughts have lead me onto attempting to install Airlock and (what I think will be) improving my API authentication. I saw Airlock and thought I'd much rather authenticate using a method that is preconfigured in laravel (cookie based session auth), without having to store API tokens and have them exposed on the client. I've followed the guide at https://laravel.com/docs/master/airlock and on Mohamed's website https://divinglaravel.com/authentication-and-laravel-airlock, both great resources that are really well written and easy to follow. To confirm the set-up:

In my env file I have AIRLOCK_STATEFUL_DOMAINS="clubs.domain.test,my.domain.test". In my Kernel I have the EnsureFrontendRequestsAreStateful middleware in the api group. I have the fruitcake/laravel-cors package installed and "supports_credentials" set to true. In my JS I have window.axios.defaults.withCredentials = true;. In my env file I have SESSION_DOMAIN=".domain.test". In my api routes file I've set my route group middleware to auth:airlock.

With all these settings configured as per the guides I can't seem to get passed the initial OPTIONS request, the GET/POST request isn't getting performed, which I guess points to a CORS problem?

Questions on Airlock:

  1. Could this (CORS) issue be down to the fact that I'm not performing the get initial request to the "/airlock/csrf-cookie" endpoint as my users sign in the traditional way with a full server request rather than through a SPA, or is that unrelated?
  2. Ideally, I don't want to configure the session cookie domain to support any subdomain as I don't want a user who signs into the clubs. subdomain to also be signed into the my. subdomain. Is Airlock un-usable without the session cookie domain configuration supporting any subdomain?
  3. Am I wasting my time with Airlock and is it overkill for such a simple set-up?

Thanks for looking and reading (if you've made it this far!),

Sam

0 likes
7 replies
yordie's avatar

Hey pom,

Thanks for the response, I hadn't realised Passport offered this feature so thanks for pointing that out, I'll give it a go and see if it works for us.

Best, Sam

yordie's avatar

@pom so that's got me a step further I'm just getting a 401 unauthenticated response now from the GET request so CORS is no longer an issue, any ideas?

I can see the laravel_token cookie is set on the page and in the API request I'm sending the X-CSRF-TOKEN and X-Requested-With headers using:

window.axios.defaults.headers.common[
    'X-CSRF-TOKEN'
] = document.head.querySelector('meta[name="csrf-token"]').content;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

Do you know if the laravel_token cookie should be sent in the API request or is this just used server side?

Cheers

Sam

pom's avatar

Do you have the relevant meta tag in your template?

<meta name="csrf-token" content="{{ csrf_token() }}">

How is your endpoint protected?

yordie's avatar

Thanks for sticking with me on this @pom, yep the meta tag is there and this is included in the request headers:

Host: api.domain.test
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:73.0) Gecko/20100101 Firefox/73.0
Accept: application/json, text/plain, */*
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate
X-CSRF-TOKEN: E3tRSJ9Imp... (obfuscated)
X-Requested-With: XMLHttpRequest
Origin: http://clubs.domain.test
Connection: keep-alive
Referer: http://clubs.domain.test/classes
Cache-Control: max-age=0

The endpoint is just protected with the auth:api middleware, nothing else:

Route::domain(config('app.api_url'))->namespace('Api')->name('api.')->group(function () {
    Route::group(['middleware' => ['auth:api']], function () {
        Route::get('classes', 'ClassController@index')->name('classes.index');

Pretty simple implementation, it works when removing the middleware group obviously, so the issue must be isolated to this middleware and passport's authenticating?

pom's avatar

What results do you get with a simple route?

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});
yordie's avatar

FInally managed to test this properly with a fresh install of laravel and the simplest of routes you've suggested @pom. Everything worked fine for Passport on subdomains when I included the following, as outlined in the Airlock docs under CORS & Cookies.

CORS config:

'supports_credentials' => true,

JS:

axios.defaults.withCredentials = true;

.env:

SESSION_DOMAIN=.domain.test

I imagine this will also work fine for Airlock as well so not sure what my initial issue was with that, but I'll stick with Passport now as this is tried and tested. I've not found a way of getting either to work without setting the session domain, which makes sense as you need the session to persist across subdomains, so you'll just need to live with the user being logged in to all domains.

In my case one of my subdomains requires another session property so I've got some middleware to check for the existence of this and to sign out the user if it doesn't exist. I have another middleware that does the opposite of this for the subdomain that doesn't require it, so the SESSION_DOMAIN isn't an issue anymore.

Thanks for your help with this one pom, much appreciated.

Sam

Please or to participate in this conversation.