PHPackages                             rsthn/rose-ext-sentinel - 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. rsthn/rose-ext-sentinel

ActiveRose-extension[Authentication &amp; Authorization](/categories/authentication)

rsthn/rose-ext-sentinel
=======================

Sentinel Authentication Extension

v3.0.6(2w ago)27353Apache-2.0PHP

Since May 14Pushed 2w ago1 watchersCompare

[ Source](https://github.com/rsthn/rose-ext-sentinel)[ Packagist](https://packagist.org/packages/rsthn/rose-ext-sentinel)[ RSS](/packages/rsthn-rose-ext-sentinel/feed)WikiDiscussions master Synced 4d ago

READMEChangelogDependencies (6)Versions (55)Used By (3)

Sentinel Authentication Extension
=================================

[](#sentinel-authentication-extension)

This extension adds user authentication features to [Rose](https://github.com/rsthn/rose-core).

```
composer require rsthn/rose-ext-sentinel
```

Database Structure
==================

[](#database-structure)

The following tables are required by Sentinel, and any of them can be extended if desired.

Ready-to-run schema files are included in the repository: [`mysql.sql`](./mysql.sql) and [`postgres.sql`](./postgres.sql). If you use rose-core's database-backed sessions, the `sessions` table defined in those files is also required. Those files also carry columns and tables that Sentinel itself never reads, such as the `devices` table and the extra `users` columns in [`postgres.sql`](./postgres.sql). Those are provided for your own CRUD purposes and can be adjusted or removed to suit your application.

The DDL shown below is the MySQL variant. Because PostgreSQL has no `UNSIGNED` integers, [`postgres.sql`](./postgres.sql) uses plain `INT` for all identifier columns.

```
CREATE TABLE users
(
    user_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    created_at DATETIME NOT NULL,
    deleted_at DATETIME DEFAULT NULL,
    blocked_at DATETIME DEFAULT NULL,
    username VARCHAR(256) NOT NULL,
    password VARCHAR(96) NOT NULL,
    username_active VARCHAR(256) GENERATED ALWAYS AS (IF(deleted_at IS NULL, username, NULL)) STORED
)
ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1;

CREATE INDEX users_created_at ON users (created_at);
CREATE INDEX users_deleted_at ON users (deleted_at);
CREATE INDEX users_blocked_at ON users (blocked_at);
CREATE INDEX users_username ON users (deleted_at, username);
CREATE UNIQUE INDEX users_username_uniq ON users (username_active);
```

The `username` of a non-deleted user must be unique, otherwise the login functions will silently authenticate an arbitrary one of the duplicates. The `username_active` generated column exists solely to enforce this on MySQL, which has no partial indexes (PostgreSQL uses `CREATE UNIQUE INDEX ... WHERE deleted_at IS NULL` instead). It requires MySQL 5.7.8+ or MariaDB 10.2+.

```
CREATE TABLE permissions
(
    permission_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    deleted_at DATETIME DEFAULT NULL,
    name VARCHAR(128) NOT NULL UNIQUE
)
ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```

```
CREATE TABLE user_permissions
(
    user_id INT UNSIGNED NOT NULL,
    permission_id INT UNSIGNED NOT NULL,
    flag INT DEFAULT 0,
    PRIMARY KEY (user_id, permission_id),
    FOREIGN KEY (user_id) REFERENCES users (user_id),
    FOREIGN KEY (permission_id) REFERENCES permissions (permission_id)
)
ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE INDEX user_permissions_flag ON user_permissions (user_id, flag);
```

The `flag` column is never read by Sentinel, it is reserved for your own CRUD purposes (the `user_permissions_flag` index exists to serve it). Drop both if you have no use for them.

### Token Authorization

[](#token-authorization)

Add the `tokens` table whenever authorization via access tokens is reachable, that is, when either `auth_bearer` or `auth_basic` is set to true in the Sentinel configuration section. Note that `auth_basic` is enough on its own, because a basic credential using the literal username `token` is verified against this table (see `sentinel:login`).

```
CREATE TABLE tokens
(
    token_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    created_at DATETIME NOT NULL,
    deleted_at DATETIME DEFAULT NULL,
    blocked_at DATETIME DEFAULT NULL,
    user_id INT UNSIGNED NOT NULL,
    token VARCHAR(128) NOT NULL UNIQUE,
    name VARCHAR(128) NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users (user_id)
)
ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci AUTO_INCREMENT=1;

CREATE INDEX tokens_user_id ON tokens (user_id, deleted_at);
CREATE INDEX tokens_token ON tokens (token, deleted_at);
```

### Token Permissions

[](#token-permissions)

The `token_permissions` table is gated by the separate `token_permissions` configuration field, not by `auth_bearer`. Add it only when that field is set to true, otherwise permissions are always resolved from `user_permissions` and this table is never queried, even for sessions that authenticated with a token.

```
CREATE TABLE token_permissions
(
    token_id INT UNSIGNED NOT NULL,
    permission_id INT UNSIGNED NOT NULL,
    flag INT DEFAULT 0,
    PRIMARY KEY (token_id, permission_id),
    FOREIGN KEY (token_id) REFERENCES tokens (token_id),
    FOREIGN KEY (permission_id) REFERENCES permissions (permission_id)
)
ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE INDEX token_permissions_flag ON token_permissions (token_id, flag);
```

As with `user_permissions`, the `flag` column here is reserved for your own CRUD purposes and is never read by Sentinel.

### Identifier Banning

[](#identifier-banning)

Sentinel include support to blacklist identifiers that are trying to brute force the system. To use this feature check the `sentinel:access-required`,`sentinel:access-denied` and `sentinel:access-granted` functions.

The following table is required for this feature:

```
CREATE TABLE suspicious_identifiers
(
    identifier VARCHAR(512) NOT NULL,
    PRIMARY KEY (identifier),
    next_attempt_at DATETIME DEFAULT NULL,
    last_attempt_at DATETIME NOT NULL,
    count_failed INT DEFAULT 1,
    count_blocked INT DEFAULT 0,
    is_banned INT DEFAULT 0
)
ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```

Configuration Section: "Sentinel"
=================================

[](#configuration-section-sentinel)

FieldTypeDescriptionDefaulthashstringName of the hash algorithm to use (passed directly to PHP's hash function).sha384prefixstringPassword prefix (salt).-suffixstringPassword suffix (salt).-masterboolIndicates if permission `master` should be added to all permissions checks.falseauth\_bearerboolWhen set to `true`, allows authentication via "Authorization: Bearer" header and enables the `sentinel:authorize` function.falseauth\_basicboolWhen set to `true`, allows authentication via "Authorization: Basic" header and automatically sends `WWW-Authenticate` header along with HTTP status 401 when authentication has not been completed.falsetoken\_permissionsboolWhen set to `true`, permissions will be loaded from the `token_permissions` table instead of `user_permissions` when the user authenticates using a token.falseNote that `auth_bearer` and `auth_basic` are read from the `Sentinel` configuration section, and both are disabled when that section is absent entirely.

Strings
=======

[](#strings)

Sentinel resolves its error messages from the `messages` strings section. Any key that is not defined will appear verbatim in the API response (i.e. `@messages.invalid_credentials`), so define all of the following:

KeyUsed whenauthorization\_blockedThe user or the token has a non-null `blocked_at`.invalid\_credentialsThe username, password or token did not match any record.authorization\_bearer\_not\_supportedA bearer token was presented while `auth_bearer` is disabled.authorization\_basic\_not\_supportedA basic credential was presented while `auth_basic` is disabled.authentication\_required`sentinel:auth-required` and friends failed with `401`.required\_permission\_not\_fulfilled`sentinel:permission-required` failed with `403`.required\_permission\_level\_not\_fulfilled`sentinel:level-required` failed with `403`.authorization\_banned`sentinel:access-required` found a banned identifier and no `message` was given.retry\_later`sentinel:access-required` found a blocked identifier; the remaining time is appended to this message.Functions
=========

[](#functions)

### (`sentinel:password` &lt;password&gt;)

[](#sentinelpassword-password)

Calculates the hash of the given password and returns it. The plain password gets the `suffix` and `prefix` configuration fields appended and prepended respectively before calculating its hash. The hash algorithm is set by the `hash` configuration field.

### (`sentinel:status`)

[](#sentinelstatus)

Returns the authentication status (boolean) of the active session.

### (`sentinel:auth-required`)

[](#sentinelauth-required)

Fails with error code `401` if the active session is not authenticated.

### (`sentinel:permission-required` &lt;permissions&gt;)

[](#sentinelpermission-required-permissions)

Verifies if the active session has the specified permissions. Fails with `401` if the session has not been authenticated, or with `403` if the permission requirements are not met. The permissions string contains the permission names OR-sets separated by pipe (|), and the AND-sets separated by ampersand (&amp;).

```
(sentinel:permission-required "admin | provider & enabled | customer")
; false
```

### (`sentinel:has-permission` &lt;permissions&gt; \[username\])

[](#sentinelhas-permission-permissions-username)

Verifies if the active session has the specified permissions. Returns boolean. The permissions string contains the permission name sets (see `sentinel:permission-required`). If `username` is provided, the check is performed against that user instead of the active session.

### (`sentinel:case` &lt;case1&gt; &lt;result1&gt; ... \[default &lt;default&gt;\])

[](#sentinelcase-case1-result1--default-default)

Checks the permissions of the active user against one of the case values. Returns the respective result or the default result if none matches. If no default result is specified an empty string will be returned. Note that each case result should be a value not a block. Each case string is a permission name set (see `sentinel:permission-required`).

```
(sentinel:case
    "admin"      "Has permission admin"
    "client"     "Has permission client"
    "x | y"      "Has permission x or y"
    "a & b & c"  "Has permission a, b and c"
)
```

### (`sentinel:level-required` &lt;level&gt;)

[](#sentinellevel-required-level)

Verifies if the active user meets the specified minimum permission level. The level is the permission\_id divided by 100. Fails with `401`if the user has not been authenticated, or with `403` if the permission level requirements are not met.

### (`sentinel:has-level` &lt;level&gt;)

[](#sentinelhas-level-level)

Verifies if the active user meets the specified minimum permission level. The level is the permission\_id divided by 100. Returns boolean.

```
(sentinel:has-level 7)
; true
```

### (`sentinel:get-level` \[username\])

[](#sentinelget-level-username)

Returns the permission level of the active session user, or of the given user if `username` is provided.

```
(sentinel:get-level "admin")
; 7
```

### (`sentinel:validate` &lt;username&gt; &lt;password&gt;)

[](#sentinelvalidate-username-password)

Verifies if the given credentials are valid, returns boolean.

```
(sentinel:validate "admin" "admin")
; true
```

### (`sentinel:login` &lt;username&gt; &lt;password&gt;)

[](#sentinellogin-username-password)

Verifies if the given credentials are valid, fails with `422` and sets the `error` field accordingly. When successful, opens a session and loads the `user` field with the data of the user that has been authenticated.

Note that Sentinel will automatically run the login process (without creating a session) if the `Authorization: BASIC data` header is detected
and the `auth_basic` flag is enabled in the configuration.

When the credentials in the `Authorization: BASIC data` header use the literal username `token`, the password will be treated as an access
token and verified against the `tokens` table instead of the user's password. This happens regardless of the `auth_bearer` flag, therefore
the `tokens` table is required whenever `auth_basic` is enabled.

When using Apache, the `HTTP_AUTHORIZATION` header is not sent to the application, however by setting the following in your `.htaccess` it
will be available for Sentinel to use it.

`SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1`

### (`sentinel:authorize` &lt;token&gt; \[persistent=false\])

[](#sentinelauthorize-token-persistentfalse)

Checks if the `auth_bearer` flag is set to `true` in the Sentinel configuration and then verifies the validity of the token and authorizes access. On errors return status code `422` and sets the `error` field accordingly.

When successful, opens a session only if the `persistent` flag is set to `true`, and loads the `user` field of the session
with the data of the user related to the token that was just authorized.

Note that Sentinel will automatically run the authorization process (without creating a session) if the `Authorization: BEARER token`
header is detected while `auth_bearer` is enabled in the configuration.

### (`sentinel:token-id`)

[](#sentineltoken-id)

Returns the `token_id` of the active session or `null` if the user is either not authenticated yet or the user authenticated by other means without a token (i.e. regular login).

```
(sentinel:token-id)
; 13
```

### (`sentinel:login-manual` &lt;data&gt;)

[](#sentinellogin-manual-data)

Starts a session and loads the specified data object into the `user` session field, effectively creating (manually) an authenticated session. If the data being placed in the session does not actually exist in the database, ensure to use only the `sentinel:auth-required` and `sentinel:logout` functions in your API, all others that query the database will fail.

```
(sentinel:login-manual { user_id 1 permissions ["admin"] })
```

### (`sentinel:login-user` &lt;user\_id&gt;)

[](#sentinellogin-user-user_id)

Verifies if the user exist and forces a login **without** any password. Fails with `422` and sets the `error` field accordingly. When successful, opens a session and loads the `user` field of the session with the data of the user that was just authenticated.

```
(sentinel:login-user 1)
```

### (`sentinel:logout`)

[](#sentinellogout)

Removes authentication status from the active session. Note that this function does not remove the session itself, only the authentication data related to the user. Use `session:destroy` afterwards to fully remove the session completely.

### (`sentinel:reload`)

[](#sentinelreload)

Reloads the active user's session data and permissions from the database. Do not call this function if you logged in in a manual way using `sentinel:login-manual` because the user's data you placed will be overwritten.

### (`sentinel:access-required` &lt;identifier&gt; \[message\])

[](#sentinelaccess-required-identifier-message)

Ensures the provided identifier is not either banned or blocked. Fails with status code `409` and with the default error message if the `message` parameter is not provided.

```
(sentinel:access-required "127.0.0.1" "Your IP has been blocked.")
; If identifier `127.0.0.1` is blocked:
; {"response":409, "error":"@messages.retry_later (60s)", "retry_at":"2024-11-21 11:20:00", "wait":60}

(sentinel:access-required "127.0.0.1" "Your IP has been blocked.")
; If identifier `127.0.0.1` is banned:
; {"response":409, "error":"Your IP has been blocked."}
```

### (`sentinel:access-denied` &lt;identifier&gt; \[action='auto'\] \[wait-timeout=2\] \[block-timeout=30\])

[](#sentinelaccess-denied-identifier-actionauto-wait-timeout2-block-timeout30)

Registers an access-denied attempt for the specified identifier. Returns a string indicating the action taken for the identifier, valid values are `auto`, `wait`, `block`, or `ban`.

```
(sentinel:access-denied "127.0.0.1")
; "wait"
```

### (`sentinel:access-granted` &lt;identifier&gt; \[unban=false\])

[](#sentinelaccess-granted-identifier-unbanfalse)

Grants access to an identifier, calling this will reset the failed and blocked counters. A ban will **continue**to be in effect unless the `unban` parameter is set to `true`.

```
(sentinel:access-granted "127.0.0.1" true)
; null
```

###  Health Score

53

—

FairBetter than 96% of packages

Maintenance97

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community16

Small or concentrated contributor base

Maturity70

Established project with proven stability

 Bus Factor1

Top contributor holds 94.9% 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 ~42 days

Recently: every ~154 days

Total

54

Last Release

18d ago

Major Versions

v1.1.2 → v2.0.02024-04-08

v2.0.6 → v3.0.02024-05-19

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/40846185?v=4)[RedStar Technologies](/maintainers/rsthn)[@rsthn](https://github.com/rsthn)

---

Top Contributors

[![strikeone88](https://avatars.githubusercontent.com/u/50781762?v=4)](https://github.com/strikeone88 "strikeone88 (74 commits)")[![ciachn](https://avatars.githubusercontent.com/u/66389295?v=4)](https://github.com/ciachn "ciachn (3 commits)")[![rsthn](https://avatars.githubusercontent.com/u/40846185?v=4)](https://github.com/rsthn "rsthn (1 commits)")

### Embed Badge

![Health badge](/badges/rsthn-rose-ext-sentinel/health.svg)

```
[![Health](https://phpackages.com/badges/rsthn-rose-ext-sentinel/health.svg)](https://phpackages.com/packages/rsthn-rose-ext-sentinel)
```

###  Alternatives

[vitalybaev/laravel5-dkim

Laravel 5/6 package for signing outgoing messages with DKIM.

3163.1k](/packages/vitalybaev-laravel5-dkim)[denniseilander/laravel-passport-scopes-restriction

Restrict scopes for different Laravel Passport clients.

1636.3k](/packages/denniseilander-laravel-passport-scopes-restriction)

PHPackages © 2026

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