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);
+ }
}
};