Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 5 additions & 53 deletions docs/0 - install.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Installation

These instructions assume that you have already [installed the CodeIgniter 4 app starter](https://codeigniter.com/user_guide/installation/installing_composer.html) as the basis for your new project, set up your `.env` file, and created a database that you can access via the Spark CLI script.

Installation is done through [Composer](https://getcomposer.org). The example assumes you have it installed globally.
If you have it installed as a phar, or othewise you will need to adjust the way you call composer itself.

Expand Down Expand Up @@ -75,11 +77,12 @@ public $ruleSets = [

## Controller Filters

Shield provides 3 [Controller Filters](https://codeigniter.com/user_guide/incoming/filters.html) you can
Shield provides 4 [Controller Filters](https://codeigniter.com/user_guide/incoming/filters.html) you can
use to protect your routes, `session`, `tokens`, and `chained`. The first two cover the `Session` and
`AccessTokens` authenticators, respectively. The `chained` filter will check both authenticators in sequence
to see if the user is logged in through either of authenticators, allowing a single API endpoint to
work for both an SPA using session auth, and a mobile app using access tokens.
work for both an SPA using session auth, and a mobile app using access tokens. The fourth, `auth-rates`,
provides a good basis for rate limiting of auth-related routes.

These filters are already loaded for you by the registrar class located at `src/Config/Registrar.php`.

Expand Down Expand Up @@ -110,54 +113,3 @@ public $filters = [
]
];
```

## Further Customization

### Route Configuration

If you need to customize how any of the auth features are handled, you will likely need to update the routes to point to the correct controllers. You can still use the `service('auth')->routes()` helper, but you will need to pass the `except` option with a list of routes to customize:

```php
service('auth')->routes($routes, ['except' => ['login', 'register']]);
```

Then add the routes to your customized controllers:

```php
$routes->get('login', '\App\Controllers\Auth\LoginController::loginView');
$routes->get('register', '\App\Controllers\Auth\RegisterController::registerView');
```

### Extending the Controllers

Shield has the following controllers that can be extended to handle
various parts of the authentication process:

- **ActionController** handles the after login and after-registration actions that can be ran, like Two Factor Authentication and Email Verification.

- **LoginController** handles the login process.

- **RegisterController** handles the registration process. Overriding this class allows you to customize the User Provider, the User Entity, and the validation rules.

- **MagicLinkController** handles the "lost password" process that allows a user to login with a link sent to their email. Allows you to
override the message that is displayed to a user to describe what is happening, if you'd like to provide more information than simply swapping out the view used.

It is not recommended to copy the entire controller into app and change it's namespace. Instead, you should create a new controller that extends
the existing controller and then only override the methods needed. This allows the other methods to always stay up to date with any security
updates that might happen in the controllers.

```php
<?php

namespace App\Controllers;

use CodeIgniter\Shield\Controllers\LoginController as ShieldLogin;

class LoginController extends ShieldLogin
{
public function logoutAction()
{
// new functionality
}
}
```
39 changes: 24 additions & 15 deletions docs/1 - concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,47 @@ This document covers some of the base concepts used throughout the library.

## Repository State

Shield is designed so that the initial setup of your application can all happen in code with nothing required to be saved in the database. This means you do not have to create large seeder files that need
to run within each environment. Instead, it can be placed under version control, though the Settings library allows those settings to be easily
stored in the database if you create an interface for the user to update those settings at.
Shield is designed so that the initial setup of your application can all happen in code with nothing required to be
saved in the database. This means you do not have to create large seeder files that need to run within each environment.
Instead, it can be placed under version control, though the Settings library allows those settings to be easily stored
in the database if you create an interface for the user to update those settings.

## Settings

In place of the CodeIgniter `config()` helper, Shield uses the official
[Settings](https://github.com/codeigniter4/settings) library. This provides a way to save any Config class values to the database if you want to modify them, but falls back on the standard Config class if nothing is found in the database.
In place of the CodeIgniter `config()` helper, Shield uses the official [Settings](https://github.com/codeigniter4/settings)
library. This provides a way to save any Config class values to the database if you want to modify them, but falls back
on the standard Config class if nothing is found in the database.

## User Providers

To make the system as flexible as possible, you can define which class should be able to interact with your chosen persistence system to get the user records. Typically this is going to be a Model, and one is provided for you, at `CodeIgniter\Shield\Models\UserModel`. This is defined in `Config\Auth->userProvider`.
You can use your own models to handle user persistence. Shield calls this the "User Provider" class. A default model
is provided for you at `CodeIgniter\Shield\Models\UserModel`. You can change this in the `Config\Auth->userProvider` setting.
The only requirement is that your new class MUST extend the provided `UserModel`.

```php
public $userProvider = 'CodeIgniter\Shield\Models\UserModel';
```

## User Identities

User accounts are stored separately from the information needed to identify that user. These identifying pieces of data we call User Identities. By default, the library has two types of identities: one for standard email/password information, and one for access tokens.
User accounts are stored separately from the information needed to identify that user. These identifying pieces of data are
called User Identities. By default, the library has two types of identities: one for standard email/password information,
and one for access tokens.

By keeping the identity information loosely coupled from the user account itself, it frees the system up to more easily integrate third-party sign-in systems, JWT systems, and more, all on a single user. With small overrides you could even allow a single user to have multiple email/password combinations if your needs demands the functionality.
Keeping these identities loosely coupled from the user account itself facilitates integrations with third-party sign-in systems, JWT systems, and more - all on a single user.

While this has the potential to make the system more complex, the `email` and `password` fields are automatically looked up for you when attempting to access from the User entity. Caution should be used to craft queries that will pull in the `email` field when you need to display it to the user, as you could easily run into some n+1 slow queries otherwise.
While this has the potential to make the system more complex, the `email` and `password` fields are automatically
looked up for you when attempting to access them from the User entity. Caution should be used to craft queries that will pull
in the `email` field when you need to display it to the user, as you could easily run into some n+1 slow queries otherwise.

When you `save($user)` a `User` instance in the `UserModel`, the email/password identity will automatically be updated. If no email/password identity exists, you must
pass both the email and the password to the User instance prior to calling `save()`.
When you `save($user)` a `User` instance in the `UserModel`, the email/password identity will automatically be updated.
If no email/password identity exists, you must pass both the email and the password to the User instance prior to calling `save()`.

## Password Validators

When registering a user account, the user's password must be validated to ensure it matches the security requirements of your application. To make the system as flexible as possible Shield uses a pipeline of
Validators to handle the validation. This allows you turn on or off any validation systems that are appropriate for your application. The following Validators are available:
When registering a user account, the user's password must be validated to ensure it matches the security requirements of
your application. Shield uses a pipeline of Validators to handle the validation. This allows you turn on or off any validation
systems that are appropriate for your application. The following Validators are available:

- **CompositionValidator** validates the makeup of the password itself. This used to include things
like ensuring it contained a symbol, a number, etc. According to the current
Expand All @@ -50,11 +59,11 @@ Validators to handle the validation. This allows you turn on or off any validati
`Config\Auth->maxSimilarity`. The default value is 50, but see the docblock in the config
file for more details. This is enabled by default.
- **DictionaryValidator** will compare the password against a provided file with about 600,000
frequently used passwords as has been seen in various data dumps over the years. If the
frequently used passwords that have been seen in various data dumps over the years. If the
chosen password matches any found in the file, it will be rejected. This is enabled by default.
- **PwnedValidator** is like the `DictionaryValidator`. Instead of comparing to a local file, it
uses a third-party site, [Have I Been Pwned](https://haveibeenpwned.com/Passwords) to check
against a list of over 500 million leaked passwords from many data dumps across the web.
against a list of over 630 million leaked passwords from many data dumps across the web.
The search is done securely, and provides more information than the simple dictionary version.
However, this does require an API call to a third-party which not every application will
find acceptable. You should use either this validator or the `DictionaryValidator`, not both.
Expand Down
15 changes: 10 additions & 5 deletions docs/2 - authentication.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# Authentication

Shield provides a flexible, secure, authentication system for your web apps and API's.
Authentication is the process of determining that a visitor actually belongs to your website,
and identifying them. Shield provides a flexible and secure authentication system for your
web apps and APIs.

## Available Authenticators

Shield ships with 2 authenticators that will serve several typical situations within web app development: the
Session authenticator, which uses username/email/password to authenticate against and stores it in the session,
and Access Tokens authenticator which uses private access tokens passed in the headers.
and the Access Tokens authenticator which uses private access tokens passed in the headers.

The available authenticators are defined in `Config\Auth`:

Expand Down Expand Up @@ -37,9 +39,9 @@ helper('auth');
auth()->user();

// get the current user's id
user_id()
// or
auth()->id()
// or
user_id()
```

## Authenticator Responses
Expand Down Expand Up @@ -152,6 +154,8 @@ auth()->logout();
The `forget` method will purge all remember-me tokens for the current user, making it so they
will not be remembered on the next visit to the site.



## Access Token Authenticator

The Access Token authenticator supports the use of revoke-able API tokens without using OAuth. These are commonly
Expand Down Expand Up @@ -185,6 +189,7 @@ This creates the token using a cryptographically secure random string. The token
is hashed (sha256) before saving it to the database. The method returns an instance of
`CodeIgniters\Shield\Authentication\Entities\AccessToken`. The only time a plain text
version of the token is available is in the `AccessToken` returned immediately after creation.

**The plain text version should be displayed to the user immediately so they can copy it for
their use.** If a user loses it, they cannot see the raw version anymore, but they can generate
a new token to use.
Expand Down Expand Up @@ -236,7 +241,7 @@ Tokens will expire after a specified amount of time has passed since they have b
By default, this is set to 1 year. You can change this value by setting the `accessTokenLifetime`
value in the `Auth` config file. This is in seconds so that you can use the
[time constants](https://codeigniter.com/user_guide/general/common_functions.html#time-constants)
CodeIgniter provides.
that CodeIgniter provides.

```php
public $unusedTokenLifetime = YEAR;
Expand Down
12 changes: 4 additions & 8 deletions docs/3 - auth_actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ Views for all of these pages are defined in the `Auth` config file, with the `$v
];
```

NOTE: a session flag is set with the current action step and the user cannot continue until that
flag has been cleared.

## Defining New Actions

While the provided email-based activation and 2FA will work for many sites, others will have different
Expand All @@ -72,9 +69,8 @@ told the user would be happening. For example, in the `Email2FA` class, this met
sends the email to the user, and then displays the form the user should enter the 6 digit code into.

**verify()** is the final step in the action's journey. It verifies the information the user provided
and provides feedback. One important task is to remove the `auth_action` field from the session so
that a user can proceed through the site like normal. In the `Email2FA` class, it verifies the code
against what is saved in the database and either sends them back to the previous form to try again
or redirects the user to the page that a `login` task would have redirected them to anyway.
and provides feedback. In the `Email2FA` class, it verifies the code against what is saved in the
database and either sends them back to the previous form to try again or redirects the user to the
page that a `login` task would have redirected them to anyway.

All methods should return either a RedirectResponse or string of a view, like through the `view()` method.
All methods should return either a `RedirectResponse` or a view string (e.g. using the `view()` function).
31 changes: 17 additions & 14 deletions docs/4 - authorization.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# Authorization

Shield provides a flexible role-based access control that allows users to belong to multiple groups at once.
Authorization happens once a user has been identified through authentication. It is the process of
determining what actions a user is allowed to do within your site.

Shield provides a flexible role-based access control (RBAC) that allows users to belong to multiple groups at once.
Groups can be thought of as traditional roles (admin, moderator, user, etc), but can also group people together
around features, like Beta feature access, or used to provide discrete groups of users within a forum, etc.
around features, like Beta feature access, or used to provide discrete groups of users within a forum, etc.

## Defining Available Groups

Expand All @@ -19,7 +22,7 @@ public $groups = [
```

The key of the `$groups` array is the common term of the group. This is what you would call when referencing the
group elsewhere, like checking if `$user->inGroup('superadmin')`. By default, the following groups are available:
group elsewhere, like checking if `$user->inGroup('superadmin')`. By default, the following groups are available:
`superadmin`, `admin`, `developer`, `user`, and `beta`.

### Default User Group
Expand All @@ -35,7 +38,7 @@ public $defaultGroup = 'users';

All permissions must be added to the `AuthGroups` config file, also. A permission is simply a string consisting of
a scope and action, like `users.create`. The scope would be `users` and the action would be `create`. Each permission
can have a description for display within UIs if needed.
can have a description for display within UIs if needed.

```php
public $permissions = [
Expand All @@ -53,15 +56,15 @@ public $permissions = [

In order to grant any permissions to a group, they must have the permission assigned to the group, within the `AuthGroups`
config file, under the `$matrix` property. The matrix is an associative array with the group name as the key,
and array of permissions that should be applied to that group.
and an array of permissions that should be applied to that group.

```php
public $matrix = [
'admin' => ['admin.access', 'users.create', 'users.edit', 'users.delete', 'beta.access'],
];
```

You can use a wildcard within a scope to allow all actions within that scope, by using a '*' in place of the action.
You can use a wildcard within a scope to allow all actions within that scope, by using a `*` in place of the action.

```php
public $matrix = [
Expand All @@ -71,17 +74,17 @@ public $matrix = [

## Authorizing Users

When the `Authorization` trait is applied to the user model, it provides the following methods to authorize your users.
The `Authorizable` trait on the `User` entity provides the following methods to authorize your users.

#### can()

Allows you to check if a user is permitted to do a specific action. The only argument is the permission string. Returns
Allows you to check if a user is permitted to do a specific action. The only argument is the permission string. Returns
boolean `true`/`false`. Will check the user's direct permissions first, and then check against all of the user's groups
permissions to determine if they are allowed.

```php
if ($user->can('users.create')) {
//
//
}
```

Expand All @@ -97,15 +100,15 @@ if (! $user->inGroup('superadmin', 'admin')) {

## Managing User Permissions

Permissions can be granted on a user level as well as on a group level. Any user-level permissions granted will
Permissions can be granted on a user level as well as on a group level. Any user-level permissions granted will
override the group, so it's possible that a user can perform an action that their groups cannot.

None of the changes are saved on the User entity until you `save()` with the `UserModel`.

#### addPermission()

Adds one or more permissions to the user. If a permission doesn't exist, a `CodeIgniter\Shield\Authorization\AuthorizationException`
is thrown.
Adds one or more permissions to the user. If a permission doesn't exist, a `CodeIgniter\Shield\Authorization\AuthorizationException`
is thrown.

```php
$user->addPermission('users.create', 'users.edit');
Expand All @@ -114,7 +117,7 @@ $user->addPermission('users.create', 'users.edit');
#### removePermission()

Removes one or more permissions from a user. If a permission doesn't exist, a `CodeIgniter\Shield\Authorization\AuthorizationException`
is thrown.
is thrown.

```php
$user->removePermission('users.delete');
Expand All @@ -134,7 +137,7 @@ $user->syncPermissions(['admin.access', 'beta.access']);
#### addGroup()

Adds one or more groups to a user. If a group doesn't exist, a `CodeIgniter\Shield\Authorization\AuthorizationException`
is thrown.
is thrown.

```php
$user->addGroup('admin', 'beta');
Expand Down
Loading