Skip to Content
API v2Authentication

Authentication

Overview

Housr API v2 uses a custom Legacy JWT Guard to authenticate requests. This guard is compatible with JWT tokens issued by the v1 API, allowing the mobile app and web app to authenticate against both APIs with the same token.

How It Works

LegacyJwtGuard

The custom guard is implemented at app/Auth/Guards/LegacyJwtGuard.php. It:

  1. Extracts a JWT from the request (see token sources below)
  2. Decodes it using firebase/php-jwt with the shared JWT_KEY secret (HS256)
  3. Retrieves the user ID from sub or data.id in the payload
  4. Loads the User model from the database

Token sources (checked in order):

  1. jwt form/query parameter (legacy v1 compatibility)
  2. Authorization: Bearer <token> header
  3. Raw Authorization header (without “Bearer ” prefix)

Token expiry: The guard sets JWT::$leeway = PHP_INT_MAX before decoding, which effectively disables expiry checking. This matches v1 behaviour where tokens are long-lived.

Guard Registration

The guard is registered in app/Providers/LegacyAuthServiceProvider.php:

Auth::extend('legacy-jwt', function ($app, $name, $config) { return new LegacyJwtGuard( Auth::createUserProvider($config['provider']), $app->make('request') ); });

And configured in config/auth.php:

'guards' => [ 'legacy-jwt' => [ 'driver' => 'legacy-jwt', 'provider' => 'users', ], ],

Using Auth in Routes

Required authentication

Apply the auth:legacy-jwt middleware to require a valid JWT:

Route::middleware('auth:legacy-jwt')->group(function () { Route::get('/protected-endpoint', [Controller::class, 'method']); });

Returns 401 if no valid token is provided.

Optional authentication

Use the OptionalLegacyJwtAuth middleware when the endpoint works for both authenticated and unauthenticated users, but returns extra data for logged-in users:

Route::middleware(OptionalLegacyJwtAuth::class)->group(function () { Route::get('/houses', [HouseController::class, 'index']); });

This middleware (app/Http/Middleware/OptionalLegacyJwtAuth.php):

  • Sets the default guard to legacy-jwt
  • Attempts to resolve the user from the token
  • Allows the request through regardless of auth result
  • The controller can then check $request->user() for null vs authenticated user

Webhook/service-to-service authentication

For incoming webhooks (e.g., HubSpot), use ValidateAuthorizationHeader middleware. This checks the Authorization header against a shared secret (AUTHORIZATION_KEY env var):

Route::middleware(ValidateAuthorizationHeader::class)->group(function () { Route::post('/webhook', [WebhookController::class, 'handle']); });

External API Authentication

The External module (Modules/External) implements a separate OAuth-style token system for third-party API clients:

  • RequireApiKey middleware: Validates API key from the ApiClient model
  • RequireAuthToken middleware: Validates bearer token issued by AuthController
  • RequireScope middleware: Checks that the authenticated client has the required scope

External clients authenticate via:

  1. Exchange API key for a token via the auth endpoint
  2. Use the token as a Bearer token on subsequent requests
  3. Scoped access controls which endpoints the client can call

Firebase

The app/Services/FirebaseService.php provides Firebase Realtime Database access (not used for auth — that is handled by the v1 API). Firebase credentials are loaded from the FIREBASE_CREDENTIALS env var (JSON string).

app/Services/FirebaseMessagingService.php handles push notifications via Firebase Cloud Messaging.

Last updated on