Permalink
Please sign in to comment.
Showing
with
205 additions
and 4 deletions.
- +204 −3 README.md
- +1 −1 docs.json
- BIN ids_and_credentials.png
207
README.md
| @@ -1,4 +1,205 @@ | ||
| -loopback-passport | ||
| -================= | ||
| +# loopback-passport | ||
| -LoopBack passport integration to support third party logins and account linking | ||
| +The module provides integration between [LoopBack](http://loopback.io) and | ||
| +[Passport](http://passportjs.org) to support third party login and account | ||
| +linking for LoopBack applications. | ||
| + | ||
| +# Use cases | ||
| + | ||
| +## Third party login | ||
| + | ||
| +Social login becomes popular these days as our users don’t want to deal with so | ||
| +many identities. It would be nice to allow the use of a third party provider | ||
| +such as Facebook, Google, Twitter, or Github to log into LoopBack. The login | ||
| +profiles will be tracked and associated with corresponding LoopBack users. | ||
| + | ||
| +## Linked accounts | ||
| + | ||
| +In LoopBack, most APIs will be built using models that are backed by data | ||
| +sources, which in turn uses connectors to interact with other systems or cloud | ||
| +services. Some of the backend systems require user-specific credentials to | ||
| +access the protected resources. For example, an e-commerce engine requires the | ||
| +user credential to see the order history. It’s also true to get pictures from | ||
| +one or more facebook accounts. One solution to this requirement is to link or | ||
| +pre-authorize a LoopBack user to other accounts. | ||
| + | ||
| +# Key components | ||
| + | ||
| + | ||
| + | ||
| +## UserIdentity model | ||
| + | ||
| +UserIdentity model keeps track of 3rd party login profiles. Each user identity | ||
| +is uniquely identified by provider and externalId. UserIdentity model comes with | ||
| +a 'belongsTo' relation to the User model. | ||
| + | ||
| +Properties | ||
| + | ||
| +- {String} provider: The auth provider name, such as facebook, google, twitter, linkedin | ||
| +- {String} authScheme: The auth scheme, such as oAuth, oAuth 2.0, OpenID, OpenID Connect | ||
| +- {String} externalId: The provider specific user id | ||
| +- {Object} profile: The user profile, see http://passportjs.org/guide/profile | ||
| +- {Object} credentials | ||
| + - oAuth: token, tokenSecret | ||
| + - oAuth 2.0: accessToken, refreshToken | ||
| + - OpenID: openId | ||
| + - OpenID Connect: accessToken, refreshToken, profile | ||
| +- {*} userId: The LoopBack user id | ||
| +- {Date} created: The created date | ||
| +- {Date} modified: The last modified date | ||
| + | ||
| +## UserCredential model | ||
| + | ||
| +UserCredential has the same set of properties as UserIdentity. It's used to | ||
| +store the credentials from a third party authentication/authorization provider | ||
| +to represent the permissions/authorizations from a user from the third party | ||
| +system. | ||
| + | ||
| +## ApplicationCredential model | ||
| + | ||
| +Interacting with third party systems often require some client application level | ||
| +credentials. For example, you will need oAuth 2.0 client id and client secret to | ||
| +call facebook APIs. Such credentials can be supplied from a configuration file | ||
| +to your server globally. But if your server accepts API requests from multiple | ||
| +client applications, each client application should have its own credentials. To | ||
| +support the multi tenancy, this module provides the ApplicationCredential model | ||
| +to store credentials associated with a client application. | ||
| + | ||
| +Properties | ||
| + | ||
| +- {String} provider: The auth provider name, such as facebook, google, twitter, linkedin | ||
| +- {String} authScheme: The auth scheme, such as oAuth, oAuth 2.0, OpenID, OpenID Connect | ||
| +- {Object} credentials: The provider specific credentials | ||
| + - openId: {returnURL: String, realm: String} | ||
| + - oAuth2: {clientID: String, clientSecret: String, callbackURL: String} | ||
| + - oAuth: {consumerKey: String, consumerSecret: String, callbackURL: String} | ||
| +- {Date} created: The created date | ||
| +- {Date} modified: The last modified date | ||
| + | ||
| +ApplicationCredential model comes with a 'belongsTo' relation to the Application | ||
| +model. | ||
| + | ||
| +## PassportConfigurator | ||
| + | ||
| +PassportConfigurator is the bridge between LoopBack and Passport. | ||
| + | ||
| +- set up models with LoopBack | ||
| +- initialize passport | ||
| +- load the provider configuration and set them as Passport strategies with | ||
| +corresponding routes for auth and callback. | ||
| + | ||
| +# Flows | ||
| + | ||
| +## Third party login flow | ||
| + | ||
| +1. A visitor uses Facebook (or other providers) login | ||
| +2. LoopBack receives the Facebook profile | ||
| +3. LoopBack searches the UserIdentity model by (provider, externalId) to see | ||
| +there is an existing LoopBack user for the given Facebook id | ||
| +4. If yes, set the LoopBack user to the current context | ||
| +5. If not, create a LoopBack user from the profile and create a corresponding | ||
| +record in UserIdentity to track the 3rd party login. Set the newly created user | ||
| +to the current context. | ||
| + | ||
| +## Third party account linking flow | ||
| + | ||
| +1. Log into LoopBack first | ||
| +2. Access a backend on behalf of the logged in LoopBack user | ||
| +3. Link accounts for a given backend to the current user | ||
| + | ||
| +# Use the module with a LoopBack application | ||
| + | ||
| +A demo application is built with this module to showcase how to use the APIs | ||
| +with a LoopBack application. The code is available at: | ||
| + | ||
| +[https://github.com/strongloop-community/loopback-example-passport](https://github.com/strongloop-community/loopback-example-passport) | ||
| + | ||
| +## Configure third party providers | ||
| + | ||
| +The following example shows two providers: facebook-login for login with | ||
| +facebook and google-link for linking your google accounts with the current | ||
| +LoopBack user. | ||
| + | ||
| +```json | ||
| +{ | ||
| + "facebook-login": { | ||
| + "provider": "facebook", | ||
| + "module": "passport-facebook", | ||
| + "clientID": "{facebook-client-id-1}", | ||
| + "clientSecret": "{facebook-client-secret-1}", | ||
| + "callbackURL": "http://localhost:3000/auth/facebook/callback", | ||
| + "authPath": "/auth/facebook", | ||
| + "callbackPath": "/auth/facebook/callback", | ||
| + "successRedirect": "/auth/account", | ||
| + "scope": ["email"] | ||
| + }, | ||
| + ... | ||
| + "google-link": { | ||
| + "provider": "google", | ||
| + "module": "passport-google-oauth", | ||
| + "strategy": "OAuth2Strategy", | ||
| + "clientID": "{google-client-id-2}", | ||
| + "clientSecret": "{google-client-secret-2}", | ||
| + "callbackURL": "http://localhost:3000/link/google/callback", | ||
| + "authPath": "/link/google", | ||
| + "callbackPath": "/link/google/callback", | ||
| + "successRedirect": "/link/account", | ||
| + "scope": ["email", "profile"], | ||
| + "link": true | ||
| + } | ||
| +} | ||
| +``` | ||
| + | ||
| +**NOTE** | ||
| + | ||
| +You'll need to register with facebook and google to get your own client id and | ||
| +client secret. | ||
| + | ||
| +- Facebook: https://developers.facebook.com/apps | ||
| +- Google: https://console.developers.google.com/project | ||
| + | ||
| +## Add code snippets to app.js | ||
| + | ||
| +``` | ||
| +var loopback = require('loopback'); | ||
| +var path = require('path'); | ||
| +var app = module.exports = loopback(); | ||
| + | ||
| +// Create an instance of PassportConfigurator with the app instance | ||
| +var PassportConfigurator = require('loopback-passport').PassportConfigurator; | ||
| +var passportConfigurator = new PassportConfigurator(app); | ||
| + | ||
| +app.boot(__dirname); | ||
| + | ||
| +... | ||
| + | ||
| +// Enable http session | ||
| +app.use(loopback.session({ secret: 'keyboard cat' })); | ||
| + | ||
| +// Load the provider configurations | ||
| +var config = {}; | ||
| +try { | ||
| + config = require('./providers.json'); | ||
| +} catch(err) { | ||
| + console.error('Please configure your passport strategy in `providers.json`.'); | ||
| + console.error('Copy `providers.json.template` to `providers.json` and replace the clientID/clientSecret values with your own.'); | ||
| + process.exit(1); | ||
| +} | ||
| + | ||
| +// Initialize passport | ||
| +passportConfigurator.init(); | ||
| + | ||
| +// Set up related models | ||
| +passportConfigurator.setupModels({ | ||
| + userModel: app.models.user, | ||
| + userIdentityModel: app.models.userIdentity, | ||
| + userCredentialModel: app.models.userCredential | ||
| +}); | ||
| + | ||
| +// Configure passport strategies for third party auth providers | ||
| +for(var s in config) { | ||
| + var c = config[s]; | ||
| + c.session = c.session !== false; | ||
| + passportConfigurator.configureProvider(s, c); | ||
| +} | ||
| +``` |
2
docs.json
| @@ -1,8 +1,8 @@ | ||
| { | ||
| "title": "LoopBack Passport Integration", | ||
| "content": [ | ||
| - "lib/models/application-credential.js", | ||
| "lib/models/user-identity.js", | ||
| + "lib/models/application-credential.js", | ||
| "lib/passport-configurator.js" | ||
| ] | ||
| } |
0 comments on commit
fef3bea