1. Make it possible to reuse getIdForRequest()
2. Introduce a flag to control if oAuth2 bearer token should be base64
encoded
3. Promote resolve() to locate/validate access tokens by id
This commit adds:
- user.prototype.verify(verifyOptions, options, cb)
- remote method /user/:id/verify
- User.getVerifyOptions()
The remote method can be used to replay the sending of a user
identity/email verification message.
`getVerifyOptions()` can be fully customized programmatically
or partially customized using user model's `.settings.verifyOptions`
`getVerifyOptions()` is called under the hood when calling the
/user/:id/verify remote method
`getVerifyOptions()` can also be used to ease the building
of identity verifyOptions:
```js
var verifyOptions = {
type: 'email',
from: 'noreply@example.com'
template: 'verify.ejs',
redirect: '/',
generateVerificationToken: function (user, options, cb) {
cb('random-token');
}
};
user.verify(verifyOptions);
```
NOTE: the `User.login()` has been modified to return the userId when
failing due to unverified identity/email. This userId can then be used
to call the /user/:id/verify remote method.
Improve the flow for setting/changing/resetting User password to make
it more secure.
1. Modify `User.resetPassword` to create a token scoped to allow
invocation of a single remote method: `User.setPassword`.
2. Scope the method `User.setPassword` so that regular tokens created
by `User.login` are not allowed to execute it.
For backwards compatibility, this new mode (flow) is enabled only
when User model setting `restrictResetPasswordTokenScope` is set to
`true`.
3. Changing the password via `User.prototype.patchAttributes`
(and similar DAO methods) is no longer allowed. Applications
must call `User.changePassword` and ask the user to provide
the current (old) password.
For backwards compatibility, this new mode (flow) is enabled only
when User model setting `rejectPasswordChangesViaPatchOrReplace` is set
to `true`.
Implement a new method for changing user password with password-reset
token but without the old password.
REST API
POST /api/users/reset-password
Authorization: your-password-reset-token-id
Content-Type: application/json
{"newPassword": "new-pass"}
JavaScript API
User.setPassword(userId, newPassword[, cb])
userInstance.setPassword(newPassword[, cb])
Note: the new REST endpoint is not protected by scopes yet, therefore
any valid access token can invoke it (similarly to how any valid access
token can change the password via PATCH /api/users/:id).
Fix the code builing a scoped method to correctly handle the case
when the setup method is called twice and the previously defined
method has to be overriden with new remoting metadata.
Define a new property `AccessToken.scopes` to contain the list of
scopes granted to this access token.
Define a new remote method metadata `accessScopes` to contain a list
of scope name required by this method.
Define a special built-in scope name "DEFAULT" that's used when
a method/token does not provide any scopes. This allows access
tokens to grant access to both the default scope and any additional
custom scopes at the same time.
Modify the authorization algorithm to ensure that at least one
of the scopes required by a remote method is allowed by the scopes
granted to the requesting access token.
The "DEFAULT" scope preserve backwards compatibility because existing
remote methods with no `accessScopes` can be accessed by (existing)
access tokens with no `scopes` defined.
Impact on existing applications:
- Database schema must be updated after upgrading the loopback version
- If the application was already using a custom `AccessToken.scopes`
property with a type different from an array, then the relevant code
must be updated to work with the new type "array of strings".
Currently any `currentUserLiteral` routes when accessed with a bad
token throw a 500 due to a SQL error that is raised because
`Model.findById` is invoked with `id={currentUserLiteral}`
(`id=me` in our case) when the url rewrite fails.
This commit changes the token middleware to return 401 Not Authorized
when the client is requesting a currentUserLiteral route without
a valid access token.
Emit a new method "remoteMethodAdded" whenever a new method is added,
typically when `Model.remoteMethod` or `Model.nestRemoting` is called.
The method is emitted both by the Model affected and the application
object.
Implement a new method for changing user passwords the secure way.
The method requires the old password to be provided before a new
password can be used.
REST API:
POST /api/users/change-password
Authorization: your-token-id
Content-Type: application/json
{"oldPassword":"old-pass", "newPassword": "new-pass"}
JavaScript API:
User.changePassword(userId, oldPassword, newPassword[, cb])
There is also an instance-level (prototype) method that can be used
from JavaScript:
userInstance.changePassword(oldPassword, newPassword[, cb])
Adds an authorizedRoles object to remotingContext.args.options
which contains all the roles (static and dynamic) that are
granted to the user when performing a request through
strong-remoting to an app with authentication enabled.
The authorizedRoles object for example looks like:
{
$everyone: true,
$authenticated: true,
myRole: true
}
NOTE: this pr also covers a number of jsdoc fixes as well
as refactoring in ACL.js and access-context.js
Allow custom properties to be added to Change Model,
and make change filter customizable through mixins
to allow to add the custom property to the filter
used to look up relevant changes during change replication.
Applications using MongoDB connectors typically have `user.id`
property of type ObjectID.
This commit fixes the code building the verification URL to
correctly convert the user id value into string.
Enhance User.prototype.verify to pass the generated token
to the templating function in addition to other existing properties.
This allows application to build multiple URLs in the email template,
for example in order to provide a different URL for desktop and
mobile browsers.
Fixing server/middleware/token.js to handle correctly the
setup of a custom AccessToken model by name in either
middleware.json or using any of :
app.use(loopback.token({...}));
app.middlewareFromConfig(loopback.token, {...})
app.middleware('auth', loopback.token({...})
Modify `app.enableAuth()` to verify that (custom) User and AccessToken
models have correctly configured their hasMany/belongsTo relations
and print a warning otherwise.
Fix the code invalidating access tokens on user email/password changes
to correctly handle the case when the relation
"AccessToken belongs to (subclassed) user" is not configured.
Add a new model-level setting "replicationChunkSize" which allows
users to configure change replication algorithm to issue several
smaller requests to fetch changes and upload updates.
Fix `Role.isOwner()` to check both principalId and principalType.
This fixes a bug where users from different User model were treated
as owners as long as their user id was the same as owner's id.
Fix User.prototype.verify to call `querystring.stringify` instead
of concatenating query-string components directly.
In particular, this fixes the bug where `options.redirect` containing
a hash fragment like `#/home?arg1=value1&arg2=value2` produced incorrect
URL, because the `redirect` value was not correctly encoded.
Allow LoopBack applications to configure multiple User models and share
the same AccessToken model.
To enable this feature:
1) In your custom AccessToken model:
- add a new property "principalType" of type "string".
- configure the relation "belongsTo user" as polymorphic,
using "principalType" as the discriminator
2) In your User models:
- Configure the "hasMany accessTokens" relation as polymorphic,
using "principalType" as the discriminator
When creating custom Role and Principal instances, set your
User model's name as the value of "prinicipalType".
Fix configuration of Karma:
- Disable ES6 modules. The ES6 module transpiler is adding
"use strict" to all source files, this breaks e.g. chai or juggler
- Relax "ignore" setting to exclude only strong-task-emitter,
thus bring back Babel transpilation for chai and juggler.
Before this change, dependencies in node_modules (e.g. strong-remoting)
were not transformed to ES5 and thus crashed the tests in PhantomJS.
Note that loopback-datasource-juggler cannot be babelified to ES5
because it does not correctly support strict mode yet.
Return 401 when the request does not provide any accessToken argument
or the token was not found.
Also simplify the implementation of the `logout` method to make only
a single database call (`deleteById`) instead of `findById` + `delete`.
- Remove ES5 parser exception from .eslintrc
- Configure Karma to use Babel to transpile ES6 sources to ES5,
because PhantomJS does not support ES6
- Upgrade es5-shim to es6-shim
Fix User model to preserve the current session (provided via
"options.accessToken") when invalidating access tokens after a change
of email or password property.
Yesterday, the loopback we are using in our system was upgraded
via npm, and since the upgrade, we noticed that every time
the user model updates, the emailVerified column would change to false.
I took a look and realized there might be an error in
https://github.com/strongloop/loopback/commit/eb640d8
The intent of the commit just mention is to make emailVerified false
when the email gets changed, but notice that ctx.data.email is null
on updates, so the condition is always met and emailVerified always
becomes false.
This commit fixes the issue just mentioned.
Define a new Model method "createOptionsFromRemotingContext" that allows
models to define what "options" should be passed to methods invoked
via strong-remoting (e.g. REST).
Define a new http mapping `http: 'optionsFromRequest'` that invokes
`Model.createOptionsFromRemotingContext` to build the value from
remoting context.
This should provide enough infrastructure for components and
applications to implement their own ways of building the "options"
object.
Currently the return type of Role.getRoles() method is inconsistent:
role names are returned for smart roles and role ids are returned for
static roles (configured through user-role mapping).
This commit adds a new option to Role.getRoles() allowing the caller
to request role names to be returned for all types of roles.
this is needed because we added allowArray flag to persisted model's
remoteMethod, but when relations try to rebuild such methods, it does
not carry over such flags
Notable side-effects:
- loopback no longer exports "caller" and "arguments" properties
- kv-memory connector is now properly added to the connector registry
- the file "test/support.js" was finally removed
- Fix for current implimentation that returned all models that had any
assigned roles. Context was not carried into listByPrincipalType,
setting roleId as null.
In strong-remoting 3.x, we have stricken the coercion of inputs
methods that are expecting an Object will nolonger accept an array
as input, to preserve backwards compatibility we have added flag
allowArray in remote arguments, which would accept an array of objects
- Add a new User setting 'allowEternalTokens'
- Enhance 'AccessToken.validate' to support eternal tokens with ttl
value -1 when the user model allows it.
Fix the definition of "data" argument to
{ type: 'object', model: modelName, ... }
That way strong-remoting passed the request body directly to the model
method (does not create a new model instance), but the swagger will
still provide correct schema for these arguments.
This fixes a bug where upsert in relation methods was adding default
property values to request payload.
Current implementation of `app.model(modelName, settings)`
works as a sugar for model creation. In 3.0, this is
not supported anymore. This implementation reports an
error when sugar is used for model creation.
Includes:
- Updated app.model() method
- Fixed test cases reflecting the change