diff --git a/pages/en/lb3/Accessing-related-models.md b/pages/en/lb3/Accessing-related-models.md
index 11a47bb9b..05d3e94d0 100644
--- a/pages/en/lb3/Accessing-related-models.md
+++ b/pages/en/lb3/Accessing-related-models.md
@@ -11,8 +11,10 @@ summary: For related models, LoopBack automatically related model methods
---
-{% include important.html content="When accessing a related model, the active ACL is still the one for the model you are calling.
-So even if your model has DENY ALL permissions set, if the model relating to it has no ACL, then all the relation endpoints will be open. This can be a security risk because, for example, `GET /OpenModel/{id}/ACLSecuredModel` will allow full access to `ACLSecuredModel` through the `OpenModel` relations.
+{% include important.html content="When accessing a related model, the effective ACL (the one considered to resolve access permission) is the one for the model you are primarily calling.
+So if a given model has no ACL, then all the endpoints accessible through the model relations will be open, even if a related model has DENY ALL permissions set.
+
+This can be a security risk since, for example, `GET /OpenModel/{id}/ACLSecuredModel` will allow full access to `ACLSecuredModel` through the `OpenModel` relations.
" %}
## Restricting access to related models
diff --git a/pages/en/lb3/Authentication-authorization-and-permissions.md b/pages/en/lb3/Authentication-authorization-and-permissions.md
index fdbb6ff59..3b3f5df9f 100644
--- a/pages/en/lb3/Authentication-authorization-and-permissions.md
+++ b/pages/en/lb3/Authentication-authorization-and-permissions.md
@@ -2,6 +2,7 @@
title: "Authentication, authorization, and permissions"
lang: en
layout: navgroup
+toc_level: 2
navgroup: user-mgmt
keywords: LoopBack
tags: authentication
@@ -20,21 +21,11 @@ LoopBack apps access data through models (see [Defining models](Defining-models
so controlling access to data means putting restrictions on models; that is,
specifying who or what can read/write the data or execute methods on the models.
-When you create your app with the LoopBack [application generator](Application-generator.html), access control is automatically enabled, _except_ if you choose the "empty-server" application type.
-To enable access control for an "empty-server" application, you must add a boot
-script that calls `enableAuth()`. For example, in `server/boot/authentication.js`:
-
-```js
-module.exports = function enableAuthentication(server) {
- server.enableAuth();
-};
-```
-
## Access control concepts
LoopBack's access control system is built around a few core concepts, as summarized in the following table.
-| Term | Description | Responsibility | Example |
+| Term | Description | Responsibility | Example |
|---|---|---|---|
| Principal | An entity that can be identified or authenticated. | Represents identities of a request to protected resources. | A user
An application
A role (please note a role is also a principal) |
| Role | A group of principals with the same permissions. | Organizes principals into groups so they can be used. | **Dynamic role**:
`$everyone` (for all users)
`$unauthenticated` (unauthenticated users)
`$owner` (the principal is owner of the model instance), which can be:
◦ A simple property called `userId`
◦ A simple property called `owner`
◦ A relation to a model that extends User.
**Static role**: admin (a defined role for administrators) |
@@ -45,15 +36,222 @@ LoopBack's access control system is built around a few core concepts, as summari
The general process to implement access control for an application is:
-1. **Specify user roles**.
+1. **Implement authentication**:
+ In the application, add code to create (register) new users, log in users (get and use authentication tokens), and log out users.
+2. **Specify user roles**.
Define the user roles that your application requires.
- For example, you might create roles for anonymous users, authorized users, and administrators.
-2. **Define access for each role and model method**.
+ For example, you might create roles for anonymous users, authenticated users, group members, and administrators.
+3. **Define access for each role and model method**.
For example, you might enable anonymous users to read a list of banks, but not allow them to do anything else.
LoopBack models have a set of built-in methods, and each method maps to either the READ or WRITE access type.
- In essence, this step amounts to specifying whether access is allowed for each role and each Model - access type, as illustrated in the example below.
-3. **Implement authentication**:
- in the application, add code to create (register) new users, login users (get and use authentication tokens), and logout users.
+ In essence, this step amounts to specifying whether access is allowed for each role and each Model/access type, as illustrated in the example below.
+ NOTE: you can also map access rights directly to specific users or applications.
+4. **Set-up access control for users**. Do one of:
+ - Statically map users with any role previously created using the Role model. For more information, see [Static roles](Defining-and-using-roles.html#static-roles).
+ - Add code to register dynamic role resolvers that at runtime resolve whether a user, given a preconfigured set of conditions, has a given role. For more information, see [Dynamic roles](Defining-and-using-roles.html#dynamic-roles).
+
+## Initial setup
+
+### Enabling access control
+
+Applications created with the LoopBack [application generator](Application-generator.html)
+have access control enabled by default, _except_ for the "empty-server" application type.
+To enable access control for an "empty-server" application, add a boot
+script that calls [`enableAuth()`](https://apidocs.strongloop.com/loopback/#app-enableauth); for example:
+
+{% include code-caption.html content="server/boot/authentication.js" %}
+```javascript
+module.exports = function enableAuthentication(server) {
+ server.enableAuth();
+};
+```
+
+### Preparing access control models
+
+Make sure the `User` model, (and possibly the `AccessToken` model) is configured appropriately according to your set-up.
+
+Normally you should have already implemented at least one custom user model, extending the built-in `User` model, as described in [Using built-in models](Using-built-in-models.html#user-model).
+
+Usually you don't need to extend or customize the built-in models `Role`, `RoleMapping`, and `ACL`. Just make sure they are declared in the `model-config.json` configuration file, or in case you don't require either to customize the `AccessToken` model that you pass a datasource to the `enableAuth()` method as follows:
+
+```javascript
+server.enableAuth({ datasource: 'db' });
+```
+
+{% include note.html content="
+Passing a `datasource` to the `enableAuth()` method as shown here will let LoopBack take care of attaching any built-in models required by the access control feature, which is suitable for most applications.
+" %}
+
+{% include tip.html content="
+Whether you can use the built-in `AccessToken` model or create a custom accessToken model extending the built-in model depends on whether you plan to use one or several user models extending the built-in `User` model. Both cases are covered in the next two sections.
+" %}
+
+#### Access control with a single user model
+
+If your application leverages only one type of user extending the built-in `User` model (which should the case in a majority of configurations), you have little further configuration to do.
+
+1. Rely on the built-in `AccessToken` model.
+2. Make sure your custom user model implements a hasMany relation with the AccessToken model as follows:
+
+{% include code-caption.html content="common/models/custom-user.json" %}
+```json
+...
+"relations": {
+ "accessTokens": {
+ "type": "hasMany",
+ "model": "AccessToken",
+ "foreignKey": "userId",
+ "options": {
+ "disableInclude": true
+ }
+ }
+},
+...
+```
+
+#### Access control with multiple user models
+
+Having multiple user models may be required in certain situations to deal with significantly different types of users in an application.
+
+If the types of users differ by just a few properties, then it's easiest to overload a single custom user model with all properties required, and differentiate access control behavior with static roles mapped to the different user types.
+
+A more complex situation is when the different types of users differ not just in their properties, but also by their access rights and relations with other models. For example, applications where the concept of an _organization_ is involved, creating intertwined layers of relationships and thus of access control.
+Such circumstances require the application to manage the different user types in separate models.
+
+Consider the following example:
+
+- **Application** has `Users`: _app-admins, app-managers, app-auditors, etc._
+- **Application** has Organizations
+- **Organizations** have `Users`: _org-admins, org-managers, org-marketing, org-sales_
+- **Organizations** have Customers (which are also `Users`)
+
+Such an application has three different types of users:
+
+- `App-Managers`
+- `Org-Managers`
+- `Org-Customers`
+
+Each type of user has different relations with and access rights to the models composing the application.
+
+##### Setup
+
+{% include important.html content="
+When using multiple user models, you should not let LoopBack auto-attach built-in models required by the access control feature. Instead, call the `enableAuth()` method with no argument and manually define all models required in the `server/model-config.json` configuration file.
+" %}
+
+To use several models extending the built-in `User` model, you must modify the relations between the `users` models and the `AccessToken` models to allow a single `AccessToken` model to host access tokens for multiple types of users while at the same time allowing each `user` model instance to be linked to unique related access tokens.
+
+This is achieved by changing the **hasMany** relation from `User` to `AccessToken` and the **belongsTo** relation from `AccessToken` to `User` by their [polymorphic](Polymorphic-relations.html) equivalents, in which the `principalType` property is used as a _discriminator_ to resolve which of the potential `user` model instance an 'accessToken' instance belongs to.
+
+{% include note.html content="Adapt the following configuration snippets in your custom `users` and `accessToken` model definitions.
+"%}
+
+{% include code-caption.html content="common/models/any-custom-user.json" %}
+```json
+...
+"relations": {
+ "accessTokens": {
+ "type": "hasMany",
+ "model": "customAccessToken",
+ "polymorphic": {
+ "foreignKey": "userId",
+ "discriminator": "principalType"
+ },
+ "options": {
+ "disableInclude": true
+ }
+ }
+},
+...
+```
+
+{% include code-caption.html content="common/models/custom-access-token.json" %}
+```json
+...
+"relations": {
+ "user": {
+ "type": "belongsTo",
+ "idName": "id",
+ "polymorphic": {
+ "idType": "string",
+ "foreignKey": "userId",
+ "discriminator": "principalType"
+ }
+ }
+},
+...
+```
+
+{% include important.html content="
+In particular, pay attention to:
+
+* The `model` name used to refer to the access token model in the different user models (here named \"customAccessToken\")
+* The `idName` used for the foreignKey in the access token model referring to the user instance (here named \"id\")
+* The `idType` used for this foreignKey, according to the type of connector used for the related user models (here using \"string\" for a MongoDB connector for example)
+* Use \"principalType\" for the `discriminator` name. This is mandatory and cannot be changed
+" %}
+
+Don't forget to specify the custom `accessToken` model as follows:
+
+{% include code-caption.html content="server/middleware.json" %}
+```json
+{
+ "auth": {
+ "loopback#token": {
+ "params": {
+ "model": "customAccessToken"
+ }
+ }
+ }
+}
+```
+
+**Note**: Alternatively, you can put these lines in the `server.js` file or in a boot script, once again paying attention to the _name_ of the custom `accessToken` model.
+
+{% include code-caption.html content="server/server.js" %}
+```javascript
+var loopback = require('loopback');
+...
+app.use(loopback.token({
+ model: app.models.customAccessToken
+}));
+```
+
+{% include tip.html content="From this point you should be able to use LoopBack access control over multiple user models extending the built-in `User` model, with barely no modification compared to the way you're used to handle it with a single user model.
+" %}
+
+##### Methods and parameters impacted when using multiple user models
+
+{% include important.html content="
+Pay attention to the following methods and parameters impacted by the switch to multiple user models support.
+" %}
+
+Anytime a method is expecting the **principalType** for a principal of the `User` type (as-is or nested in an [AccessContext](https://apidocs.strongloop.com/loopback/#accesscontext) object), provide the name of the targeted `user` model name (e.g. `'oneCustomUserModelName'`) instead of the usual `Principal.USER` (or `'USER'`).
+Such methods include: `Role.getRoles()` and `Role.isInRole()`. For example:
+
+```javascript
+Role.getRoles({
+ principalType: 'oneCustomUserModelName',
+ principalId: 123,
+});
+```
+
+`Role` instance method `Role.prototype.users()`: the method which return all the users mapped with a given role instance should now be called with the following syntax:
+
+```javascript
+roleInstance.users({where: {
+ principalType: 'oneCustomUserModelName'
+});
+```
+
+`RoleMapping` static methods: these methods either accessed directly or through the relation `principals` of the `Role` model should also use the new `principalType` syntax, for example:
+
+```javascript
+roleInstance.principals.create({
+ principalType: 'oneCustomUserModelName',
+ principalId: 123
+});
+```
## Exposing and hiding models, methods, and endpoints
@@ -124,9 +322,9 @@ MyUser.disableRemoteMethod('__get__accessTokens', false);
MyUser.disableRemoteMethod('__updateById__accessTokens', false);
```
-### Read-Only endpoints example
+### Read-only endpoints example
-You may want to only expose read-only operations on your model hiding all POST, PUT, DELETE verbs
+You may want to only expose read-only operations on your model; in other words hiding all operations that use HTTP POST, PUT, DELETE method.
**common/models/model.js**
diff --git a/pages/en/lb3/Controlling-data-access.md b/pages/en/lb3/Controlling-data-access.md
index af7894557..cda128117 100644
--- a/pages/en/lb3/Controlling-data-access.md
+++ b/pages/en/lb3/Controlling-data-access.md
@@ -161,21 +161,19 @@ Each incoming request is mapped to an object with three attributes:
ACL rules are described as an array of objects, each of which consists of attributes listed at
[Model definition JSON file - ACLs](Model-definition-JSON-file.html#acls).
-1. model
-2. property
-3. accessType
-4. principalType
- 1. USER
- 2. APP
- 3. ROLE
- 1. custom roles
- 2. $owner
- 3. $authenticated
- 4. $unauthenticated
- 5. $everyone
-5. permission
- 1. DENY
- 2. ALLOW
+* model
+* property
+* accessType
+* principalType
+ * USER: a user ID
+ * APP: an application ID
+ * ROLE: a role name
+ * _built-in dynamic roles_, one of [`$everyone`, `$unauthenticated`, `$authenticated`, `$owner`]
+ * [_custom static roles_](Defining-and-using-roles.html#static-roles), directly mapped to principals
+ * [_custom dynamic roles_](Defining-and-using-roles.html#dynamic-roles), registered with custom role resolvers
+* permission
+ * DENY
+ * ALLOW
### ACL rule precedence
diff --git a/pages/en/lb3/Model-definition-JSON-file.md b/pages/en/lb3/Model-definition-JSON-file.md
index 3bf1f1d0e..6e65ae226 100644
--- a/pages/en/lb3/Model-definition-JSON-file.md
+++ b/pages/en/lb3/Model-definition-JSON-file.md
@@ -740,7 +740,7 @@ The value of the `acls` key is an array of objects that describes the access
| Key | +Key | Type | Description |
|---|