PHPackages                             jardissupport/auth - 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. jardissupport/auth

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

jardissupport/auth
==================

Token management, session handling, and security utilities for authentication and authorization

v1.0.0(1mo ago)00proprietaryPHPPHP &gt;=8.3CI passing

Since Apr 5Pushed 1mo agoCompare

[ Source](https://github.com/jardisSupport/auth)[ Packagist](https://packagist.org/packages/jardissupport/auth)[ Docs](https://github.com/jardisSupport/auth)[ RSS](/packages/jardissupport-auth/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (4)Versions (3)Used By (0)

Jardis Auth
===========

[](#jardis-auth)

[![Build Status](https://github.com/jardisSupport/auth/actions/workflows/ci.yml/badge.svg)](https://github.com/jardisSupport/auth/actions/workflows/ci.yml/badge.svg)[![License: PolyForm Shield](https://camo.githubusercontent.com/d8fb46c82be4c5312bf3e372ac734dfdf6a8b328e9c2b2856af671adbb0600a5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d506f6c79466f726d253230536869656c642d626c75652e737667)](LICENSE.md)[![PHP Version](https://camo.githubusercontent.com/ecbf012e3704aed80a710e072f8a2b65afb62dddc90cadf159926d8678d54888/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d253345253344382e332d3737374242342e737667)](https://www.php.net/)[![PHPStan Level](https://camo.githubusercontent.com/c51bda247654363d3e30bc352674dd761a9557803a14af0226eb411d6dc0006b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d4c6576656c253230382d627269676874677265656e2e737667)](phpstan.neon)[![PSR-12](https://camo.githubusercontent.com/34b10db0caa29bacd49bda5c437a8de95385f036f3230b31fa605326e18da22c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f64652532305374796c652d5053522d2d31322d626c75652e737667)](phpcs.xml)

> Part of the **[Jardis Business Platform](https://jardis.io)** — Enterprise-grade PHP components for Domain-Driven Design

**Authentication and authorization without framework coupling.** Opaque tokens, session management, password hashing, and role-based access control — designed for DDD applications. No HTTP layer, no JWT, no external dependencies. Pure support package.

---

Why This Package?
-----------------

[](#why-this-package)

- **Four classes to learn** — `SessionManager`, `PasswordHasher`, `Guard`, `PasswordAuthenticator`. Everything else is data
- **Opaque tokens** — server-side state, SHA-256 hashed storage, no JWT complexity
- **Token rotation** — automatic refresh with old-token revocation
- **RBAC as Value Objects** — policies are immutable, defined in code, not in a database
- **Zero external dependencies** — uses PHP built-ins: `password_hash`, `random_bytes`, `hash_hmac`, `hash_equals`

---

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

[](#installation)

```
composer require jardissupport/auth
```

---

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

[](#quick-start)

### Create a Session

[](#create-a-session)

```
use JardisSupport\Auth\SessionManager;
use JardisSupport\Auth\Data\Subject;

$sessionManager = new SessionManager($tokenStore);
$subject = Subject::from('user-42', 'user');

$result = $sessionManager->create($subject, ['role' => 'editor']);

$accessToken  = $result->accessToken;   // send to client
$refreshToken = $result->refreshToken;  // store securely on client
$session      = $result->session;       // use server-side

// Dispatch events (optional — use your EventDispatcher)
foreach ($result->events as $event) {
    $dispatcher->dispatch($event);
}
```

### Verify &amp; Refresh Tokens

[](#verify--refresh-tokens)

```
use JardisSupport\Auth\Handler\Token\VerifyToken;
use JardisSupport\Auth\Data\TokenType;

// Verify an access token
$verifier = new VerifyToken();
$hash = hash('sha256', $accessToken);
$stored = $tokenStore->find($hash);
$verifier($accessToken, $stored, TokenType::Access);
// throws TokenExpiredException or TokenRevokedException

// Refresh — rotates tokens, revokes the old refresh token
$newResult = $sessionManager->refresh($refreshToken);
// $newResult->events contains SessionCreated + SessionRefreshed
```

### Hash &amp; Verify Passwords

[](#hash--verify-passwords)

```
use JardisSupport\Auth\PasswordHasher;

$hasher = PasswordHasher::argon2id();

// Registration
$hash = $hasher->hash('secret-password');

// Login
$hasher->verify('secret-password', $hash); // true

// Rehash check on every login
if ($hasher->needsRehash($hash)) {
    $newHash = $hasher->hash('secret-password');
    // update stored hash
}
```

### Authorize with RBAC

[](#authorize-with-rbac)

```
use JardisSupport\Auth\Guard;
use JardisSupport\Auth\Data\Policy;

$policy = Policy::create()
    ->role('admin')->allow('*')
    ->role('editor')
        ->allow('article:read', 'article:write', 'article:publish')
        ->deny('article:delete')
    ->role('viewer')->allow('article:read')
    ->role('moderator')->includes('editor')->allow('comment:delete')
    ->build();

$guard = new Guard($policy);

$guard->check($session, 'article:publish');     // true/false
$guard->authorize($session, 'article:delete');  // throws UnauthorizedException

// Multi-role sessions — first matching role wins
$session = new Session(
    subject: 'user:42',
    tokenHash: $hash,
    createdAt: new DateTimeImmutable(),
    expiresAt: null,
    metadata: ['role' => ['editor', 'moderator']],
);
$guard->check($session, 'comment:delete');  // true (moderator has permission)
```

### Authenticate with Password

[](#authenticate-with-password)

```
use JardisSupport\Auth\PasswordAuthenticator;
use JardisSupport\Auth\Data\Credential;

$authenticator = new PasswordAuthenticator(
    $passwordHasher,
    $sessionManager,
    function (string $identifier): ?array {
        $user = $userRepository->findByEmail($identifier);
        if ($user === null) {
            return null;
        }
        return [
            'hash' => $user->passwordHash,
            'subject' => Subject::from($user->id, 'user'),
            'claims' => ['role' => $user->role],
        ];
    },
);

$credential = Credential::password('john@example.com', 'secret123');
$result = $authenticator->authenticate($credential);

if ($result->isSuccess()) {
    $session = $result->session;
    $accessToken = $result->accessToken;
}

// All events in one place: SessionCreated + AuthenticationSucceeded (or AuthenticationFailed)
foreach ($result->events as $event) {
    $dispatcher->dispatch($event);
}
```

### Invalidate Sessions

[](#invalidate-sessions)

```
// Single session (logout) — returns SessionInvalidated event
$event = $sessionManager->invalidate($session);

// All sessions for a subject (logout everywhere) — returns AllSessionsInvalidated event
$event = $sessionManager->invalidateAll('user:user-42');
```

---

Token Store
-----------

[](#token-store)

The package defines `TokenStoreInterface` — you implement it in your infrastructure layer:

```
use JardisSupport\Contract\Auth\TokenStoreInterface;
use JardisSupport\Auth\Data\HashedToken;

class DatabaseTokenStore implements TokenStoreInterface
{
    public function __construct(private PDO $pdo) {}

    public function store(HashedToken $token): void { /* INSERT */ }
    public function find(string $hash): ?HashedToken { /* SELECT */ }
    public function revoke(string $hash): void { /* UPDATE revoked = true */ }
    public function revokeAllForSubject(string $subject): void { /* UPDATE WHERE subject = ? */ }
    public function deleteExpired(): int { /* DELETE WHERE expires_at < NOW() */ }
}
```

An `InMemoryTokenStore` is included in `tests/Support/` for testing.

---

Password Hashing
----------------

[](#password-hashing)

```
// Argon2id (default, recommended)
$hasher = PasswordHasher::argon2id(memoryCost: 65536, timeCost: 4, threads: 1);

// Bcrypt (fallback)
$hasher = PasswordHasher::bcrypt(cost: 12);

// Default constructor uses Argon2id
$hasher = new PasswordHasher();
```

---

Error Handling
--------------

[](#error-handling)

ExceptionWhen`AuthenticationException`Authentication failed (base class)`TokenExpiredException`Token has expired`TokenRevokedException`Token was revoked`InvalidCredentialException`Invalid credentials provided`UnauthorizedException`Insufficient permissions (RBAC)```
use JardisSupport\Auth\Exception\TokenExpiredException;
use JardisSupport\Auth\Exception\UnauthorizedException;

try {
    $verifier($token, $storedToken, TokenType::Access);
} catch (TokenExpiredException $e) {
    // Token expired — client should use refresh token
}

try {
    $guard->authorize($session, 'admin:delete');
} catch (UnauthorizedException $e) {
    // Access denied
}
```

---

Architecture
------------

[](#architecture)

The user sees four orchestrators. Internally, each delegates to invokable handlers:

```
SessionManager (Orchestrator)
  ├── Handler/Session/CreateSession         create session + token pair + SessionCreated event
  ├── Handler/Session/RefreshSession        rotate tokens + SessionRefreshed event
  ├── Handler/Session/InvalidateSession     revoke single session + SessionInvalidated event
  └── Handler/Session/InvalidateAllSessions revoke all + AllSessionsInvalidated event

PasswordAuthenticator (Orchestrator)
  ├── Handler/Authentication/LookupUser      resolve user via $userLookup closure
  ├── Handler/Authentication/VerifyCredential verify password against hash
  └── Handler/Authentication/BuildAuthResult  assemble AuthenticationResult + events

PasswordHasher (Orchestrator)
  ├── Handler/Password/HashPassword         hash via password_hash()
  ├── Handler/Password/VerifyPassword       verify via password_verify()
  └── Handler/Password/CheckRehash          check via password_needs_rehash()

Guard (Orchestrator)
  ├── Handler/Authorization/CheckPermission    check role(s) against policy
  └── Handler/Authorization/AuthorizePermission check + throw on failure

Data (Value Objects, Enums, Builder, Events)
  ├── Token, HashedToken, TokenType
  ├── Session, SessionResult
  ├── Subject, Credential, CredentialType, AuthResult, AuthenticationResult
  ├── Permission, Policy, PolicyBuilder
  └── Event/ (AuthenticationSucceeded, AuthenticationFailed, SessionCreated,
       SessionRefreshed, SessionInvalidated, AllSessionsInvalidated)

```

Each handler is an **invokable object** (`__invoke`) — independently testable, replaceable, composable. The orchestrators contain no business logic, only delegation.

### Test Structure

[](#test-structure)

Tests mirror the `src/` directory:

```
tests/Integration/
├── GuardTest.php                          ← src/Guard.php
├── SessionManagerTest.php                 ← src/SessionManager.php
├── PasswordHasherTest.php                 ← src/PasswordHasher.php
├── PasswordAuthenticatorTest.php          ← src/PasswordAuthenticator.php
├── Data/
│   ├── AuthResultTest.php                 ← src/Data/AuthResult.php
│   ├── CredentialTest.php                 ← src/Data/Credential.php
│   ├── SubjectTest.php                    ← src/Data/Subject.php
│   ├── PermissionTest.php                 ← src/Data/Permission.php
│   ├── PolicyTest.php                     ← src/Data/Policy.php
│   ├── TokenTest.php                      ← src/Data/Token.php
│   └── HashedTokenTest.php                ← src/Data/HashedToken.php
├── Handler/Token/
│   └── VerifyTokenTest.php                ← src/Handler/Token/VerifyToken.php
└── Support/
    └── InMemoryTokenStoreTest.php         ← tests/Support/InMemoryTokenStore.php

```

---

Contracts
---------

[](#contracts)

Defined in `jardissupport/contract` — implement these in your infrastructure:

InterfacePurpose`TokenStoreInterface`Token persistence: store, find, revoke, deleteExpired`PasswordHasherInterface`Hash, verify, needsRehash`GuardInterface`Permission check + authorize`AuthenticatorInterface`Authenticate credentials, return AuthResult---

Foundation Integration
----------------------

[](#foundation-integration)

Auth is a **support package** — no service hook in `DomainApp`. Integration happens in your bounded context:

- **TokenStore**: Implement in infrastructure (database, Redis)
- **Policy**: Define as value object in application layer
- **Guard**: Instantiate in application layer, inject Policy

### ENV Variables (optional)

[](#env-variables-optional)

```
# Password Hashing
AUTH_HASH_ALGO=argon2id
AUTH_HASH_MEMORY=65536
AUTH_HASH_TIME=4
AUTH_HASH_THREADS=1

# Token Defaults
AUTH_TOKEN_LENGTH=32
AUTH_ACCESS_TOKEN_TTL=3600
AUTH_REFRESH_TOKEN_TTL=604800
```

---

What This Package Does NOT Do
-----------------------------

[](#what-this-package-does-not-do)

- **No JWT** — opaque tokens only. JWT comes in v2 at the earliest
- **No OAuth2/OIDC** — no authorization server, no PKCE
- **No HTTP layer** — no cookies, no middleware, no `session_start()`
- **No user management** — no user model, no registration flow
- **No rate limiting** — brute-force protection is infrastructure concern
- **No token persistence** — only the interface. You implement the store
- **No event dispatching** — events are returned to the caller, not dispatched internally

---

Development
-----------

[](#development)

```
cp .env.example .env    # One-time setup
make install             # Install dependencies
make phpunit             # Run tests
make phpstan             # Static analysis (Level 8)
make phpcs               # Coding standards (PSR-12)
```

---

License
-------

[](#license)

[PolyForm Shield License 1.0.0](LICENSE.md) — free for all use including commercial. Only restriction: don't build a competing framework.

###  Health Score

39

—

LowBetter than 87% of packages

Maintenance91

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity50

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% 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

Unknown

Total

1

Last Release

46d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/e07a1b668e9e01ee6d1b85de7b3be1c2513f68aae9494b2011d1592104d5daa0?d=identicon)[jardis](/maintainers/jardis)

---

Top Contributors

[![Headgent](https://avatars.githubusercontent.com/u/245725954?v=4)](https://github.com/Headgent "Headgent (1 commits)")

---

Tags

securityauthAuthenticationtokenauthorizationsessionDomain Driven DesignHeadgentjardis

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/jardissupport-auth/health.svg)

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

###  Alternatives

[lusitanian/oauth

PHP 7.2 oAuth 1/2 Library

1.1k23.2M121](/packages/lusitanian-oauth)[dyorg/slim-token-authentication

Slim 3.0+ Token Authentication Middleware

78106.5k](/packages/dyorg-slim-token-authentication)[matricali/akamai-token-auth

This library provides necessary logic to generate Akamai edge authorization token and signed URL.

111.2M](/packages/matricali-akamai-token-auth)

PHPackages © 2026

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