From 97e625e8f271fd5fe72d5f4aed71fe6acfdafe4a Mon Sep 17 00:00:00 2001 From: Paolo Furini Date: Sat, 28 May 2016 16:42:05 +0200 Subject: [PATCH] feat(authentication): add support for auth0 login using lock Adds an Auth0Lock provider class that wraps the Auth0-Lock library. It activates when the oauthType property is equal to 'auth0-lock', and expects some peculiar properties in its configuration block (see the sample config in baseConfig.js). TODO: proper testing; better way to load it (provider extensibility api) --- config.js | 1 + package.json | 12 +------ src/auth0Lock.js | 80 +++++++++++++++++++++++++++++++++++++++++++ src/authentication.js | 13 +++++-- src/baseConfig.js | 14 ++++++++ 5 files changed, 106 insertions(+), 14 deletions(-) create mode 100644 src/auth0Lock.js diff --git a/config.js b/config.js index 2dffe1e..c837497 100644 --- a/config.js +++ b/config.js @@ -12,6 +12,7 @@ System.config({ "aurelia-fetch-client": "npm:aurelia-fetch-client@1.0.0-beta.1.2.1", "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.1.2.0", "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.1.2.0", + "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.2.2", "aurelia-pal-browser": "npm:aurelia-pal-browser@1.0.0-beta.1.2.0", "aurelia-path": "npm:aurelia-path@1.0.0-beta.1.2.1", "aurelia-polyfills": "npm:aurelia-polyfills@1.0.0-beta.1.1.1", diff --git a/package.json b/package.json index 62db9e6..4deb699 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ }, "jspm": { "registry": "npm", - "jspmPackage": true, "main": "aurelia-authentication", "format": "amd", "directories": { @@ -35,16 +34,7 @@ "aurelia-fetch-client": "^1.0.0-beta.1.2.1", "aurelia-logging": "^1.0.0-beta.1.2.0", "aurelia-metadata": "^1.0.0-beta.1.2.0", - "aurelia-path": "^1.0.0-beta.1.2.1", - "aurelia-router": "^1.0.0-beta.1.2.0", - "extend": "^3.0.0" - }, - "peerDependencies": { - "aurelia-api": "^3.0.0-rc1", - "aurelia-dependency-injection": "^1.0.0-beta.1.2.0", - "aurelia-fetch-client": "^1.0.0-beta.1.2.1", - "aurelia-logging": "^1.0.0-beta.1.2.0", - "aurelia-metadata": "^1.0.0-beta.1.2.0", + "aurelia-pal": "^1.0.0-beta.1.2.2", "aurelia-path": "^1.0.0-beta.1.2.1", "aurelia-router": "^1.0.0-beta.1.2.0", "extend": "^3.0.0" diff --git a/src/auth0Lock.js b/src/auth0Lock.js new file mode 100644 index 0000000..2da8d19 --- /dev/null +++ b/src/auth0Lock.js @@ -0,0 +1,80 @@ +import {inject} from 'aurelia-dependency-injection'; +import {PLATFORM} from 'aurelia-pal'; +import extend from 'extend'; + +import {Storage} from './storage'; +import {BaseConfig} from './baseConfig'; + +@inject(Storage, BaseConfig) +export class Auth0Lock { + constructor(storage, config) { + this.storage = storage; + this.config = config; + this.defaults = { + name: null, + state: null, + scope: null, + scopeDelimiter: null, + redirectUri: null, + clientId: null, + clientDomain: null, + display: 'popup', + lockOptions: { + popup: true + }, + popupOptions: null, + responseType: 'token' + }; + } + + open(options, userData) { + // check pre-conditions + if (typeof PLATFORM.global.Auth0Lock !== 'function') { + throw new Error('Auth0Lock was not found in global scope. Please load it before using this provider.'); + } + const provider = extend(true, {}, this.defaults, options); + const stateName = provider.name + '_state'; + + if (typeof provider.state === 'function') { + this.storage.set(stateName, provider.state()); + } else if (typeof provider.state === 'string') { + this.storage.set(stateName, provider.state); + } + + this.lock = this.lock || new PLATFORM.global.Auth0Lock(provider.clientId, provider.clientDomain); + + const openPopup = new Promise(function(resolve, reject) { + let opts = provider.lockOptions; + opts.popupOptions = provider.popupOptions; + opts.responseType = provider.responseType; + opts.callbackURL = provider.redirectUri; + opts.authParams = opts.authParams || {}; + if (provider.scope) opts.authParams.scope = provider.scope; + if (provider.state) opts.authParams.state = this.storage.get(provider.name + '_state'); + + this.lock.show(provider.lockOptions, (err, profile, tokenOrCode) => { + if (err) { + reject(err); + } else { + resolve({ + //NOTE: this is an id token (JWT) and it shouldn't be named access_token + access_token: tokenOrCode + }); + } + }); + }.bind(this)); + + return openPopup + .then(lockResponse => { + if (provider.responseType === 'token' || + provider.responseType === 'id_token%20token' || + provider.responseType === 'token%20id_token' + ) { + return lockResponse; + } + //NOTE: 'code' responseType is not supported, this is an OpenID response (JWT token) + // and code flow is not secure client-side + throw new Error('Only `token` responseType is supported'); + }); + } +} diff --git a/src/authentication.js b/src/authentication.js index b6f542a..337d28d 100644 --- a/src/authentication.js +++ b/src/authentication.js @@ -6,14 +6,16 @@ import {BaseConfig} from './baseConfig'; import {Storage} from './storage'; import {OAuth1} from './oAuth1'; import {OAuth2} from './oAuth2'; +import {Auth0Lock} from './auth0Lock'; -@inject(Storage, BaseConfig, OAuth1, OAuth2) +@inject(Storage, BaseConfig, OAuth1, OAuth2, Auth0Lock) export class Authentication { - constructor(storage, config, oAuth1, oAuth2) { + constructor(storage, config, oAuth1, oAuth2, auth0Lock) { this.storage = storage; this.config = config; this.oAuth1 = oAuth1; this.oAuth2 = oAuth2; + this.auth0Lock = auth0Lock; this.updateTokenCallstack = []; this.accessToken = null; this.refreshToken = null; @@ -225,7 +227,12 @@ export class Authentication { oauthType = this.config.providers[name].oauthType; } - const providerLogin = oauthType === '1.0' ? this.oAuth1 : this.oAuth2; + let providerLogin; + if (oauthType === 'auth0-lock') { + providerLogin = this.auth0Lock; + } else { + providerLogin = (oauthType === '1.0' ? this.oAuth1 : this.oAuth2); + } return providerLogin.open(this.config.providers[name], userData); } diff --git a/src/baseConfig.js b/src/baseConfig.js index 2f12ec5..30eef6e 100644 --- a/src/baseConfig.js +++ b/src/baseConfig.js @@ -249,6 +249,20 @@ export class BaseConfig { scopeDelimiter: ' ', oauthType: '2.0', popupOptions: { width: 1028, height: 529 } + }, + auth0: { + name: 'auth0', + oauthType: 'auth0-lock', + clientId: 'your_client_id', + clientDomain: 'your_domain_url', + display: 'popup', + lockOptions: { + popup: true + }, + responseType: 'token', + state: function() { + return Math.random().toString(36).substr(2); + } } };