Download the PHP package enlivenapp/flight-shield without Composer

On this page you can find all versions of the php package enlivenapp/flight-shield. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package flight-shield

Stable? Not Quite Yet License PHP Version Monthly Downloads Total Downloads GitHub Issues Contributors Latest Release Contributions Welcome PHPUnit PHPStan Psalm Infection

Flight Shield

Flight Shield is an authentication and authorization plugin for FlightPHP. It was ported and adapted from CodeIgniter4 Shield around its v1.2. We watch for security patches, found some on our own and we've made significant functionality additions. It runs inside FlightPHP as used by Pubvana CMS v3. It provides session login, access token auth, HMAC-SHA256 request signing, JWT verification, role-based groups and permissions, password validation, and route-level middlewares.


Features


Requirements


Testing

{explain our testing here. What we test with (all of them), and a summary of the results for the current release version}

Installation

1. Install via Composer

2. Provide the override config

Shield ships with sensible defaults in src/Config/Config.php. Create the override config file app/config/shield.php to set your own values (details in Configuration). At minimum it sets the route prefix, 'routePrepend' => 'auth', and you can add anything from the default config file to it.

Start sessions before Shield so session state is ready at auth time. Start CSRF after both.

3. Run the migrations

Shield ships PHP migration classes under Database/Migrations/ (plus seed data in Database/Seeds/). Run all pending migrations across every module with the enlivenapp/migrations runway command:

This creates the following tables: users, auth_identities, auth_logins, auth_token_logins, auth_remember_tokens, auth_groups_users, auth_permissions_users, auth_groups, auth_permissions, auth_group_permissions. Apps that apply pending migrations during boot pick these up on the first request, so you can skip the CLI step.

4. Seed default groups and permissions

Default groups, permissions, and group-permission mappings are declared as seed data in Plugin::$seeds / the package Seeds directory. They apply automatically when migrations run, so there is no separate CLI step.

The seeds populate:


Quick Start

Protect a route with session auth

Log a user in manually

Check a permission

Note: Superadmins always has access

Get the current user ID anywhere


Management APIs

For app-level administration (user/group/permission CRUD), Shield exposes management services through Auth. These services wrap the models and apply validation and consistency rules, so prefer them over calling the models directly.

Accessor Class Purpose
auth()->users() Services\UserManagement List/find/create users, profile updates (username/email/password with validation + hashing), activate/deactivate, soft delete
auth()->groups() Authorization\Groups Group CRUD, permission assignment/sync, delete with membership fallback to user
auth()->permissions() Authorization\Permissions Permission CRUD; deletes also clean group mappings and direct grants/denies
auth()->stats() Services\UserStats User/login statistics

UserManagement::create(), updateProfile(), and similar operations return a Result (isOK(), reason(), extraInfo()). Password changes run through the configured password validators before hashing; duplicate email addresses are rejected.


How It Works

Architecture overview

Plugin::register() registers Auth as a Flight service ($app->auth()). The Auth class delegates every call to the currently active AuthenticatorInterface instance. That instance comes from Authentication::factory(), which resolves the alias (session, tokens, hmac, jwt) against the authenticators map in config and returns a singleton per request. All models extend flightphp/active-record (\flight\ActiveRecord).

The four authenticators are:

Alias Class Transport
session Session PHP session + optional remember-me cookie
tokens AccessTokens Authorization: Bearer <token> header
hmac HmacSha256 Authorization: HMAC-SHA256 <key>:<sig> + X-Request-Timestamp header
jwt JWT Authorization: Bearer <jwt> header

JWT is not enabled by default (it is commented out in the default authenticators map). Add it explicitly and install firebase/php-jwt to use it.

ChainAuthMiddleware iterates the authentication_chain list (default: ['session', 'tokens', 'hmac']) and stops at the first authenticator that reports loggedIn() === true. That lets a single route accept both browser sessions and API clients.

Authorization uses two parallel systems: groups (assigned via auth_groups_users) and direct permissions (assigned via auth_permissions_users). User::can() checks direct permissions first, then walks the user's groups and queries auth_group_permissions for each. Matching uses hierarchical wildcard semantics: a trailing * grants a node and all its descendants (users.* grants users.create, users.create.team, …), and a middle * matches exactly one segment (admin.*.post). A standalone * matches nothing. Use can() on a user in the superadmin group instead (that group bypasses all permission checks).

Password validation is a pipeline of ValidatorInterface classes run in order. The first failure short-circuits the pipeline and returns an error.


Session Authentication

Shield stores all session state in the database through enlivenapp/flight-sessions, not PHP's native file sessions. See that package's README for how the session storage works.

The Session authenticator stores the authenticated user's ID under $config['session.field'] (default key: user). On login the session ID regenerates and the row binds to the user. On logout the session keys (user, auth_action) are removed, the user binding clears, and the session regenerates again.

Remember me

When session.allow_remembering is true (the default), calling auth('session')->getAuthenticator()->remember() after login stores a split-token cookie. The cookie holds a selector:validator pair. The selector is stored in plain text in auth_remember_tokens; the validator is stored as sha256(validator). On each request where no session exists, the remember cookie is checked:

  1. Locate the token by selector.
  2. Compare sha256(cookie_validator) against the stored hash using hash_equals().
  3. If a mismatch is detected (possible token theft), all remember tokens for that user are purged.
  4. On success, the old token is deleted, the user is logged in, and a new token is issued (token rotation).

Expired tokens are purged probabilistically (20% of successful remember-me logins) to avoid a separate cleanup job.

Post-login actions (2FA)

When actions.login is set to an action class (e.g. Email2FA), attempt() puts the session into a STATE_PENDING by storing the user ID and action class name in the session. The user isn't considered fully logged in until the action completes. The /auth/2fa routes drive this flow.

Email activation

Registration takes an email and a password. Shield derives the username from the email local part and adds a number when it is taken, so callers never supply one.

Set actions.register to EmailActivator::class to require email activation. Registration creates the account inactive and sends the activation link in the same request, without touching the session. The link at /auth/activate/verify?token=... resolves the account from the token alone, so it works from any browser or device: the account is activated, the token is burned, and a session starts for the login hand-off (running the actions.login action first when one applies).

The activation page at /auth/activate is the landing point for anyone whose account is not activated. It shows a message, an email field, and a resend button.

Configuration


Access Token Authentication

The AccessTokens authenticator reads the Authorization header and strips the Bearer prefix. It hashes the raw token with SHA-256, looks it up in auth_identities (type = access_token), and checks expiry and unused_token_lifetime. On success it touches last_used_at.

Tokens are stored as identities in auth_identities. Use the shield:user CLI or your own application code to generate them.


HMAC Authentication

The HmacSha256 authenticator authenticates API clients that sign each request instead of sending a reusable secret.

Request format

The client sends:

The signature is computed as:

Verification

  1. Parse userKey and signature from the header.
  2. Look up the identity by userKey in auth_identities (type = hmac_sha256).
  3. Reject the request if the timestamp is more than 300 seconds from time() (replay protection).
  4. Decrypt the stored secret2 and recompute the HMAC. Compare with hash_equals().
  5. Check unused_token_lifetime.
  6. Touch last_used_at.

HMAC secrets at rest

Secrets are stored in auth_identities.secret2 encrypted with AES-256-GCM. Encryption keys live in the hmac.encryption_keys override block (never in the database); the active key is identified by hmac.encryption_current_key. These are secrets, so prefer keeping them out of version control. Your host's .env loader can inject them into the config array at boot.

The shield:hmac CLI writes and rotates these keys in place into the same override config file described under Configuration. That file must already contain an empty hmac block. See the shield:hmac section below for the full workflow.


JWT Authentication

The JWT authenticator is stateless. It reads Authorization: Bearer <token>, parses and verifies the JWT using JWTManager (which wraps firebase/php-jwt), extracts the sub claim as the user ID, and loads the user from the database.

JWT is not enabled by default. To enable it:

  1. Install the dependency: composer require firebase/php-jwt ^6.0
  2. Add jwt to the authenticators map in your plugin config:

  3. Provide JWT keys in your config overrides (fill in the values):

To generate a token for a user, call generateToken() directly on the JWT authenticator instance:


Chain Authentication

ChainAuthMiddleware tries each authenticator in authentication_chain in order. The first one whose loggedIn() returns true grants access and records the active date. If all fail, the user is redirected to the configured login URL.

Default chain: ['session', 'tokens', 'hmac']. Override with authentication_chain in config.


Groups and Permissions

Data model

Permission resolution

User::can(string $permission) checks in this order:

  1. Direct user permissions in auth_permissions_users
  2. Group permissions from auth_group_permissions for each of the user's groups

Permission matching is hierarchical. A trailing * matches a node and every descendant (users.* grants users.create, users.edit, users.edit.settings, … but never users itself); a middle * matches exactly one segment (admin.*.post grants admin.news.post but not admin.news.manage.post). Wildcards are only allowed as whole segments, and a standalone * no longer matches everything. Grant the superadmin group to bypass checks entirely.

Superadmin visibility

Superadmin users are hidden from non-superadmin callers by default. That prevents lower-privilege admins from viewing, editing, or deleting superadmin accounts.

The User model methods that support this:

Pass $currentUser->inGroup('superadmin') as the flag so superadmins see everyone and non-superadmins see only non-superadmin users.

Note: Filtering happens post-fetch in PHP (not at the query level) due to ActiveRecord limitations with table-qualified column names in WHERE clauses. For paginated results, a page may contain fewer results than $perPage if superadmin users were in the batch.

Default seeds

The following seed data is inserted automatically whenever migrations run (enlivenapp/migrations applies package seed classes alongside migrations):

Groups: superadmin, admin, user

Permissions: admin.access, users.list, users.create, users.edit, users.delete, profile.edit

Group-permission mappings:

Working with groups and permissions in code

Note: typed-property assignments bypass ActiveRecord's change tracking, so Shield's trait methods use explicit dirty([...]) calls internally.


Password Validators

Validators run as a pipeline during registration and password changes. The default set is CompositionValidator, NothingPersonalValidator, DictionaryValidator. PwnedValidator is available but not enabled by default.

Validator What it checks
CompositionValidator Min length (default 8, matching the NIST SP 800-63B minimum) and max length (128)
NothingPersonalValidator Password must not contain or closely match the username, email address, or reversed username. Also checks similarity via similar_text() against max_similarity (default 50%)
DictionaryValidator Password must not appear in the bundled 65,000-entry common-password list
PwnedValidator Password must not appear in the Have I Been Pwned database (uses k-anonymity API, fails open if the API is unreachable)

Configure in passwords:


Middlewares

Apply middlewares to routes or route groups using FlightPHP's ->addMiddleware().

SessionAuthMiddleware

Requires an active session login. Redirects to the configured login URL if not authenticated. Records the active date on success.

TokenAuthMiddleware

Authenticates via a Bearer access token in the Authorization header. Halts with a redirect on failure.

HmacAuthMiddleware

Authenticates via HMAC-SHA256 request signing. Reads the Authorization header, X-Request-Timestamp header, and the raw request body. Redirects on failure.

JWTAuthMiddleware

Authenticates via a JWT Bearer token. Requires firebase/php-jwt. Redirects on failure.

ChainAuthMiddleware

Tries each authenticator in authentication_chain in order. Grants access on the first success. Redirects to the login URL if all fail.

GroupMiddleware

Requires the authenticated user to belong to at least one of the specified groups. Redirects to group_denied URL on failure, login URL if not authenticated.

PermissionMiddleware

Requires the authenticated user to hold at least one of the specified permissions (checked via User::can()). Redirects to permission_denied URL on failure.

ForcePasswordResetMiddleware

If the authenticated user's requiresPasswordReset() returns true, redirects to the configured force_reset URL. Passes through silently if the user isn't logged in.

RateLimitMiddleware

Applied automatically to POST /auth/login, POST /auth/magic-link, POST /auth/2fa/verify, POST /auth/2fa/resend, and POST /auth/activate/resend. Can also be applied to custom routes.

Counts failed login attempts from the client IP across both auth_logins and auth_token_logins within the decay_minutes window. If max_attempts is reached and the most recent failure is still within the lockout_minutes window, the request is halted with HTTP 429 and a JSON error body.


CLI Commands

Flight Shield uses the runway CLI provided by flightphp/runway.

shield

Displays available sub-commands.


shield:user

Manage users.

Every shield:user command that changes a user runs through the same Services\UserManagement logic used by the HTTP layer. So CLI calls validate identically: duplicate emails are rejected, and passwords must pass the configured validator pipeline (min length, personal-info checks, dictionary, and so on). show prints full detail for one user: groups, direct permissions including denies, last login, and status.


shield:group

Manage groups.

syncpermissions replaces the group's permission set with the given comma-separated list. Aliases that don't exist are reported and skipped; if none of the provided aliases resolve, the command aborts and the group is left unchanged.

Deleting a group also removes all group-permission mappings and all user memberships. Any user left with no groups after deletion is automatically reassigned to the user group.


shield:permission

Manage permissions.

Deleting a permission also cleans up everything referencing it: group-permission mappings (auth_group_permissions) and direct user grants/denies (auth_permissions_users) are removed in the same operation, so no orphaned rows remain.


shield:hmac

Manage HMAC encryption keys and token lifecycle.

Key rotation workflow:

init, addkey, and removekey write to the host's app/config/shield.php override file. That file must already exist with an empty hmac block before you run them; otherwise the command reports that there is nothing to write to. The application merges this override file with the package defaults at load time.


Configuration

All Shield options live inside the plugin's config. The plugin merges your overrides with its defaults from src/Config/Config.php, so you only need to include values you want to override.

Provide your overrides in the per-app override config file at app/config/shield.php (resolved from PROJECT_ROOT). The application merges it over the package defaults at load time:

Full default configuration

Key Default Description
default_authenticator 'session' Authenticator used when none is specified
authentication_chain ['session', 'tokens', 'hmac'] Order tried by ChainAuthMiddleware
allow_registration true Allow new user self-registration
allow_magic_link false Enable magic link login
magic_link_lifetime 3600 Magic link token TTL in seconds
default_group 'user' Group assigned to newly registered users
record_login_attempt 'all' Login attempt recording: 'none', 'failure', or 'all'
record_active_date true Update last_active on every authenticated request
unused_token_lifetime 7776000 Access/HMAC token inactivity TTL in seconds (90 days)
valid_login_fields ['email'] Fields accepted as login identifier
personal_fields [] No-op in this port: NothingPersonalValidator pulls username/email from the identity record itself
email_sender null Callback for outbound emails (see Email Setup)
actions.login null Post-login action class (Email2FA::class or null)
actions.register null Set to EmailActivator::class to require email activation at registration; null creates accounts active
token_header 'Authorization' Header read by the AccessTokens authenticator
hmac_header 'Authorization' Header read by the HmacSha256 authenticator

Session settings

Password settings

HMAC settings

JWT settings

Rate limiting settings

Redirect URLs


Routes

Shield registers the following routes under the prefix defined by the returned routePrepend value in src/Config/Config.php (default: auth).

Method Path Notes
GET /auth/login Show login form
POST /auth/login Process login - CSRF + rate limit
GET /auth/logout Log out
GET /auth/register Show registration form
POST /auth/register Process registration - CSRF
GET /auth/magic-link Show magic link request form
POST /auth/magic-link Send magic link email - CSRF + rate limit
GET /auth/magic-link/verify Verify magic link token
GET /auth/2fa Show 2FA verification page (sends code)
POST /auth/2fa/verify Verify 2FA code - CSRF + rate limit
POST /auth/2fa/resend Resend 2FA code - CSRF + rate limit
GET /auth/activate Activation page (message + resend form)
POST /auth/activate/resend Send a new activation email - CSRF + rate limit
GET /auth/activate/verify Verify email activation token (from the email)

Email Setup

Shield doesn't ship with a mailer. Provide a callback that accepts the recipient address, subject, and body:

The callback runs for 2FA codes, magic links, and email activation messages.


Views

Shield renders its own views by default, shipping both .php and .tpl variants under src/Views/. To override any view, create a matching file under app/Views/<plugin-id>/. The plugin id is the package name, so login.php overrides to:

A theme can override it the same way. Replace <active> with the currently active theme's directory (for example themes/default/Views/enlivenapp/flight-shield/login.php).

Overrideable views:


Security Notes

Passwords are hashed with password_hash() using PASSWORD_DEFAULT (bcrypt, cost 12). You can switch to Argon2 via algorithm, memory_cost, time_cost, and threads. Passwords rehash automatically when the cost parameters change.

Session storage is handled by enlivenapp/flight-sessions; see that package's README for how payloads are protected at rest.

HMAC secrets are stored encrypted with AES-256-GCM. Encryption keys live in app configuration (or better, injected from .env), never in the database.

All token and hash comparisons use hash_equals() to prevent timing attacks.

HMAC requests with a timestamp more than 300 seconds from time() are rejected, which blocks replay attempts.

Remember-me tokens use a split-token scheme (selector stored plain, validator stored as a SHA-256 hash). A mismatch purges all tokens for the user.

All mutating auth routes are protected by CsrfMiddleware from enlivenapp/flight-csrf.

Failed login attempts are tracked per IP across auth_logins and auth_token_logins. Excessive failures result in HTTP 429.

The session ID regenerates on login and logout, which prevents session fixation.


License

MIT - see LICENSE.


All versions of flight-shield with dependencies

PHP Build Version
Package Version
Requires php Version >=8.1
ext-pdo Version *
ext-openssl Version *
flightphp/core Version ^3.0
flightphp/active-record Version >=0.7
enlivenapp/flight-csrf Version ^1.0
enlivenapp/flight-sessions Version ^0.1
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package enlivenapp/flight-shield contains the following files

Loading the files please wait ...