PHPackages                             anastosios/firauth - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. anastosios/firauth

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

anastosios/firauth
==================

JWT + Cookie Auth Package

v1.0.1(8mo ago)0705MITPHPPHP ^7.3|^8.0

Since Sep 3Pushed 7mo agoCompare

[ Source](https://github.com/anas16299/firauth)[ Packagist](https://packagist.org/packages/anastosios/firauth)[ RSS](/packages/anastosios-firauth/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (1)Versions (3)Used By (0)

FirAuth — JWT / Cookie Authentication for Multi‑Service Laravel
===============================================================

[](#firauth--jwt--cookie-authentication-for-multiservice-laravel)

FirAuth provides a clean, extensible authentication layer for **multi‑service** Laravel setups:

- **JWT (RS256 recommended)** — sign on the **main** service, verify on **consumer** services
- **Cookie or Bearer transport** — HttpOnly cookie across subdomains, or `Authorization: Bearer`
- **Single‑session via Redis** — `session_id` claim validated across services
- **Pre / Post checkers** — plug custom gates (integration credentials, password reset, MFA, timezone, …)
- **Drop‑in routes** — `/firauth/login`, `/firauth/refresh`, `/firauth/logout`
- **Stateless on consumers** — `request()->user()` is built from JWT claims (no DB hit)

> Package namespace used below: **`cs/auth`** (PSR‑4 root: `Firauth\auth\`).
> Adjust to your vendor/name if you publish under another vendor on Packagist.

---

Table of Contents
-----------------

[](#table-of-contents)

- [Requirements](#requirements)
- [Installation](#installation)
- [Configuration](#configuration)
    - [Environment variables quick reference](#environment-variables-quick-reference)
    - [JWT (RS256 vs HS256)](#jwt-rs256-vs-hs256)
    - [Cookie transport](#cookie-transport)
    - [Redis session binding](#redis-session-binding)
    - [Routes toggles](#routes-toggles)
- [Quick Start](#quick-start)
    - [Main service](#main-service)
    - [Consumer services](#consumer-services)
- [Routes \\u0026 Middlewares](#routes--middlewares)
- [Overriding *only* the Login action (best practice)](#overriding-only-the-login-action-best-practice)
- [Pre / Post Checkers](#pre--post-checkers)
- [Refresh Best Practice](#refresh-best-practice)
- [Logout Behavior](#logout-behavior)
- [Testing Recipes](#testing-recipes)
- [Postman Tips](#postman-tips)
- [Troubleshooting](#troubleshooting)
- [Security Notes](#security-notes)
- [Optional Artisan Commands](#optional-artisan-commands)
- [License](#license)

---

Requirements
------------

[](#requirements)

- PHP 8.0+ (Laravel 8/9/10/11 supported)
- Redis (recommended for single‑session)
- [tymon/jwt-auth](https://github.com/tymondesigns/jwt-auth) (used internally)
- OpenSSL if using RS256

---

Installation
------------

[](#installation)

```
composer require anastosios/firauth

# Publish config
php artisan vendor:publish --tag=firauth-config

# Interactive setup (main vs consumer, cookie/jwt, keys, redis, etc.)
php artisan firauth:install
```

The installer will ask you:

- Is this the **MAIN** service? (issues tokens and exposes /firauth routes)
- Strategy: `cookie` (HttpOnly) or `jwt`
- Redis connection TTL (seconds) for sessions
- JWT: **RS256** (preferred) or HS256, along with TTLs and key paths

> **Recommendation**: Use **RS256** for multi‑service , private key on main only, public key on consumers.

---

Configuration
-------------

[](#configuration)

### Environment variables quick reference

[](#environment-variables-quick-reference)

```
# Core
FIRAUTH_MAIN_SERVICE=true|false
FIRAUTH_STRATEGY=cookie|jwt

# Cookie
FIRAUTH_COOKIE_NAME=FIRAUTH_token
FIRAUTH_COOKIE_DOMAIN=.your-domain.com
FIRAUTH_COOKIE_SECURE=true
FIRAUTH_COOKIE_HTTP_ONLY=true
FIRAUTH_COOKIE_SAMESITE=Lax   # Lax|Strict|None (use None with HTTPS only)
FIRAUTH_COOKIE_LIFETIME=60    # minutes

# Redis session binding
FIRAUTH_SESSION_REDIS=session
FIRAUTH_SESSION_KEY_PREFIX=session_
FIRAUTH_SESSION_TTL=0         # 0=no expiry at Redis level

# JWT behavior
FIRAUTH_REMEMBER_DAYS=90
FIRAUTH_RENEW_WINDOW=600       # refresh only if  **Tip:** For local dev cookie tests, map a host in `/etc/hosts` (e.g., `127.0.0.1 api.your-domain.test`) and use `FIRAUTH_COOKIE_DOMAIN=.your-domain.test` so the browser/Postman attaches cookies correctly.

### JWT (RS256 vs HS256)

[](#jwt-rs256-vs-hs256)

- **RS256** (recommended): sign with **private key** on the main service; verify with **public key** on all consumers. Keys are read via `file://` URIs.
- **HS256**: shared secret across services (simpler but less ideal for multi‑service separation).

### Cookie transport

[](#cookie-transport)

When `FIRAUTH_STRATEGY=cookie` the package sets an `HttpOnly` cookie named `firauth_token`. Configure domain/samesite/secure flags in config/env. Consumers under subdomains will automatically receive the cookie.

> The middleware prefers the cookie in cookie strategy; otherwise it falls back to the `Authorization: Bearer` header.

### Redis session binding

[](#redis-session-binding)

Single‑session across services is enforced by a `session_id` claim bound to Redis:

- On login, a `session_id` is included in the JWT (either from your `getJWTCustomClaims()` or generated).
- Middleware validates `session_id` against Redis (`FIRAUTH_SESSION_REDIS` `FIRAUTH_SESSION_KEY_PREFIX`).
- Logout revokes the Redis entry and blacklists the token.

### Routes toggles

[](#routes-toggles)

You can selectively expose package routes:

```
FIRAUTH_ROUTES_EXPOSE_LOGIN=true|false
FIRAUTH_ROUTES_EXPOSE_REFRESH=true|false
FIRAUTH_ROUTES_EXPOSE_LOGOUT=true|false
```

Disable only login to override it in your app while keeping refresh/logout from the package.

---

Quick Start
-----------

[](#quick-start)

### Main service

[](#main-service)

1. Run the installer and choose **Main service = yes**.
2. Set RS256 keys using `file://` paths in `.env` (or use HS256 with `JWT_SECRET`).
3. If using cookies, configure `FIRAUTH_COOKIE_DOMAIN` for your apex/subdomains.
4. Test login: ```
    curl -i -X POST https://api.your-domain.com/firauth/login \
      -H 'Content-Type: application/json' \
      -d '{"email":"user@example.com","password":"secret"}'
    ```

    You should see a JSON response and a `Set-Cookie: firauth_token=...` header (cookie strategy).

### Consumer services

[](#consumer-services)

1. Run the installer and choose **Main service = no**.
2. Configure **only the public key** for RS256 (`JWT_PUBLIC_KEY`).
3. Protect routes with the middleware: ```
    Route::middleware('firauth')->group(function () {
        Route::get('/me', fn() => response()->json(request()->user()));
        // your protected routes…
    });
    ```

The middleware will verify signature/expiry/nbf/blacklist, validate the Redis `session_id`, and expose a dynamic `request()->user()` built from claims (no DB hit).

---

Routes &amp; Middlewares
------------------------

[](#routes--middlewares)

**Routes (package):**

- `POST /firauth/login` — issues the JWT (+ `Set-Cookie` in cookie mode)
- `POST /firauth/refresh` — refreshes token; middleware: `firauth.token` (token presence; accepts expired token)
- `POST /firauth/logout` — blacklists current token revokes Redis session; middleware: `firauth` (full check)

**Middlewares:**

- `firauth` — Full verification (signature, expiry, blacklist) Redis `session_id` validation; binds `request()->user()` from claims.
- `firauth.token` — Helper that injects cookie token into the `Authorization` header if missing (used for `/refresh`).

---

Overriding *only* the Login action (best practice)
--------------------------------------------------

[](#overriding-only-the-login-action-best-practice)

You can replace only the login endpoint and reuse the package’s services:

1. Disable the package login route in `.env`:

    ```
    FIRAUTH_ROUTES_EXPOSE_LOGIN=false
    ```
2. Create a **subclass** of the base controller and override `login()`:

    ```
    // app/Http/Controllers/Auth/CustomAuthController.php
    namespace App\Http\Controllers\Auth;

    use Illuminate\Http\Request;
    use Illuminate\Validation\ValidationException;
    use Firauth\auth\Http\Controllers\AuthController as BaseAuthController;

    class CustomAuthController extends BaseAuthController
    {
        public function login(Request $request)
        {
            $request->validate([
                'email'    => 'required|string',
                'password' => 'required|string',
                'remember' => 'sometimes|boolean',
            ]);

            $rememberDays = $request->boolean('remember')
                ? (int) config('firauth.jwt.remember_me_days')
                : null;

            // Reuse token driver (no direct Tymon calls here)
            $result = $this->tokens->attempt(
                $request->only('email','password'),
                $rememberDays
            );

            if (!$result) {
                throw ValidationException::withMessages(['email' => ['Invalid credentials']])->status(422);
            }

            $claims    = $result['claims'] ?? [];
            $user      = $result['user'];
            $sessionId = $claims['session_id'] ?? null;

            if (config('firauth.jwt.bind_session_in_login', false) && $sessionId) {
                $this->sessions->bind($user['id'], $sessionId);
            }

            // Use the transport to emit JSON + optional Set-Cookie
            return $this->transport->loginResponse($result['token'], $user);
        }
    }
    ```
3. Register only your login route (keep refresh/logout from the package):

    ```
    // routes/api.php
    use App\Http\Controllers\Auth\CustomAuthController;
    Route::post('firauth/login', [CustomAuthController::class, 'login']);
    ```

---

Pre / Post Checkers
-------------------

[](#pre--post-checkers)

Use checkers to inject custom logic **without** touching core auth:

- **Pre checkers** (run first) — may short‑circuit or **bypass JWT** by returning user attributes.
    If a pre checker returns an array, we treat the user as authenticated and **skip everything else** by default.
    If that case still needs post checks, include `['_run_post' => true]` in the returned array.
- **Post checkers** (run last) — final gate **after** JWT \\u002b Redis session validation (or pre opted‑in).
    Perfect for: password‑reset gates (423), tenant disabled, timezone binding, MFA after JWT, etc.
    Return a `Response` to block, or `null` to allow; you may mutate the `$userAttrs` array by reference.

**Contracts:**

```
// Pre — return Response|array|null
interface PreAuthCheckerInterface {
    public function handle(Request $request): \Symfony\Component\HttpFoundation\Response|array|null;
}

// Post — return ?Response; can mutate user attrs by reference
interface PostAuthCheckerInterface {
    public function handle(Request $request, array &$userAttrs): ?\Symfony\Component\HttpFoundation\Response;
}
```

Register your checkers in `config/firauth.php`:

```
'middleware' => [
  'pre'  => [
    // \App\FirAuth\Checkers\IntegrationCredentialsPreChecker::class,
  ],
  'post' => [
    // \App\FirAuth\Checkers\ForcePasswordResetPostChecker::class,
    // \App\FirAuth\Checkers\TimezoneBinderPostChecker::class,
  ],
],
```

---

Refresh Best Practice
---------------------

[](#refresh-best-practice)

- Set a window to refresh **only when near expiry** or expired: ```
    FIRAUTH_RENEW_WINDOW=600   # 10 minutes
    ```
- Behavior at `/firauth/refresh`:
    - If token **far from expiry** → `{ "still_valid": true, "expires_in":  }`
    - If **expired** or **within window** → a **new token** is issued; the old is **blacklisted** immediately.
- To reduce races during refresh (parallel requests): ```
    JWT_BLACKLIST_GRACE_PERIOD=30
    ```

---

Logout Behavior
---------------

[](#logout-behavior)

- `POST /firauth/logout` (middleware: `firauth` full check):
    - Blacklists the current token.
    - Revokes the Redis session entry → **global single‑session kill**.
    - Returns 200 (cookie cleared if cookie strategy).
    - A second logout with the same token returns **401** (blacklisted \\u002b no session).

---

Testing Recipes
---------------

[](#testing-recipes)

**JWT (header)**

```
# Login (get token)
TOKEN=$(curl -s -X POST https://api.your-domain.com/firauth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"secret"}' | jq -r '.data.token')

# Protected route
curl -H "Authorization: Bearer $TOKEN" https://api.your-domain.com/protected

# Refresh
curl -X POST -H "Authorization: Bearer $TOKEN" https://api.your-domain.com/firauth/refresh

# Logout
curl -X POST -H "Authorization: Bearer $TOKEN" https://api.your-domain.com/firauth/logout
```

**Cookie**

```
# Let curl manage cookies automatically (-c to save, -b to send)
curl -c cookies.txt -b cookies.txt -i -X POST https://api.your-domain.com/firauth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"secret"}'

# Refresh / protected / logout
curl -c cookies.txt -b cookies.txt -i -X POST https://api.your-domain.com/firauth/refresh
curl -c cookies.txt -b cookies.txt -i https://api.your-domain.com/protected
curl -c cookies.txt -b cookies.txt -i -X POST https://api.your-domain.com/firauth/logout
```

**Feature tests (Laravel)**

- Login returns token (or sets cookie) \\u002b payload has `exp`
- Refresh near expiry issues a new token; far from expiry returns `still_valid`
- Logout revokes session and blacklists token; second logout is 401

---

Postman Tips
------------

[](#postman-tips)

- **Do not** paste the full `Set‑Cookie` header as your `Cookie` header; send only `Cookie: firauth_token=`.
- In cookie mode, prefer using a host that matches `FIRAUTH_COOKIE_DOMAIN` (e.g., `api.your-domain.test`) so Postman attaches cookies automatically.
- If both cookie *and* `Authorization` header are present, cookie is preferred in cookie strategy (package behavior). Keep your `Authorization` header clean to avoid sending stale tokens.

---

What’s Next (Roadmap)
=====================

[](#whats-next-roadmap)

### 1. Pluggable Session Stores (Not Only Redis)

[](#1-pluggable-session-stores-not-only-redis)

Make the session binding layer driver-based so teams can choose from:

- Redis (default)
- Database
- File
- In-memory / Array (for testing)
- Custom-defined stores

**Goal:** Decouple session handling from Redis to allow for flexible and testable implementations.

---

### 2. Multi-Device Sessions &amp; Revocation UX

[](#2-multi-device-sessions--revocation-ux)

Support **both** `single-session` (default today) and `multi-session` modes.

#### Modes

[](#modes)

- `single`: One active `session_id` per user (current behavior).
- `multi`: Track a **set** of session IDs per user.
    - `revoke(user, session_id)` → removes one device
    - `revoke(user, null)` → removes all sessions for the user

#### Claims

[](#claims)

Add optional claims:

- `device_id`
- `client metadata` (OS, browser, device info)

#### APIs (optional)

[](#apis-optional)

- `GET /firauth/sessions` → List all active devices
- `DELETE /firauth/sessions/{session_id}` → Revoke specific device

#### ⚙ Dev Experience

[](#-dev-experience)

- Artisan command: ```
    php artisan firauth:sessions {userId}
    ```

---

### 3. Pre-Built SSO Adapters (as Pre-Checkers)

[](#3-pre-built-sso-adapters-as-pre-checkers)

Ship optional pre-auth adapters for popular identity providers:

- OAuth2 / OIDC flows:
    - Microsoft
    - Google
    - GitHub.

###  Health Score

34

—

LowBetter than 77% of packages

Maintenance62

Regular maintenance activity

Popularity17

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity42

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 85.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

251d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/93675077?v=4)[anas16299](/maintainers/anas16299)[@anas16299](https://github.com/anas16299)

---

Top Contributors

[![anasalmasri99](https://avatars.githubusercontent.com/u/211709520?v=4)](https://github.com/anasalmasri99 "anasalmasri99 (12 commits)")[![anas16299](https://avatars.githubusercontent.com/u/93675077?v=4)](https://github.com/anas16299 "anas16299 (2 commits)")

### Embed Badge

![Health badge](/badges/anastosios-firauth/health.svg)

```
[![Health](https://phpackages.com/badges/anastosios-firauth/health.svg)](https://phpackages.com/packages/anastosios-firauth)
```

###  Alternatives

[lab404/laravel-impersonate

Laravel Impersonate is a plugin that allows to you to authenticate as your users.

2.3k16.4M48](/packages/lab404-laravel-impersonate)[santigarcor/laratrust

This package provides a flexible way to add Role-based Permissions to Laravel

2.3k5.4M43](/packages/santigarcor-laratrust)[overtrue/laravel-follow

User follow unfollow system for Laravel.

1.2k404.7k5](/packages/overtrue-laravel-follow)[codegreencreative/laravel-samlidp

Make your PHP Laravel application an Identification Provider using SAML 2.0. This package allows you to implement your own Identification Provider (idP) using the SAML 2.0 standard to be used with supporting SAML 2.0 Service Providers (SP).

263763.5k1](/packages/codegreencreative-laravel-samlidp)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
