API fine grained Authorization using Roles and ACLs in LoopBack
Connecting authentication to the front end
Third party and social (facebook, twitter) logins using OAuth2
Adding Node middleware for proxying, authentication, and traffic throttling
4. LOOPBACK FEATURES
Model-driven API development
Dynamic REST API endpoint generation
Connect to any datasource (SQL, NoSQL, REST, SOAP)
Rich model relations
Access controls (built in token authentication)
Geolocation, push notifications, offline sync
Angular, Android, and iOS SDKs
5. STEP 1: INSTALL STRONGLOOP TOOLS
~$ npm install g strongloop
And scaffold your application:
~$ slc loopback
7. CREATING A MODEL VIA CLI
~/myapp$ slc loopback:model
[?] Enter the model name: CoffeeShop
[?] Select the datasource to attach CoffeeShop to: (Use arrow keys)
❯ mdb (mongodb)
[?] Select model's base class: (Use arrow keys)
Model
❯ PersistedModel
ACL
[?] Expose CoffeeShop via the REST API? (Y/n) Y
[?] Custom plural form (used to build REST URL):
8. CREATING A MODEL VIA CLI
[?] Property name: name
invoke loopback:property
[?] Property type: (Use arrow keys)
❯ string
number
boolean
object
array
date
...
[?] Required? (y/N)
When complete, simply hit "enter" with a blank property
25. ADDING USERS TO A CUSTOM ROLE
theAdminRole.principals.create({
principalType: app.models.RoleMapping.USER,
principalId: someUser.id
}, function(err, principal) {
// handle the error!
cb(err);
});
26. CUSTOM DYNAMIC ROLES
Use Role.registerResolver() to set up a custom role handler:
// server/boot/roles.js
Role.registerResolver('admin', function(role, context, cb) {
// custom method you create to determine this...
determineAdminStatus(function(err, isAuthorized) {
if (err) {
// maybe handle the error here?
return cb(err);
}
// execute callback with a boolean
cb(null, isAuthorized);
});
});
27. ACCESS CONTROL LAYERS
A layer defines what access a principal has for a certain
operation against a specific model.
28. WHITELISTING
For example, we might create these layers:
Deny everyone to access the model
Allow '$everyone' role to read instances
Allow '$authenticated' role to create instances
Allow '$owner' to update an existing instance
This is an example of "whitelisting", and is safer than
denying specific operations.
29. DEFINING AN ACL
We use the slc loopback:acl subcommand:
~/myapp$ slc loopback:acl
30. DEFINING AN ACL
The CLI will ask you some questions...
~/myapp$ slc loopback:acl
? Select the model to apply the ACL entry to: CoffeeShop
? 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
31. DEFINING AN ACL
Now allow everyone to read CoffeeShops:
~/myapp$ slc loopback:acl
? Select the model to apply the ACL entry to: CoffeeShop
? 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
32. DEFINING AN ACL
Here's what this looks like in the config file:
// in common/models/coffeeshop.json
"acls": [
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY"
},
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
]
33. DEFINING ACLS
Access Control Layers execute in order, so be sure to DENY
first, then add all of your "whitelisting".
34. USING OWNERS
By creating a belongsTo relation, we can use $owner:
"acls": [
// ...,
{
"accessType": "WRITE",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW"
}
]
35. RESTRICTING SPECIFIC METHODS
We can ALLOW or DENY access to specific remote methods:
"acls": [
// ...,
{
"accessType": "EXECUTE",
"principalType": "ROLE",
"principalId": "admin",
"permission": "ALLOW",
"property": "create"
}
]
41. RESTRICTING REMOTE METHODS
Remember, we can ALLOW or DENY access to specific
remote methods on a model, including custom ones!
"acls": [
// ...,
{
"accessType": "EXECUTE",
"principalType": "ROLE",
"principalId": "$unauthenticated",
"permission": "DENY",
"property": "status"
}
]
42. GETTING THE CURRENT USER
At any point in your application you can get the current
user access token, and the user ID:
// common/models/coffeeshop.js
var loopback = require('loopback');
module.exports = function(CoffeeShop) {
CoffeeShop.status = function(id, cb) {
var context = loopback.getCurrentContext();
var token = context.get('accessToken');
console.log( 'Access Token ID', token.id );
console.log( 'Current User ID', token.userId );
};
};
43. CONNECTING TO THE FRONT END
LoopBack has client SDKs for Angular, iOS, Android, and
Xamarin!
44. STEP ONE
The first step is to generate the client code.
Here we use the Angular generator:
~/myapp$ mkdir client/js/services
~/myapp$ lbng server/server.js client/js/services/lbservices.js
45. ANGULAR MODELS
LoopBack provides models for you to use in Angular which
have all of the remote methods (create, find, destroy, etc).
For User objects this includes the login() method!
46. ANGULAR MODELS
When you create your application modules, just include the
LoopBack Services we created from the CLI:
angular.module('myapp', ['ui.router', 'lbServices'])
.config( ... )
.run( ... );
47. CREATING AN AUTH SERVICE
Now we can create an Angular service for authentication:
angular.module('myapp').factory(
'AuthService',
['User', '$q', '$rootScope', function(User, $q, $rootScope) {
function login(email, password) {
return User
.login({ email: email, password: password })
.$promise
.then(function(response) {
$rootScope.currentUser = {
id: response.user.id,
tokenId: response.id
};
});
}
return {
login: login
};
}]
);
48. CREATING AN AUTH SERVICE
The logout() method is easy!
function logout() {
return User
.logout()
.$promise
.then(function() {
$rootScope.currentUser = null;
});
};
49. CREATING AN AUTH SERVICE
When you use the User.login() method (and it is successful),
LoopBack will send an Authorization header with each
request containing the accessToken!
50. USING 3RD PARTY AUTH
Integrating with 3rd party authentication services is easy!
Just use the .passport LoopBack component
51. 3RD PARTY AUTH
The basic workflow is:
1. Visitor clicks "login with X" button (i.e. Facebook (FB))
2. LoopBack (LB) redirects to FB login page
3. FB redirects to your LB server
4. LB requests access token from FB
5. LB uses token to get user info
6. LB matches info to internal User model
(LB creates the user locally if it doesn't exist)
7. User info is added to the LB context
52. 3RD PARTY AUTH
First, install the passport component:
~/myapp$ npm install save loopbackcomponentpassport
Then set up a PassportConfigurator...
(This is the piece that connects LoopBack to Passport.)
53. CONFIGURING PASSPORT
We need to configure Passport, a boot script works:
// server/boot/setupauth.js
var loopback = require('loopback'),
PPConfigurator = require('loopbackcomponentpassport').PassportConfigu
module.exports = function(app) {
// Enable http sessions
app.use(loopback.session({ secret: 'supersecretstring' }));
var configurator = new PPConfigurator(app);
configurator.init();
configurator.setupModels({
userModel: app.models.user,
userIdentityModel: app.models.userIdentity,
userCredentialModel: app.models.userCredential
});
configurator.configureProvider('facebooklogin', {
// ...
});
};
55. ADDITIONAL PASSPORT STEPS
Fill in your FB/Google/etc app details
Create a UI button linking to /auth/facebook
Set up a page at /auth/account (or wherever successRedirect
points)
Add other auth providers!
See an example app here:
https://github.com/strongloop/loopback-example-passport
56. BEING AN OAUTH PROVIDER
Want to be your own OAuth provider?
(instead of using Facebook, Google, etc)
57. BEING AN OAUTH PROVIDER
You can use StrongLoop's !oauth2 LoopBack component
~/myapp$ npm install save loopbackcoponentoath2
* Note that this component requires a license.
61. WHAT ABOUT RATE LIMITING AND PROXY?
Create more middleware at specific phases.
Capture the request and evaluate before passing it on.
Centralized in front of your resource server!
62. DEMO PROJECT
https://github.com/jakerella/lb-central
You can use this demo project as a starting point:
~$ git clone https://github.com/jakerella/lbcentral.git
~$ cd lbcentral && npm install
NOTE: This code is licensed by StrongLoop, you will need a license to use it!
63. RATE LIMITING YOUR API
Control how many requests a client can
make within a time period.
Simply add the middleware config!
65. REQUEST PROXIES
Once the the user is authenticated, rate limiting is
compelte, and any other centralized code, we can proxy
requests to their final destination.
66. PROXYING REQUESTS
Send requests from this server to the resource server(s):
// in middleware.json
"routes:after": {
"./middleware/proxy": {
"params": {
"rules": [
"^/api/foo/(.*)$ https://serviceone.com:3007/api/$1 [P]",
"^/api/bar/(.*)$ https://servicetwo.com:3001/api/v2/$1 [P]"
]
}
}
}