Skip to content
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

Documentation for AccessToken invalidation #3112

Closed
bajtos opened this issue Jan 17, 2017 · 11 comments
Closed

Documentation for AccessToken invalidation #3112

bajtos opened this issue Jan 17, 2017 · 11 comments
Assignees

Comments

@bajtos
Copy link
Member

bajtos commented Jan 17, 2017

Update our documentation to describe all recent changes (and the new config options) related to access token invalidation

@bajtos
Copy link
Member Author

bajtos commented Jan 24, 2017

We should update the documentation ASAP, we have already had a customer asking about the warning printed by recent LoopBack 2.x versions.

Below is my response, some of the information can be reused in the docs.

Some time ago, we have fixed a security vulnerability in LoopBack. The fix was viewed as backwards-incompatible by some LoopBack users, therefore we introduced a feature flag to enable/disable the fix in 2.x LTS line. The purpose of the new warning is to educate upgrading users about the vulnerability and let them decide how to handle it.

The gist of the vulnerability: when user's account is compromised (e.g. a password is leaked), then the user needs to log out other sessions after changing password to a new (not compromised) one, so that attacker's sessions are invalidated and the attacker can no longer access the system.
 
Users that have implemented their own solution for access-token invalidation should set logoutSessionsOnSensitiveChanges to false, to prevent interference between the built-in invalidation and their solution. This will also disable the warning.
 
All other users should set logoutSessionsOnSensitiveChanges to true and also configure their user model with injectOptionsFromRemoteContext, see the example below:

// server/config.json
{
  "port": 3000,
  // ...
  "logoutSessionsOnSensitiveChanges": true
}

// common/models/my-user.json
{
  "name": "MyUser",
  "base": "User",
  // ...
  "injectOptionsFromRemoteContext": true
}

@michaelfreund
Copy link

@bajtos Thank you for opening this ticket.

It's unclear what injectOptionsFromRemoteContext is exactly doing.

Because of the whole context stuff issue I have setup a test environment trying to get rid off loopback-context and use the option approach.

From your docs

In LoopBack 2.x, this feature is disabled by default for compatibility reasons. To enable, add "injectOptionsFromRemoteContext": true to your model JSON file.

It doesn't matter if I set injectOptionsFromRemoteContext to true or false in model.json config. I have to add optionsFromRequest to accepts array of a remote method to get the options parameter inside the method. So what exactly is injectOptionsFromRemoteContext doing?

@bajtos
Copy link
Member Author

bajtos commented Jan 25, 2017

So what exactly is injectOptionsFromRemoteContext doing?

It adds an options argument with optionsFromRequest to the built-in methods provided by PersistedModel, e.g. PersistedModel.create().

I have to add optionsFromRequest to accepts array of a remote method to get the options parameter inside the method.

Yes, you have to describe the options argument of your custom methods explicitly, it is an intentional part of the design.

It's unclear what injectOptionsFromRemoteContext is exactly doing.

I see your point, in hindsight the name injectOptionsFromRemoteContext truly does not convey enough information about the intent.

Could you please contribute the documentation improvement? Here is the source code of "Using-current-context" page:

And here are instructions for contributing to our documentation:

BTW this discussion is off-topic here. If there is anything else to discuss in relation to injectOptionsFromRemoteContext , then please open a new github issue for that.

@michaelfreund
Copy link

@bajtos Thank you for the detailed answer. I will contribute the documentation improvement for both versions.

@barocsi
Copy link

barocsi commented Jan 28, 2017

injectOptionsFromRemoteContext is unclear that it should only be applied to 2.x because on 3.x it gives an error message

post is using model setting injectOptionsFromRemoteContext which is no longer available.
Please rework your app to use the offical solution for injecting "options" argument from request context,
see https://loopback.io/doc/en/lb3/Using-current-context.html

where we can see its clearly not working with built in 'access' or 'loaded' hooks.

@bajtos
Copy link
Member Author

bajtos commented Jan 30, 2017

Hello, the documentation change was already landed few days ago, see loopbackio/loopback.io#258

@michaelfreund could you please take a look and send a new pull request if there is any information missing?

@barocsi where we can see its clearly not working with built in 'access' or 'loaded' hooks.

Please open a new issue and provide a small app reproducing the issue per our bug reporting instructions.

@bajtos bajtos closed this as completed Jan 30, 2017
@bajtos bajtos reopened this Jan 30, 2017
@ritch
Copy link
Member

ritch commented Jan 30, 2017

Just reviewed 👍 looks good.

@ritch ritch closed this as completed Jan 30, 2017
@michaelfreund
Copy link

michaelfreund commented Jan 31, 2017

@bajtos there is still some information missing. customer.json example in the docs isn't showing the most important fact - the injectOptionsFromRemoteContext option and where to place it.

I will send a pull request with corresponding updates. But before, let's clarify some more facts.

  1. You have to extend the build-in user model to be able to adjust option injectOptionsFromRemoteContext to ensure access token invalidation is working properly.

  2. This is true and only if you are using built-in remote methods for updating user.

  3. If you have a custom remote method for updating user, you have to pass options param to updateAttributes explicitly (like in the example below).

customerModel.updateCurrentUser = function (data, options, next) {
	customerModel.findOne(query, function (err, customer) {
		customer.updateAttributes(data, options); ///< pass options explicitly
	});
};
  1. When having a custom function to update user like above, it doesn't matter if you set injectOptionsFromRemoteContext or not in the models config.

@bajtos
Copy link
Member Author

bajtos commented Jan 31, 2017

customer.json example in the docs isn't showing the most important fact - the injectOptionsFromRemoteContext option and where to place it.

Oh, that's an oversight. The very purpose of customer.json example is to show how to enable injectOptionsFromRemoteContext, I don't know that line could get lost 😱

I will send a pull request with corresponding updates.

Excellent 🙇

You have to extend the build-in user model to be able to adjust option injectOptionsFromRemoteContext to ensure access token invalidation is working properly

Yes.

This is true and only if you are using built-in remote methods for updating user.

Yes.

If you have a custom remote method for updating user, you have to pass options param to updateAttributes explicitly (like in the example below).

Yes. You also need your updateCurrentUser method to properly announce the options argument in its remoting metadata.

When having a custom function to update user like above, it doesn't matter if you set injectOptionsFromRemoteContext or not in the models config.

Yes.

I would also recommend to explicitly disable all remote methods in the built-in User object that could be used to modify user data. I.e. don't rely on the fact that your client apps are calling updateCurrentUser and not using PATCH /api/users/:id, remove the PATCH /api/users/:id endpoint from your API.

I think it's ok to describe this use case in the documentation too, as long as the other use case (a custom model extending User and enabling injectOptionsFromRemoteContext) is documented too.

cc @crandmck

crandmck referenced this issue Jan 31, 2017
Fix User model to preserve the current session (provided via
"options.accessToken") when invalidating access tokens after a change
of email or password property.
@barocsi
Copy link

barocsi commented Apr 13, 2017

There is a sudden issue from the blue, regarding invalidation:
User.prototype.createAccessToken removes all other tokens.
Why? Why cannot an user can be logged in from multiple devices?
Where is options object defined? In the api there is a vague something about it.

	The options for access token, such as scope, appId

such as?

in user.js

 User.observe('after save', function afterEmailUpdate(ctx, next) {
    if (!ctx.instance && !ctx.data) return next();
    if (!ctx.hookState.originalUserData) return next();

    var newEmail = (ctx.instance || ctx.data).email;
    var isPasswordChange = ctx.hookState.isPasswordChange;

    if (!newEmail && !isPasswordChange) return next();

    var userIdsToExpire = ctx.hookState.originalUserData.filter(function(u) {
      return (newEmail && u.email !== newEmail) || isPasswordChange;
    }).map(function(u) {
      return u.id;
    });
    ctx.Model._invalidateAccessTokensOfUsers(userIdsToExpire, ctx.options, next);
  });

Why would you do it? Why is it neccessary to hardcode this logic?
Why is it not possible to override this logic? Why the hook not calls a function that one can override or replace...?

@bajtos
Copy link
Member Author

bajtos commented Apr 18, 2017

There is a sudden issue from the blue, regarding invalidation:
User.prototype.createAccessToken removes all other tokens.
Why? Why cannot an user can be logged in from multiple devices?

See #3112 (comment) and also https://loopback.io//doc/en/lb2/AccessToken-invalidation.html

When a user’s account is compromised (for example their password is leaked or an attacker gains access to their email account), the app needs to be able to prevent continued use of the hijacked account.

To address this case, LoopBack invalidates access tokens (logs out sessions) when a change of password or email was detected. By default, this feature is disabled in 2.x LTS for backwards compatibility and a warning is printed at startup time to notify the app developer about a possible security issue.

In #3071, I proposed a feature allowing app developers to get more control over access-token invalidation. It may be what you are looking for.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants