-
Notifications
You must be signed in to change notification settings - Fork 383
Improve auth-setup instructions #304
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -68,55 +68,88 @@ module.exports = function enableAuthentication(server) { | |
|
|
||
| ### Preparing access control models | ||
|
|
||
| Make sure the `User` model, (and possibly the `AccessToken` model) is configured appropriately according to your set-up. | ||
| You must configure the `User` model(s), (and possibly the `AccessToken` model) 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). | ||
| The best practice is to implement at least one custom user model extending the built-in `User` model instead of using the built-in `User` model as-is, 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: | ||
| 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 if you don't need to customize the `AccessToken` model, 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 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. | ||
| {% include tip.html content="Whether you can use the built-in `AccessToken` model or create a custom model that extends `AccessToken` depends on whether you plan to use one or several user models extending the built-in `User` model, as described in the following 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. | ||
| In the most common scenario, an application uses only one model that extends the built-in `User` model | ||
| and uses the built-in `AccessToken` model. In this case, you need to change the "belongsTo" relation of the built-in `AccessToken` model to refer to your custom User model. To do this, edit `server/model-config.json` file as follows: | ||
|
|
||
| 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="server/model-config.json" %} | ||
| ```json | ||
| { | ||
| // ... | ||
| "AccessToken": { | ||
| "dataSource": "db", | ||
| "public": false, | ||
| "relations": { | ||
| "user": { | ||
| "type": "belongsTo", | ||
| "model": "user", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be better to use a model named "customer" or "myUser" or anything other than "user" which is confusingly close to "User", the name of the built-in model....?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that @ebarault Unless you object, I will change "user" to "Customer".
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'm good with user for @bajtos 's reasons
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BTW : while being a good practice in any case: is this absolutely required for single user model scenario since you've fixed the possibility of the missing relation with user here ?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Well, it's not absolutely required, but only as long as we don't introduce another place that relies of this relation being correctly set up. In strongloop/loopback#3231, I added a big warning that's printed when this relation is not configured, to prevent situations like that to reappear in the future. |
||
| "foreignKey": "userId" | ||
| } | ||
| } | ||
| } | ||
| // ... | ||
| } | ||
| ``` | ||
|
|
||
| {% include tip.html content="Model relations are preserved when extending models, therefore your custom `User` model will automatically have a hasMany relation to the default `AccessToken` model. | ||
| " %} | ||
|
|
||
| ##### Customizing the AccessToken model | ||
|
|
||
| If your application needs to customize the `AccessToken` model, for example | ||
| to add extra properties, then you need to modify the `User` model to use | ||
| the new `AccessToken` model for authentication. | ||
|
|
||
| In your custom `User` model definition file, make sure the `relations` section | ||
| configures the "accessTokens" relation to use your custom `AccessToken` model | ||
| as shown here: | ||
|
|
||
| {% include code-caption.html content="common/models/custom-user.json" %} | ||
| ```json | ||
| ... | ||
| "relations": { | ||
| "accessTokens": { | ||
| "type": "hasMany", | ||
| "model": "AccessToken", | ||
| "foreignKey": "userId", | ||
| "options": { | ||
| "disableInclude": true | ||
| } | ||
| } | ||
| }, | ||
| ... | ||
| { | ||
| "name": "CustomUser", | ||
| "base": "User", | ||
| // ... | ||
| "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. | ||
| An application with significantly different types of users may require multiple user models. | ||
|
|
||
| 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. | ||
| If however the different types of users have different access rights and relations with other models | ||
| or if their properties differ too much from each other, | ||
| then you may need to use multiple distinct user models. For example, applications where the concept of an _organization_ is involved, creating intertwined layers of relationships and thus of access control. | ||
|
|
||
| Consider the following example: | ||
|
|
||
|
|
@@ -135,57 +168,65 @@ Each type of user has different relations with and access rights to the models c | |
|
|
||
| ##### 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. | ||
| {% 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. | ||
| 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. In addition to having custom user models this requires you also define a **custom AccessToken** model extending the built-in `AccessToken` model. | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i added this part for the sake a clarity |
||
| {% 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 | ||
| { | ||
| "name": "AnyCustomUser", | ||
| "base": "User", | ||
| // .. | ||
| "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" | ||
| { | ||
| "name": "CustomAccessToken", | ||
| "base": "AccessToken", | ||
| // .. | ||
| "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 `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 | ||
|
|
@@ -196,13 +237,15 @@ 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" | ||
| "model": "CustomAccessToken" | ||
| } | ||
| } | ||
| } | ||
| // ... | ||
| } | ||
| ``` | ||
|
|
||
|
|
@@ -213,7 +256,7 @@ Don't forget to specify the custom `accessToken` model as follows: | |
| var loopback = require('loopback'); | ||
| ... | ||
| app.use(loopback.token({ | ||
| model: app.models.customAccessToken | ||
| model: app.models.CustomAccessToken | ||
| })); | ||
| ``` | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i just wanted here to avoid the repetition
and tried to be a bit more directive so do get developers' confidence