Strongloop - Data Security

strongloop

https://github.com/strongloop/loopback-example-access-control
http://docs.strongloop.com/display/public/LB/Managing+users#Managingusers-Makingauthenticatedrequestswithaccesstokens
http://docs.strongloop.com/display/LB/User+REST+API#UserRESTAPI-Loginuser
http://docs.strongloop.com/display/public/LB/Registering+users - done reading
http://docs.strongloop.com/display/public/LB/Authentication%2C+authorization%2C+and+permissions
http://docs.strongloop.com/display/public/LB/Tutorial%3A+access+control
http://docs.strongloop.com/display/public/LB/Managing+users

How can we control access to data?

LoopBack apps access data through models, so controlling access to data means putting restrictions on models; that is, specifying who or what can read and write the data or execute methods on the models. LoopBack access controls are determined by access control lists or ACLs. For more information, see Controlling data access.

You're going to set up access control for the Review model. The access controls should enforce the following rules:

  1. Anyone can read reviews, but you must be logged in to create, edit, or delete them.
  2. Anyone can register as a user; then log in and log out.
  3. Logged-in users can create new reviews, and edit or delete their own reviews; however they cannot modify the coffee shop for a review.
slc loopback:acl

Deny everyone all endpoints. This is often the starting point when defining ACLs, because then you can selectively allow access for specific actions.

? Select the model to apply the ACL entry to: Review
? Select the ACL scope: All methods and properties
? Select the access type: All (match all types)
? Select the role: All users
? Select the permission to apply: Explicitly deny access

Now allow everyone to read reviews.

? Select the model to apply the ACL entry to: Review
? Select the ACL scope: All methods and properties
? Select the access type: Read
? Select the role: All users
? Select the permission to apply: Explicitly grant access

Allow authenticated users to write to the create a review; that is, if you're logged in, you can add a review.

? Select the model to apply the ACL entry to: Review
? Select the ACL scope: A single method
? Enter the method name: create
? Select the role: Any authenticated user
? Select the permission to apply: Explicitly grant access

Now, enable the author of a review (its "owner") to make any changes to it.

? Select the model to apply the ACL entry to: Review
? Select the ACL scope: All methods and properties
? Select the access type: Write
? Select the role: The user owning the object
? Select the permission to apply: Explicitly grant access

How to authenticate a user?

Login (authenticate) a user by calling the User.login() method and providing an object containing password and email or username properties as the first parameter. The method returns an access token.

User.login({username: 'foo', password: 'bar'}, function(err, accessToken) {
  console.log(accessToken);
});

You may also specify how long the access token is valid by providing a ttl (time to live) property with a value in milliseconds. For example:

var TWO_WEEKS = 1000 * 60 * 60 * 24 * 7 * 2;
User.login({
  email: 'me@domain.com',           // must provide email or "username"
  password: 'secret',               // required by default
  ttl: TWO_WEEKS                    // keep the AccessToken alive for at least two weeks
}, function (err, accessToken) {
  console.log(accessToken.id);      // => GOkZRwg... the access token
  console.log(accessToken.ttl);     // => 1209600 time to live
  console.log(accessToken.created); // => 2013-12-20T21:10:20.377Z
  console.log(accessToken.userId);  // => 1
});

What happen if authentication is not successful?

If a login attempt is unsuccessful, an error will be returned in the following format.

{
  "status": 401,             // or 400 if the credentails object is invalid
  "message": "login failed"  // could also be "realm is required" or "username or email is required"
}

Over REST, use the POST /users/login endpoint; for example:

curl -X POST -H "Content-Type:application/json" \
-d '{"email": "me@domain.com", "password": "secret", "ttl": 1209600000}' \
http://localhost:3000/api/users/login

The return value is a JSON object with an id property that is the access token to be used in subsequent requests; for example:

{
  "id": "GOkZRwgZ61q0XXVxvxlB8TS1D6lrG7Vb9V8YwRDfy3YGAN7TM7EnxWHqdbIZfheZ",
  "ttl": 1209600,
  "created": "2013-12-20T21:10:20.377Z",
  "userId": 1
}

How to sign a user out?

Use the User.logout() method to log out a user, providing the user's access token as the parameter.

 // login a user and logout
User.login({"email": "foo@bar.com", "password": "bar"}, function(err, accessToken) {
  User.logout(accessToken.id, function(err) {
    // user logged out
  });
});

// logout a user (server side only)
User.findOne({email: 'foo@bar.com'}, function(err, user) {
  user.logout();
});

Over REST, use the POST /users/logout endpoint, again providing the user's access token in the sid property of the POST payload. To destroy access tokens over REST API, use the POST /users/logout endpoint.

ACCESS_TOKEN=6Nb2ti5QEXIoDBS5FQGWIz4poRFiBCMMYJbYXSGHWuulOuy0GTEuGx2VCEVvbpBK
VERB=POST # any verb is allowed

# Authorization Header
curl -X VERB -H "Authorization: $ACCESS_TOKEN" \
http://localhost:3000/api/users/logout

# Query Parameter
curl -X VERB http://localhost:3000/api/users/logout?access_token=$ACCESS_TOKEN

What features are provided by the built-in User model?

LoopBack's built-in User model provides essential user management features such as:

  • Registration and confirmation via email.
  • Login and logout.
  • Creating an access token.
  • Password reset.

You can extend the User model to suit your specific needs, so in most cases, you don't need to create your own User model from scratch.

The LoopBack User model provides methods to register new users and confirm their email addresses. You can also use the loopback-component-passport module to integrate login with FaceBook, Google, and other third-party providers.

How to verify user email address?

Typically, an application will require users to verify their email addresses before being able to login. This will send an email to the user containing a link to verify their address. Once the user follows the link they will be redirected to web root ("/") and will be able to login normally.

This example creates a remote hook on the User model executed after the create() method is called.

/common/models/user.js:

var config = require('../../server/config.json');
var path = require('path');
module.exports = function(user) {
  //send verification email after registration
  user.afterRemote('create', function(context, user) {
    console.log('> user.afterRemote triggered');
    var options = {
      type: 'email',
      to: user.email,
      from: 'noreply@loopback.com',
      subject: 'Thanks for registering.',
      template: path.resolve(__dirname, '../../server/views/verify.ejs'),
      redirect: '/verified',
      user: user
    };
    user.verify(options, function(err, response) {
      if (err) {
        next(err);
        return;
      }
      console.log('> verification email sent:', response);
      context.res.render('response', {
        title: 'Signed up successfully',
        content: 'Please check your email and click on the verification link '
          + 'before logging in.',
        redirectTo: '/',
        redirectToLinkText: 'Log in'
      });
    });
  });
...

For a complete example, see user.js in loopback-faq-user-management.

How can we use the ACL generator to define access control?

slc loopback:acl
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License