diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index d48a4bf..0000000 --- a/.eslintrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "./node_modules/aurelia-tools/.eslintrc", - "rules" : { - "no-alert": 0 - } -} diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..07f4a40 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": "./node_modules/aurelia-tools/.eslintrc.json", + "rules": { + "no-new-func": 0 + } +} diff --git a/.gitignore b/.gitignore index e33c2cd..76489e1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ jspm_packages bower_components .idea .DS_STORE -build/reports \ No newline at end of file +build/reports diff --git a/README.md b/README.md index f982947..a8abca6 100644 --- a/README.md +++ b/README.md @@ -28,40 +28,73 @@ We've simplified installation and usage! This plugin should now be installed usi ## Installation -### Jspm/SytemJs +### Aureli-Cli -Run `jspm i aurelia-authentication` from your project root. +Run `npm i aurelia-authentication --save` from your project root. -If install breaks your application, try resolving jspm forks: +Add `aurelia-authentication` to the `build/bundles/dependencies` section of `aurelia-project/aurelia.json`. -``` -$ jspm inspect --forks -$ jspm resolve --only registry:package-name@version +Aurelia-authentication has submodules (currently only the authFilter). You need to add it to the aurelia build resources in your package.json. + +```js +"aurelia": { + "build": { + "resources": ["aurelia-authentication/authFilterValueConverter"] + } +}, ``` -E.g. +### Jspm + +Run `jspm i aurelia-authentication` +If the installation results in having forks, try resolving them by running: + +```sh +jspm inspect --forks +jspm resolve --only registry:package-name@version ``` -$ jspm inspect --forks - Installed Forks - npm:aurelia-dependency-injection 1.0.0-beta.1.2.3 1.0.0-beta.2.1.0 +E.g. + +```sh +jspm inspect --forks +> Installed Forks +> npm:aurelia-dependency-injection 1.0.0-beta.1.2.3 1.0.0-beta.2.1.0 -$ jspm resolve --only npm:aurelia-dependency-injection@1.0.0-beta.2.1.0 +jspm resolve --only npm:aurelia-dependency-injection@1.0.0-beta.2.1.0 ``` ### Webpack -Run `npm i aurelia-authentication` from your project root. +Run `npm i aurelia-authentication --save` from your project root. + +Add `'aurelia-authentication'` in the `coreBundles.aurelia section` of your `webpack.config.js`. -Aurelia-authentication has submodules (currently only the authFilter). So you need to add it to the AureliaWebpackPlugin includeSubModules list. +Aurelia-authentication has submodules (currently only the authFilter). You need to add it to the aurelia build resources in your package.json. ```js -AureliaWebpackPlugin({ - includeSubModules: [ - { moduleId: 'aurelia-authentication' } - ] - }), +"aurelia": { + "build": { + "resources": ["aurelia-authentication/authFilterValueConverter"] + } +}, +``` + +### Typescript + +Add to your `typings.json` + +```js +"aurelia-authentication": "github:spoonx/aurelia-authentication", +``` + +and run `typings i` + +or run + +```sh +typings i github:spoonx/aurelia-authentication ``` ## Documentation diff --git a/bower.json b/bower.json index dc6abcf..de322cd 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "aurelia-authentication", - "version": "3.0.0-rc5", + "version": "3.0.0-rc6", "description": "Plugin for social media authentication and local authentication together with other authentication utilities.", "keywords": [ "aurelia", @@ -19,15 +19,16 @@ "url": "https://github.com/SpoonX/aurelia-authentication" }, "dependencies": { - "aurelia-api": "^3.0.0-rc4", - "aurelia-dependency-injection": "^1.0.0-beta.1.2.3", - "aurelia-fetch-client": "^1.0.0-beta.1.2.5", - "aurelia-logging": "^1.0.0-beta.1.2.1", - "aurelia-metadata": "^1.0.0-beta.1.2.1", - "aurelia-pal": "^1.0.0-beta.1.2.2", - "aurelia-path": "^1.0.0-beta.1.2.2", - "aurelia-router": "^1.0.0-beta.1.2.4", - "extend": "^3.0.0", - "jwt-decode": "^2.0.1" + "aurelia-api": "^3.0.0-rc4", + "aurelia-dependency-injection": "^1.0.0-rc.1.0.0", + "aurelia-fetch-client": "^1.0.0-rc.1.0.0", + "aurelia-logging": "^1.0.0-rc.1.0.0", + "aurelia-metadata": "^1.0.0-rc.1.0.0", + "aurelia-pal": "^1.0.0-rc.1.0.0", + "aurelia-path": "^1.0.0-rc.1.0.0", + "aurelia-router": "^1.0.0-rc.1.0.0", + "aurelia-templating": "^1.0.0-rc.1.0.0", + "extend": "^3.0.0", + "jwt-decode": "^2.0.1" } } diff --git a/build/.eslintrc b/build/.eslintrc deleted file mode 100644 index cc4a83f..0000000 --- a/build/.eslintrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "rules": { - "no-var": 0, - "no-console": 0 - } -} diff --git a/build/args.js b/build/args.js index f905f48..db342fc 100644 --- a/build/args.js +++ b/build/args.js @@ -1,14 +1,13 @@ var yargs = require('yargs'); -var argv = yargs.argv; -var validBumpTypes = 'major|minor|patch|prerelease'.split('|'); -var bump = (argv.bump || 'patch').toLowerCase(); +var argv = yargs.argv, + validBumpTypes = "major|minor|patch|prerelease".split("|"), + bump = (argv.bump || 'patch').toLowerCase(); -if (validBumpTypes.indexOf(bump) === -1) { +if(validBumpTypes.indexOf(bump) === -1) { throw new Error('Unrecognized bump "' + bump + '".'); } module.exports = { - bump: bump, - depth: parseInt(argv.depth || '0') + bump: bump }; diff --git a/build/babel-options.js b/build/babel-options.js index 3ed9f1e..3e99af9 100644 --- a/build/babel-options.js +++ b/build/babel-options.js @@ -2,7 +2,7 @@ var path = require('path'); var paths = require('./paths'); exports.base = function() { - return { + var config = { filename: '', filenameRelative: '', sourceMap: true, @@ -12,22 +12,27 @@ exports.base = function() { comments: false, compact: false, code: true, - presets: [ 'es2015-loose', 'stage-1'], + presets: [ 'es2015-loose', 'stage-1' ], plugins: [ 'syntax-flow', 'transform-decorators-legacy', - 'transform-flow-strip-types' ] }; -}; - -exports['plugin-dts'] = ['babel-dts-generator', { - packageName: paths.packageName, - typings: '', - suppressModulePath: true, - suppressComments: false, - memberOutputFilter: /^_.*/ -}]; + if (!paths.useTypeScriptForDTS) { + config.plugins.push( + ['babel-dts-generator', { + packageName: paths.packageName, + typings: '', + suppressModulePath: true, + suppressComments: false, + memberOutputFilter: /^_.*/, + suppressAmbientDeclaration: true + }] + ); + }; + config.plugins.push('transform-flow-strip-types'); + return config; +} exports.commonjs = function() { var options = exports.base(); @@ -49,12 +54,12 @@ exports.system = function() { exports.es2015 = function() { var options = exports.base(); - options.presets = ['stage-1']; + options.presets = ['stage-1'] return options; }; -exports.dts = function() { +exports['native-modules'] = function() { var options = exports.base(); - options.plugins.push(exports['plugin-dts']); + options.presets[0] = 'es2015-loose-native-modules'; return options; -}; +} diff --git a/build/paths.js b/build/paths.js index a1d1503..f1bb8d4 100644 --- a/build/paths.js +++ b/build/paths.js @@ -1,24 +1,35 @@ var path = require('path'); var fs = require('fs'); +// hide warning // +var emitter = require('events'); +emitter.defaultMaxListeners = 20; + var appRoot = 'src/'; var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8')); -// your main file which exports only configure and other modules. -// usually packageName or 'index.js' -var entryFileName = pkg.name + '.js'; -module.exports = { +var paths = { root: appRoot, source: appRoot + '**/*.js', - tsSource: [ - appRoot + '**/*.js', // list files to parse for d.ts - '!' + appRoot + entryFileName // exclude entry file - ], - resources: '*ValueConverter.js', // relative to root, resources can not that easily be bundled into a single file (due to naming conventions) + resources: '*ValueConverter.*', // relative to root, resources can not that easily be bundled into a single file (due to naming conventions) html: appRoot + '**/*.html', style: 'styles/**/*.css', output: 'dist/', - doc: './doc', - test: './test/**/*.js', - packageName: pkg.name + doc:'./doc', + test: 'test/**/*.js', + exampleSource: 'doc/example/', + exampleOutput: 'doc/example-dist/', + packageName: pkg.name, + ignore: [], + useTypeScriptForDTS: false, + importsToAdd: [], + importsToIgnoreForDts: ['extend', 'jwt-decode'], + sort: false }; + +paths.files = [ + paths.source, + '!' + paths.root + paths.resources +]; + +module.exports = paths; diff --git a/build/tasks/build.js b/build/tasks/build.js index c921dbb..173144a 100644 --- a/build/tasks/build.js +++ b/build/tasks/build.js @@ -3,47 +3,99 @@ var runSequence = require('run-sequence'); var to5 = require('gulp-babel'); var paths = require('../paths'); var compilerOptions = require('../babel-options'); +var compilerTsOptions = require('../typescript-options'); var assign = Object.assign || require('object.assign'); var through2 = require('through2'); var concat = require('gulp-concat'); -var rename = require('gulp-rename'); var insert = require('gulp-insert'); +var rename = require('gulp-rename'); +var clean = require('gulp-clean'); var tools = require('aurelia-tools'); -var del = require('del'); -var vinylPaths = require('vinyl-paths'); - -// merged output file name. a folder of paths.packageName is temporarly created in build-dts +var ts = require('gulp-typescript'); +var gutil = require('gulp-util'); +var gulpIgnore = require('gulp-ignore'); +var merge = require('merge2'); var jsName = paths.packageName + '.js'; +var compileToModules = ['es2015', 'commonjs', 'amd', 'system', 'native-modules']; + +function cleanGeneratedCode() { + return through2.obj(function(file, enc, callback) { + file.contents = new Buffer(tools.cleanGeneratedCode(file.contents.toString('utf8'))); + this.push(file); + return callback(); + }); +} + +gulp.task('build-index', ['copy-resources'], function() { + var importsToAdd = paths.importsToAdd.slice(); -// concats submodules into one file, compiles d.ts file and copies them to the dist folders -gulp.task('build-dts', function() { - var importsToAdd = []; // stores extracted imports + var src = gulp.src(paths.files); - return gulp.src(paths.tsSource) - //.pipe(tools.sortFiles()) can't sort with excluded files of the tsSource - .pipe(through2.obj(function(file, enc, callback) { // extract all imports to importsToAdd + if (paths.sort) { + src = src.pipe(tools.sortFiles()); + } + + if (paths.ignore) { + paths.ignore.forEach(function(filename){ + src = src.pipe(gulpIgnore.exclude(filename)); + }); + } + + return src.pipe(through2.obj(function(file, enc, callback) { file.contents = new Buffer(tools.extractImports(file.contents.toString('utf8'), importsToAdd)); this.push(file); return callback(); })) .pipe(concat(jsName)) - .pipe(to5(assign({}, compilerOptions.dts()))); -}); - -gulp.task('copy-dts', function() { - var tdsPath = paths.packageName + '/' + paths.packageName + '.d.ts'; - return gulp.src(tdsPath) - .pipe(rename(paths.packageName + '.d.ts')) - .pipe(gulp.dest(paths.output + 'es2015')) - .pipe(gulp.dest(paths.output + 'commonjs')) - .pipe(gulp.dest(paths.output + 'amd')) - .pipe(gulp.dest(paths.output + 'system')); + .pipe(insert.transform(function(contents) { + return tools.createImportBlock(importsToAdd) + contents; + })) + .pipe(gulp.dest(paths.output)); }); -gulp.task('remove-dts-folder', function() { - var tdsFolder = paths.packageName; - return gulp.src([tdsFolder]) - .pipe(vinylPaths(del)); +function gulpFileFromString(filename, string) { + var src = require('stream').Readable({ objectMode: true }); + src._read = function() { + this.push(new gutil.File({ cwd: paths.appRoot, base: paths.output, path: filename, contents: new Buffer(string) })) + this.push(null) + } + return src; +} + +function srcForBabel() { + return merge( + gulp.src(paths.output + '*.js'), + gulpFileFromString(paths.output + 'index.js', "export * from './" + paths.packageName + "';") + ); +} + +function srcForTypeScript() { + return gulp + .src(paths.output + paths.packageName + '.js') + .pipe(rename(function (path) { + if (path.extname == '.js') { + path.extname = '.ts'; + } + })); +} + +compileToModules.forEach(function(moduleType){ + gulp.task('build-babel-' + moduleType, function () { + return srcForBabel() + .pipe(to5(assign({}, compilerOptions[moduleType]()))) + .pipe(cleanGeneratedCode()) + .pipe(gulp.dest(paths.output + moduleType)); + }); + + if (moduleType === 'native-modules') return; // typescript doesn't support the combination of: es5 + native modules + + gulp.task('build-ts-' + moduleType, function () { + var tsProject = ts.createProject( + compilerTsOptions({ module: moduleType, target: moduleType == 'es2015' ? 'es2015' : 'es5' }), ts.reporter.defaultReporter()); + var tsResult = srcForTypeScript().pipe(ts(tsProject)); + return tsResult.js + .pipe(gulp.dest(paths.output + moduleType)); + }); }); gulp.task('copy-resources', function() { @@ -51,77 +103,61 @@ gulp.task('copy-resources', function() { .pipe(gulp.dest(paths.output)); }); -// concats modules into one file -gulp.task('concat-modules', ['copy-resources'], function() { - var importsToAdd = []; // stores extracted imports +gulp.task('build-dts', function(){ + return gulp.src(paths.root + paths.packageName + '.d.ts') + .pipe(gulp.dest(paths.output)); +}); - return gulp.src([paths.source, '!' + paths.root + paths.resources]) - .pipe(tools.sortFiles()) - .pipe(through2.obj(function(file, enc, callback) { // extract all imports to importsToAdd +gulp.task('fixup-dts', function(){ + var importsToAdd = []; + return gulp.src(paths.output + '*.d.ts') + .pipe(through2.obj(function(file, enc, callback) { file.contents = new Buffer(tools.extractImports(file.contents.toString('utf8'), importsToAdd)); this.push(file); return callback(); })) - .pipe(concat(jsName)) + .pipe(concat(paths.packageName + '.d.ts')) .pipe(insert.transform(function(contents) { + importsToAdd = importsToAdd.filter(function(line) { + return !paths.importsToIgnoreForDts.some(function(plugin) { + return line.search(plugin) !== -1; + }); + }); return tools.createImportBlock(importsToAdd) + contents; })) .pipe(gulp.dest(paths.output)); }); -gulp.task('build-es2015', ['build-html-es2015'], function() { - return gulp.src([paths.output + jsName, paths.output + paths.resources], {base: paths.output}) - .pipe(to5(assign({}, compilerOptions.es2015()))) - .pipe(gulp.dest(paths.output + 'es2015')); -}); - -gulp.task('build-commonjs', ['build-html-commonjs'], function() { - return gulp.src([paths.output + jsName, paths.output + paths.resources], {base: paths.output}) - .pipe(to5(assign({}, compilerOptions.commonjs()))) - .pipe(gulp.dest(paths.output + 'commonjs')); -}); - -gulp.task('build-amd', ['build-html-amd'], function() { - return gulp.src([paths.output + jsName, paths.output + paths.resources], {base: paths.output}) - .pipe(to5(assign({}, compilerOptions.amd()))) - .pipe(gulp.dest(paths.output + 'amd')); -}); - -gulp.task('build-system', ['build-html-system'], function() { - return gulp.src([paths.output + jsName, paths.output + paths.resources], {base: paths.output}) - .pipe(to5(assign({}, compilerOptions.system()))) - .pipe(gulp.dest(paths.output + 'system')); -}); - - -gulp.task('build-html-es2015', function() { - return gulp.src(paths.html) - .pipe(gulp.dest(paths.output + 'es2015')); -}); - -gulp.task('build-html-commonjs', function() { - return gulp.src(paths.html) - .pipe(gulp.dest(paths.output + 'commonjs')); -}); - -gulp.task('build-html-amd', function() { - return gulp.src(paths.html) - .pipe(gulp.dest(paths.output + 'amd')); +gulp.task('clean-dts', function() { + return gulp.src([ + paths.output + '**/*.d.ts', + '!' + paths.output + '{index,' + paths.packageName + '}.d.ts' + ], {read: false}) + .pipe(clean({force: true})); }); -gulp.task('build-html-system', function() { - return gulp.src(paths.html) - .pipe(gulp.dest(paths.output + 'system')); +gulp.task('build', function(callback) { + return runSequence( + 'clean', + 'build-index', + compileToModules + .map(function(moduleType) { return 'build-babel-' + moduleType }) + .concat(['build-dts']), + 'fixup-dts', + 'clean-dts', + callback + ); }); -gulp.task('build', function(callback) { +gulp.task('build-ts', function(callback) { return runSequence( 'clean', - 'build-dts', - 'copy-dts', - 'remove-dts-folder', - 'concat-modules', - ['build-es2015', 'build-commonjs', 'build-amd', 'build-system'], + 'build-index', + 'build-babel-native-modules', + compileToModules + .filter(function(moduleType) { return moduleType !== 'native-modules' }) + .map(function(moduleType) { return 'build-ts-' + moduleType }) + .concat(paths.useTypeScriptForDTS ? ['build-dts'] : []), callback ); }); diff --git a/build/tasks/doc.js b/build/tasks/doc.js new file mode 100644 index 0000000..d54c60c --- /dev/null +++ b/build/tasks/doc.js @@ -0,0 +1,72 @@ +var gulp = require('gulp'); +var paths = require('../paths'); +var typedoc = require('gulp-typedoc'); +var runSequence = require('run-sequence'); +var through2 = require('through2'); +var to5 = require('gulp-babel'); +var compilerOptions = require('../babel-options'); +var assign = Object.assign || require('object.assign'); + +gulp.task('doc-generate', function(){ + return gulp.src([paths.output + paths.packageName + '.d.ts']) + .pipe(typedoc({ + target: 'es6', + includeDeclarations: true, + moduleResolution: 'node', + json: paths.doc + '/api.json', + name: paths.packageName + '-docs',  + mode: 'modules', + excludeExternals: true, + ignoreCompilerErrors: false, + version: true + })); +}); + +gulp.task('doc-shape', function(){ + return gulp.src([paths.doc + '/api.json']) + .pipe(through2.obj(function(file, enc, callback) { + var json = JSON.parse(file.contents.toString('utf8')).children[0]; + + json = { + name: paths.packageName, + children: json.children, + groups: json.groups + }; + + file.contents = new Buffer(JSON.stringify(json)); + this.push(file); + return callback(); + })) + .pipe(gulp.dest(paths.doc)); +}); + +function removeDTSPlugin(options) { + var found = options.plugins.find(function(x){ + return x instanceof Array; + }); + + var index = options.plugins.indexOf(found); + options.plugins.splice(index, 1); + return options; +} + +gulp.task('build-example-js', function() { + return gulp.src(paths.exampleSource + '**/*.js') + .pipe(to5(assign({}, removeDTSPlugin(compilerOptions.amd())))) + .pipe(gulp.dest(paths.exampleOutput)); +}); + +gulp.task('build-example-html', function() { + return gulp.src(paths.exampleSource + '**/*.html') + .pipe(gulp.dest(paths.exampleOutput)); +}); + +gulp.task('doc', function(callback){ + return runSequence( + 'doc-generate', + 'doc-shape', + 'build-example-js', + 'build-example-html', + callback + ); +}); diff --git a/build/tasks/prepare-release.js b/build/tasks/prepare-release.js index a7b1dc6..d28ab22 100644 --- a/build/tasks/prepare-release.js +++ b/build/tasks/prepare-release.js @@ -1,34 +1,32 @@ var gulp = require('gulp'); var runSequence = require('run-sequence'); var paths = require('../paths'); -var changelog = require('conventional-changelog'); +var conventionalChangelog = require('gulp-conventional-changelog'); var fs = require('fs'); var bump = require('gulp-bump'); var args = require('../args'); -gulp.task('bump-version', function() { +gulp.task('bump-version', function(){ return gulp.src(['./package.json', './bower.json']) - .pipe(bump({type: args.bump })) //major|minor|patch|prerelease + .pipe(bump({type:args.bump })) //major|minor|patch|prerelease .pipe(gulp.dest('./')); }); -gulp.task('changelog', function(callback) { - var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8')); - - return changelog({ - repository: pkg.repository.url, - version: pkg.version, - file: paths.doc + '/CHANGELOG.md' - }, function(err, log) { - fs.writeFileSync(paths.doc + '/CHANGELOG.md', log); - }); +gulp.task('changelog', function () { + return gulp.src(paths.doc + '/CHANGELOG.md', { + buffer: false + }).pipe(conventionalChangelog({ + preset: 'angular' + })) + .pipe(gulp.dest(paths.doc)); }); -gulp.task('prepare-release', function(callback) { +gulp.task('prepare-release', function(callback){ return runSequence( 'build', 'lint', 'bump-version', + //'doc', 'changelog', callback ); diff --git a/build/tasks/test.js b/build/tasks/test.js index d8e6456..d4d1aa1 100644 --- a/build/tasks/test.js +++ b/build/tasks/test.js @@ -23,7 +23,7 @@ gulp.task('test', ['lint'], function(done) { }); /** - * Run live test + * Watch for file changes and re-run tests on each change */ gulp.task('tdd', function(done) { server.start(function() { @@ -42,3 +42,24 @@ gulp.task('tdd', function(done) { karmaServer.start(); }); }); + +/** + * Run ie test once and exit + */ + gulp.task('test-ie', function(done) { + server.start(function() { + var karmaServer = new KarmaServer({ + configFile: __dirname + '/../../karma.conf.js', + singleRun: true, + browsers: ['IE'] + }, function(exitCode) { + server.stop(function() { + done(); + + process.exit(exitCode); + }); + }); + + karmaServer.start(); + }); + }); diff --git a/build/typescript-options.js b/build/typescript-options.js new file mode 100644 index 0000000..8a22abe --- /dev/null +++ b/build/typescript-options.js @@ -0,0 +1,9 @@ +var tsconfig = require('../tsconfig.json'); +var assign = Object.assign || require('object.assign'); + +module.exports = function(override) { + return assign(tsconfig.compilerOptions, { + "target": override && override.target || "es5", + "typescript": require('typescript') + }, override || {}); +} diff --git a/dist/amd/aurelia-authentication.d.ts b/dist/amd/aurelia-authentication.d.ts deleted file mode 100644 index ba2a7d6..0000000 --- a/dist/amd/aurelia-authentication.d.ts +++ /dev/null @@ -1,495 +0,0 @@ -declare module 'aurelia-authentication' { - export class Auth0Lock { - constructor(storage?: any, config?: any); - open(options?: any, userData?: any): any; - } - export class AuthenticatedFilterValueConverter { - constructor(authService?: any); - - /** - * route toView predictator on route.config.auth === (parameter || authService.isAuthenticated()) - * @param {RouteConfig} routes the routes array to convert - * @param {[Boolean]} [isAuthenticated] optional isAuthenticated value. default: this.authService.authenticated - * @return {Boolean} show/hide element - */ - toView(routes?: any, isAuthenticated?: any): any; - } - export class AuthenticatedValueConverter { - constructor(authService?: any); - - /** - * element toView predictator on authService.isAuthenticated() - * @return {Boolean} show/hide element - */ - toView(): any; - } - export class AuthenticateStep { - constructor(authService?: any); - run(routingContext?: any, next?: any): any; - } - export class Authentication { - constructor(storage?: any, config?: any, oAuth1?: any, oAuth2?: any, auth0Lock?: any); - - /* deprecated methods */ - getLoginRoute(): any; - getLoginRedirect(): any; - getLoginUrl(): any; - getSignupUrl(): any; - getProfileUrl(): any; - getToken(): any; - responseObject: any; - - /* get/set responseObject */ - getResponseObject(): any; - setResponseObject(response?: any): any; - - /* get data, update if needed first */ - getAccessToken(): any; - getRefreshToken(): any; - getPayload(): any; - getExp(): any; - - /* get status from data */ - getTtl(): any; - isTokenExpired(): any; - isAuthenticated(): any; - - /* get and set from response */ - getDataFromResponse(response?: any): any; - getTokenFromResponse(response?: any, tokenProp?: any, tokenName?: any, tokenRoot?: any): any; - toUpdateTokenCallstack(): any; - resolveUpdateTokenCallstack(response?: any): any; - - /** - * Authenticate with third-party - * - * @param {String} name of the provider - * @param {[{}]} [userData] - * - * @return {Promise} - */ - authenticate(name?: any, userData?: any): any; - redirect(redirectUrl?: any, defaultRedirectUrl?: any): any; - } - export class AuthFilterValueConverter { - - /** - * route toView predictator on route.config.auth === isAuthenticated - * @param {RouteConfig} routes the routes array to convert - * @param {Boolean} isAuthenticated authentication status - * @return {Boolean} show/hide element - */ - toView(routes?: any, isAuthenticated?: any): any; - } - export class AuthorizeStep { - constructor(authService?: any); - run(routingContext?: any, next?: any): any; - } - export class AuthService { - - /** - * The Authentication instance that handles the token - * - * @param {Authentication} - */ - authentication: any; - - /** - * The Config instance that contains the current configuration setting - * - * @param {Config} - */ - config: any; - - /** - * The current login status - * - * @param {Boolean} - */ - authenticated: any; - - /** - * The currently set timeoutID - * - * @param {Number} - */ - timeoutID: any; - - /** - * Create an AuthService instance - * - * @param {Authentication} authentication The Authentication instance to be used - * @param {Config} config The Config instance to be used - */ - constructor(authentication?: any, config?: any); - - /** - * Getter: The configured client for all aurelia-authentication requests - * - * @return {HttpClient} - */ - client: any; - auth: any; - - /** - * Sets the login timeout - * - * @param {Number} ttl Timeout time in ms - */ - setTimeout(ttl?: any): any; - - /** - * Clears the login timeout - */ - clearTimeout(): any; - - /** - * Stores and analyses the servers responseObject. Sets login status and timeout - * - * @param {Object} response The servers response as GOJO - */ - setResponseObject(response?: any): any; - - /** - * Get current user profile from server - * - * @param {[{}|number|string]} [criteriaOrId object or a Number|String converted to {id: criteriaOrId}] - * - * @return {Promise} - */ - getMe(criteriaOrId?: any): any; - - /** - * Send current user profile update to server - - * @param {any} Request body with data. - * @param {[{}|Number|String]} [criteriaOrId object or a Number|String converted to {id: criteriaOrId}] - * - * @return {Promise} - */ - updateMe(body?: any, criteriaOrId?: any): any; - - /** - * Get accessToken from storage - * - * @returns {String} Current accessToken - */ - getAccessToken(): any; - getCurrentToken(): any; - - /** - * Get refreshToken from storage - * - * @returns {String} Current refreshToken - */ - getRefreshToken(): any; - - /** - * Gets authentication status - * - * @returns {Boolean} For Non-JWT and unexpired JWT: true, else: false - */ - isAuthenticated(): any; - - /** - * Gets exp in milliseconds - * - * @returns {Number} Exp for JWT tokens, NaN for all other tokens - */ - getExp(): any; - - /** - * Gets ttl in seconds - * - * @returns {Number} Ttl for JWT tokens, NaN for all other tokens - */ - getTtl(): any; - - /** - * Gets exp from token payload and compares to current time - * - * @returns {Boolean} Returns (ttl > 0)? for JWT, undefined other tokens - */ - isTokenExpired(): any; - - /** - * Get payload from tokens - * - * @returns {Object} Payload for JWT, else null - */ - getTokenPayload(): any; - - /** - * Request new accesss token - * - * @returns {Promise} Requests new token. can be called multiple times - */ - updateToken(): any; - - /** - * Signup locally. Login and redirect depending on config - * - * @param {String|{}} displayNameOrCredentials displayName | object with signup data. - * @param {[String]|{}} emailOrOptions [email | options for post request] - * @param {[String]} passwordOrRedirectUri [password | optional redirectUri overwrite] - * @param {[{}]} options [options] - * @param {[String]} redirectUri [optional redirectUri overwrite] - * - * @return {Promise|Promise} Server response as Object - */ - signup(displayNameOrCredentials?: any, emailOrOptions?: any, passwordOrRedirectUri?: any, options?: any, redirectUri?: any): any; - - /** - * login locally. Redirect depending on config - * - * @param {[String]|{}} emailOrCredentials email | object with signup data. - * @param {[String]} [passwordOrOptions] [password | options for post request] - * @param {[{}]} [optionsOrRedirectUri] [options | redirectUri overwrite]] - * @param {[String]} [redirectUri] [optional redirectUri overwrite] - * - * @return {Promise|Promise} Server response as Object - */ - login(emailOrCredentials?: any, passwordOrOptions?: any, optionsOrRedirectUri?: any, redirectUri?: any): any; - - /** - * logout locally and redirect to redirectUri (if set) or redirectUri of config. Sends logout request first, if set in config - * - * @param {[String]} [redirectUri] [optional redirectUri overwrite] - * - * @return {Promise<>|Promise|Promise} Server response as Object - */ - logout(redirectUri?: any): any; - - /** - * Authenticate with third-party and redirect to redirectUri (if set) or redirectUri of config - * - * @param {String} name Name of the provider - * @param {[String]} [redirectUri] [optional redirectUri overwrite] - * @param {[{}]} [userData] [optional userData for the local authentication server] - * - * @return {Promise|Promise} Server response as Object - */ - authenticate(name?: any, redirectUri?: any, userData?: any): any; - - /** - * Unlink third-party - * - * @param {String} name Name of the provider - * - * @return {Promise|Promise} Server response as Object - */ - unlink(name?: any, redirectUri?: any): any; - } - export class BaseConfig { - - /** - * Prepends baseUrl to a given url - * @param {String} url The relative url to append - * @return {String} joined baseUrl and url - */ - joinBase(url?: any): any; - - /** - * Merge current settings with incomming settings - * @param {Object} incomming Settings object to be merged into the current configuration - * @return {Config} this - */ - configure(incomming?: any): any; - - /* ----------- default config ----------- */ - // Used internally. The used Rest instance; set during configuration (see index.js) - client: any; - - // If using aurelia-api: - // ===================== - // This is the name of the endpoint used for any requests made in relation to authentication (login, logout, etc.). An empty string selects the default endpoint of aurelia-api. - endpoint: any; - - // When authenticated, these endpoints will have the token added to the header of any requests (for authorization). Accepts an array of endpoint names. An empty string selects the default endpoint of aurelia-api. - configureEndpoints: any; - - // SPA related options - // =================== - // The SPA url to which the user is redirected after a successful login - loginRedirect: any; - - // The SPA url to which the user is redirected after a successful logout - logoutRedirect: any; - - // The SPA route used when an unauthenticated user tries to access an SPA page that requires authentication - loginRoute: any; - - // Whether or not an authentication token is provided in the response to a successful signup - loginOnSignup: any; - - // If loginOnSignup == false: The SPA url to which the user is redirected after a successful signup (else loginRedirect is used) - signupRedirect: any; - - // redirect when token expires. 0 = don't redirect (default), 1 = use logoutRedirect, string = redirect there - expiredRedirect: any; - - // API related options - // =================== - // The base url used for all authentication related requests, including provider.url below. - // This appends to the httpClient/endpoint base url, it does not override it. - baseUrl: any; - - // The API endpoint to which login requests are sent - loginUrl: any; - - // The API endpoint to which logout requests are sent (not needed for jwt) - logoutUrl: any; - - // The HTTP method used for 'unlink' requests (Options: 'get' or 'post') - logoutMethod: any; - - // The API endpoint to which signup requests are sent - signupUrl: any; - - // The API endpoint used in profile requests (inc. `find/get` and `update`) - profileUrl: any; - - // The method used to update the profile ('put' or 'patch') - profileMethod: any; - - // The API endpoint used with oAuth to unlink authentication - unlinkUrl: any; - - // The HTTP method used for 'unlink' requests (Options: 'get' or 'post') - unlinkMethod: any; - - // The API endpoint to which refreshToken requests are sent. null = loginUrl - refreshTokenUrl: any; - - // Token Options - // ============= - // The header property used to contain the authToken in the header of API requests that require authentication - authHeader: any; - - // The token name used in the header of API requests that require authentication - authTokenType: any; - - // The the property from which to get the access token after a successful login or signup. Can also be dotted eg "accessTokenProp.accessTokenName" - accessTokenProp: any; - - // If the property defined by `accessTokenProp` is an object: - // ------------------------------------------------------------ - //This is the property from which to get the token `{ "accessTokenProp": { "accessTokenName" : '...' } }` - accessTokenName: any; - - // This allows the token to be a further object deeper `{ "accessTokenProp": { "accessTokenRoot" : { "accessTokenName" : '...' } } }` - accessTokenRoot: any; - - // Refresh Token Options - // ===================== - // Option to turn refresh tokens On/Off - useRefreshToken: any; - - // The option to enable/disable the automatic refresh of Auth tokens using Refresh Tokens - autoUpdateToken: any; - - // Oauth Client Id - clientId: any; - - // The the property from which to get the refresh token after a successful token refresh. Can also be dotted eg "refreshTokenProp.refreshTokenProp" - refreshTokenProp: any; - - // If the property defined by `refreshTokenProp` is an object: - // ----------------------------------------------------------- - // This is the property from which to get the token `{ "refreshTokenProp": { "refreshTokenName" : '...' } }` - refreshTokenName: any; - - // This allows the refresh token to be a further object deeper `{ "refreshTokenProp": { "refreshTokenRoot" : { "refreshTokenName" : '...' } } }` - refreshTokenRoot: any; - - // Miscellaneous Options - // ===================== - // Whether to enable the fetch interceptor which automatically adds the authentication headers - // (or not... e.g. if using a session based API or you want to override the default behaviour) - httpInterceptor: any; - - // For OAuth only: Tell the API whether or not to include token cookies in the response (for session based APIs) - withCredentials: any; - - // Controls how the popup is shown for different devices (Options: 'browser' or 'mobile') - platform: any; - - // Determines the `PLATFORM` property name upon which aurelia-authentication data is stored (Default: `PLATFORM.localStorage`) - storage: any; - - // The key used for storing the authentication response locally - storageKey: any; - - // List of value-converters to make global - globalValueConverters: any; - - //OAuth provider specific related configuration - // ============================================ - providers: any; - - /* deprecated defaults */ - _authToken: any; - _responseTokenProp: any; - _tokenName: any; - _tokenRoot: any; - _tokenPrefix: any; - authToken: any; - responseTokenProp: any; - tokenRoot: any; - tokenName: any; - tokenPrefix: any; - current: any; - _current: any; - } - export class FetchConfig { - - /** - * Construct the FetchConfig - * - * @param {HttpClient} httpClient - * @param {Config} clientConfig - * @param {Authentication} authService - * @param {BaseConfig} config - */ - constructor(httpClient?: any, clientConfig?: any, authService?: any, config?: any); - - /** - * Interceptor for HttpClient - * - * @return {{request: Function, response: Function}} - */ - interceptor: any; - - /** - * Configure client(s) with authorization interceptor - * - * @param {HttpClient|Rest|string[]} client HttpClient, rest client or api endpoint name, or an array thereof - * - * @return {HttpClient[]} - */ - configure(client?: any): any; - } - export class OAuth1 { - constructor(storage?: any, popup?: any, config?: any); - open(options?: any, userData?: any): any; - exchangeForToken(oauthData?: any, userData?: any, provider?: any): any; - } - export class OAuth2 { - constructor(storage?: any, popup?: any, config?: any); - open(options?: any, userData?: any): any; - exchangeForToken(oauthData?: any, userData?: any, provider?: any): any; - buildQuery(provider?: any): any; - } - export class Popup { - constructor(); - open(url?: any, windowName?: any, options?: any): any; - eventListener(redirectUri?: any): any; - pollPopup(): any; - } - export class Storage { - constructor(config?: any); - get(key?: any): any; - set(key?: any, value?: any): any; - remove(key?: any): any; - } -} \ No newline at end of file diff --git a/dist/amd/aurelia-authentication.js b/dist/amd/aurelia-authentication.js index 5fbbfc6..e652a01 100644 --- a/dist/amd/aurelia-authentication.js +++ b/dist/amd/aurelia-authentication.js @@ -1,17 +1,24 @@ -define(['exports', 'extend', 'aurelia-logging', 'jwt-decode', 'aurelia-pal', 'aurelia-path', 'aurelia-dependency-injection', 'aurelia-metadata', 'aurelia-router', 'aurelia-fetch-client', 'aurelia-api', './authFilterValueConverter'], function (exports, _extend, _aureliaLogging, _jwtDecode, _aureliaPal, _aureliaPath, _aureliaDependencyInjection, _aureliaMetadata, _aureliaRouter, _aureliaFetchClient, _aureliaApi) { +define(['exports', 'aurelia-logging', 'extend', 'jwt-decode', 'aurelia-pal', 'aurelia-fetch-client', 'aurelia-api', 'aurelia-dependency-injection', 'aurelia-router', 'aurelia-metadata', 'aurelia-path', './authFilterValueConverter'], function (exports, _aureliaLogging, _extend, _jwtDecode, _aureliaPal, _aureliaFetchClient, _aureliaApi, _aureliaDependencyInjection, _aureliaRouter, _aureliaMetadata, _aureliaPath) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); - exports.configure = exports.FetchConfig = exports.AuthorizeStep = exports.AuthenticateStep = exports.AuthService = exports.Authentication = exports.OAuth2 = exports.OAuth1 = exports.Auth0Lock = exports.Storage = exports.BaseConfig = exports.Popup = undefined; - - var _extend2 = _interopRequireDefault(_extend); + exports.Storage = exports.Popup = exports.OAuth2 = exports.OAuth1 = exports.FetchConfig = exports.BaseConfig = exports.AuthService = exports.AuthorizeStep = exports.Authentication = exports.AuthenticateStep = exports.Auth0Lock = undefined; + exports.configure = configure; var LogManager = _interopRequireWildcard(_aureliaLogging); + var _extend2 = _interopRequireDefault(_extend); + var _jwtDecode2 = _interopRequireDefault(_jwtDecode); + function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + default: obj + }; + } + function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; @@ -29,11 +36,23 @@ define(['exports', 'extend', 'aurelia-logging', 'jwt-decode', 'aurelia-pal', 'au } } - function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { - default: obj + var _createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; }; - } + }(); function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; @@ -64,7 +83,9 @@ define(['exports', 'extend', 'aurelia-logging', 'jwt-decode', 'aurelia-pal', 'au return desc; } - var _dec, _class2, _dec2, _class3, _dec3, _class4, _dec4, _class5, _dec5, _dec6, _dec7, _dec8, _dec9, _dec10, _dec11, _class6, _desc, _value, _class7, _dec12, _dec13, _class8, _desc2, _value2, _class9, _dec14, _class11, _dec15, _class12, _dec16, _class13; + + + var _dec, _class, _dec2, _class2, _dec3, _dec4, _dec5, _dec6, _dec7, _dec8, _dec9, _class3, _desc, _value, _class4, _dec10, _class5, _dec11, _dec12, _class6, _desc2, _value2, _class7, _dec13, _class10, _dec14, _class11, _dec15, _class12, _dec16, _class13; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; @@ -72,1379 +93,1350 @@ define(['exports', 'extend', 'aurelia-logging', 'jwt-decode', 'aurelia-pal', 'au return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; - var _createClass = function () { - function defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } + function configure(aurelia, config) { + if (!_aureliaPal.PLATFORM.location.origin) { + _aureliaPal.PLATFORM.location.origin = _aureliaPal.PLATFORM.location.protocol + '//' + _aureliaPal.PLATFORM.location.hostname + (_aureliaPal.PLATFORM.location.port ? ':' + _aureliaPal.PLATFORM.location.port : ''); } - return function (Constructor, protoProps, staticProps) { - if (protoProps) defineProperties(Constructor.prototype, protoProps); - if (staticProps) defineProperties(Constructor, staticProps); - return Constructor; - }; - }(); + var baseConfig = aurelia.container.get(BaseConfig); - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); + if (typeof config === 'function') { + config(baseConfig); + } else if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') { + baseConfig.configure(config); } - } - var Popup = exports.Popup = function () { - function Popup() { - _classCallCheck(this, Popup); + for (var _iterator = baseConfig.globalValueConverters, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; - this.popupWindow = null; - this.polling = null; - this.url = ''; + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; + } + + var converter = _ref; + + aurelia.globalResources('./' + converter); + LogManager.getLogger('authentication').info('Add globalResources value-converter: ' + converter); } + var fetchConfig = aurelia.container.get(FetchConfig); + var clientConfig = aurelia.container.get(_aureliaApi.Config); - Popup.prototype.open = function open(url, windowName, options) { - this.url = url; - var optionsString = buildPopupWindowOptions(options || {}); + if (Array.isArray(baseConfig.configureEndpoints)) { + baseConfig.configureEndpoints.forEach(function (endpointToPatch) { + fetchConfig.configure(endpointToPatch); + }); + } - this.popupWindow = _aureliaPal.PLATFORM.global.open(url, windowName, optionsString); + var client = void 0; - if (this.popupWindow && this.popupWindow.focus) { - this.popupWindow.focus(); + if (baseConfig.endpoint !== null) { + if (typeof baseConfig.endpoint === 'string') { + var endpoint = clientConfig.getEndpoint(baseConfig.endpoint); + if (!endpoint) { + throw new Error('There is no \'' + (baseConfig.endpoint || 'default') + '\' endpoint registered.'); + } + client = endpoint; + } else if (baseConfig.endpoint instanceof _aureliaFetchClient.HttpClient) { + client = new _aureliaApi.Rest(baseConfig.endpoint); } + } - return this; - }; + if (!(client instanceof _aureliaApi.Rest)) { + client = new _aureliaApi.Rest(aurelia.container.get(_aureliaFetchClient.HttpClient)); + } - Popup.prototype.eventListener = function eventListener(redirectUri) { + baseConfig.client = client; + } + + var Auth0Lock = exports.Auth0Lock = (_dec = (0, _aureliaDependencyInjection.inject)(Storage, BaseConfig), _dec(_class = function () { + function Auth0Lock(storage, config) { + _classCallCheck(this, Auth0Lock); + + 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' + }; + } + + Auth0Lock.prototype.open = function open(options, userData) { var _this = this; - return new Promise(function (resolve, reject) { - _this.popupWindow.addEventListener('loadstart', function (event) { - if (event.url.indexOf(redirectUri) !== 0) { - return; - } + if (typeof _aureliaPal.PLATFORM.global.Auth0Lock !== 'function') { + throw new Error('Auth0Lock was not found in global scope. Please load it before using this provider.'); + } + var provider = (0, _extend2.default)(true, {}, this.defaults, options); + var stateName = provider.name + '_state'; - var parser = _aureliaPal.DOM.createElement('a'); - parser.href = event.url; + if (typeof provider.state === 'function') { + this.storage.set(stateName, provider.state()); + } else if (typeof provider.state === 'string') { + this.storage.set(stateName, provider.state); + } - if (parser.search || parser.hash) { - var qs = parseUrl(parser); + this.lock = this.lock || new _aureliaPal.PLATFORM.global.Auth0Lock(provider.clientId, provider.clientDomain); - if (qs.error) { - reject({ error: qs.error }); - } else { - resolve(qs); - } + var openPopup = new Promise(function (resolve, reject) { + var 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.popupWindow.close(); + _this.lock.show(provider.lockOptions, function (err, profile, tokenOrCode) { + if (err) { + reject(err); + } else { + resolve({ + access_token: tokenOrCode + }); } }); + }); - _this.popupWindow.addEventListener('exit', function () { - reject({ data: 'Provider Popup was closed' }); - }); + return openPopup.then(function (lockResponse) { + if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { + return lockResponse; + } - _this.popupWindow.addEventListener('loaderror', function () { - reject({ data: 'Authorization Failed' }); - }); + throw new Error('Only `token` responseType is supported'); }); }; - Popup.prototype.pollPopup = function pollPopup() { - var _this2 = this; - - return new Promise(function (resolve, reject) { - _this2.polling = _aureliaPal.PLATFORM.global.setInterval(function () { - var errorData = void 0; + return Auth0Lock; + }()) || _class); + var AuthenticateStep = exports.AuthenticateStep = (_dec2 = (0, _aureliaDependencyInjection.inject)(AuthService), _dec2(_class2 = function () { + function AuthenticateStep(authService) { + - try { - if (_this2.popupWindow.location.host === _aureliaPal.PLATFORM.global.document.location.host && (_this2.popupWindow.location.search || _this2.popupWindow.location.hash)) { - var qs = parseUrl(_this2.popupWindow.location); + this.authService = authService; + } - if (qs.error) { - reject({ error: qs.error }); - } else { - resolve(qs); - } + AuthenticateStep.prototype.run = function run(routingContext, next) { + var isLoggedIn = this.authService.authenticated; + var loginRoute = this.authService.config.loginRoute; - _this2.popupWindow.close(); - _aureliaPal.PLATFORM.global.clearInterval(_this2.polling); - } - } catch (error) { - errorData = error; - } + if (routingContext.getAllInstructions().some(function (route) { + return route.config.auth === true; + })) { + if (!isLoggedIn) { + return next.cancel(new _aureliaRouter.Redirect(loginRoute)); + } + } else if (isLoggedIn && routingContext.getAllInstructions().some(function (route) { + return route.fragment === loginRoute; + })) { + return next.cancel(new _aureliaRouter.Redirect(this.authService.config.loginRedirect)); + } - if (!_this2.popupWindow) { - _aureliaPal.PLATFORM.global.clearInterval(_this2.polling); - reject({ - error: errorData, - data: 'Provider Popup Blocked' - }); - } else if (_this2.popupWindow.closed) { - _aureliaPal.PLATFORM.global.clearInterval(_this2.polling); - reject({ - error: errorData, - data: 'Problem poll popup' - }); - } - }, 35); - }); + return next(); }; - return Popup; - }(); + return AuthenticateStep; + }()) || _class2); + var Authentication = exports.Authentication = (_dec3 = (0, _aureliaDependencyInjection.inject)(Storage, BaseConfig, OAuth1, OAuth2, Auth0Lock), _dec4 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.loginRoute instead.' }), _dec5 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.loginRedirect instead.' }), _dec6 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.joinBase(baseConfig.loginUrl) instead.' }), _dec7 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.joinBase(baseConfig.signupUrl) instead.' }), _dec8 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.joinBase(baseConfig.profileUrl) instead.' }), _dec9 = (0, _aureliaMetadata.deprecated)({ message: 'Use .getAccessToken() instead.' }), _dec3(_class3 = (_class4 = function () { + function Authentication(storage, config, oAuth1, oAuth2, auth0Lock) { + - var buildPopupWindowOptions = function buildPopupWindowOptions(options) { - var width = options.width || 500; - var height = options.height || 500; + this.storage = storage; + this.config = config; + this.oAuth1 = oAuth1; + this.oAuth2 = oAuth2; + this.auth0Lock = auth0Lock; + this.updateTokenCallstack = []; + this.accessToken = null; + this.refreshToken = null; + this.payload = null; + this.exp = null; + this.hasDataStored = false; + } - var extended = (0, _extend2.default)({ - width: width, - height: height, - left: _aureliaPal.PLATFORM.global.screenX + (_aureliaPal.PLATFORM.global.outerWidth - width) / 2, - top: _aureliaPal.PLATFORM.global.screenY + (_aureliaPal.PLATFORM.global.outerHeight - height) / 2.5 - }, options); + Authentication.prototype.getLoginRoute = function getLoginRoute() { + return this.config.loginRoute; + }; - var parts = []; - Object.keys(extended).map(function (key) { - return parts.push(key + '=' + extended[key]); - }); + Authentication.prototype.getLoginRedirect = function getLoginRedirect() { + return this.config.loginRedirect; + }; - return parts.join(','); - }; + Authentication.prototype.getLoginUrl = function getLoginUrl() { + return this.Config.joinBase(this.config.loginUrl); + }; - var parseUrl = function parseUrl(url) { - return (0, _extend2.default)(true, {}, (0, _aureliaPath.parseQueryString)(url.search), (0, _aureliaPath.parseQueryString)(url.hash)); - }; + Authentication.prototype.getSignupUrl = function getSignupUrl() { + return this.Config.joinBase(this.config.signupUrl); + }; - var BaseConfig = exports.BaseConfig = function () { - function BaseConfig() { - _classCallCheck(this, BaseConfig); + Authentication.prototype.getProfileUrl = function getProfileUrl() { + return this.Config.joinBase(this.config.profileUrl); + }; - this.client = null; - this.endpoint = null; - this.configureEndpoints = null; - this.loginRedirect = '#/'; - this.logoutRedirect = '#/'; - this.loginRoute = '/login'; - this.loginOnSignup = true; - this.signupRedirect = '#/login'; - this.expiredRedirect = 0; - this.baseUrl = ''; - this.loginUrl = '/auth/login'; - this.logoutUrl = null; - this.logoutMethod = 'get'; - this.signupUrl = '/auth/signup'; - this.profileUrl = '/auth/me'; - this.profileMethod = 'put'; - this.unlinkUrl = '/auth/unlink/'; - this.unlinkMethod = 'get'; - this.refreshTokenUrl = null; - this.authHeader = 'Authorization'; - this.authTokenType = 'Bearer'; - this.accessTokenProp = 'access_token'; - this.accessTokenName = 'token'; - this.accessTokenRoot = false; - this.useRefreshToken = false; - this.autoUpdateToken = true; - this.clientId = false; - this.refreshTokenProp = 'refresh_token'; - this.refreshTokenName = 'token'; - this.refreshTokenRoot = false; - this.httpInterceptor = true; - this.withCredentials = true; - this.platform = 'browser'; - this.storage = 'localStorage'; - this.storageKey = 'aurelia_authentication'; - this.globalValueConverters = ['authFilterValueConverter']; - this.providers = { - facebook: { - name: 'facebook', - url: '/auth/facebook', - authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', - redirectUri: _aureliaPal.PLATFORM.location.origin + '/', - requiredUrlParams: ['display', 'scope'], - scope: ['email'], - scopeDelimiter: ',', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 580, height: 400 } - }, - google: { - name: 'google', - url: '/auth/google', - authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth', - redirectUri: _aureliaPal.PLATFORM.location.origin, - requiredUrlParams: ['scope'], - optionalUrlParams: ['display', 'state'], - scope: ['profile', 'email'], - scopePrefix: 'openid', - scopeDelimiter: ' ', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 452, height: 633 }, - state: randomState - }, - github: { - name: 'github', - url: '/auth/github', - authorizationEndpoint: 'https://github.com/login/oauth/authorize', - redirectUri: _aureliaPal.PLATFORM.location.origin, - optionalUrlParams: ['scope'], - scope: ['user:email'], - scopeDelimiter: ' ', - oauthType: '2.0', - popupOptions: { width: 1020, height: 618 } - }, - instagram: { - name: 'instagram', - url: '/auth/instagram', - authorizationEndpoint: 'https://api.instagram.com/oauth/authorize', - redirectUri: _aureliaPal.PLATFORM.location.origin, - requiredUrlParams: ['scope'], - scope: ['basic'], - scopeDelimiter: '+', - oauthType: '2.0' - }, - linkedin: { - name: 'linkedin', - url: '/auth/linkedin', - authorizationEndpoint: 'https://www.linkedin.com/uas/oauth2/authorization', - redirectUri: _aureliaPal.PLATFORM.location.origin, - requiredUrlParams: ['state'], - scope: ['r_emailaddress'], - scopeDelimiter: ' ', - state: 'STATE', - oauthType: '2.0', - popupOptions: { width: 527, height: 582 } - }, - twitter: { - name: 'twitter', - url: '/auth/twitter', - authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', - redirectUri: _aureliaPal.PLATFORM.location.origin, - oauthType: '1.0', - popupOptions: { width: 495, height: 645 } - }, - twitch: { - name: 'twitch', - url: '/auth/twitch', - authorizationEndpoint: 'https://api.twitch.tv/kraken/oauth2/authorize', - redirectUri: _aureliaPal.PLATFORM.location.origin, - requiredUrlParams: ['scope'], - scope: ['user_read'], - scopeDelimiter: ' ', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 500, height: 560 } - }, - live: { - name: 'live', - url: '/auth/live', - authorizationEndpoint: 'https://login.live.com/oauth20_authorize.srf', - redirectUri: _aureliaPal.PLATFORM.location.origin, - requiredUrlParams: ['display', 'scope'], - scope: ['wl.emails'], - scopeDelimiter: ' ', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 500, height: 560 } - }, - yahoo: { - name: 'yahoo', - url: '/auth/yahoo', - authorizationEndpoint: 'https://api.login.yahoo.com/oauth2/request_auth', - redirectUri: _aureliaPal.PLATFORM.location.origin, - scope: [], - scopeDelimiter: ',', - oauthType: '2.0', - popupOptions: { width: 559, height: 519 } - }, - bitbucket: { - name: 'bitbucket', - url: '/auth/bitbucket', - authorizationEndpoint: 'https://bitbucket.org/site/oauth2/authorize', - redirectUri: _aureliaPal.PLATFORM.location.origin + '/', - requiredUrlParams: ['scope'], - scope: ['email'], - 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: randomState - } - }; - this._authToken = 'Bearer'; - this._responseTokenProp = 'access_token'; - this._tokenName = 'token'; - this._tokenRoot = false; - this._tokenPrefix = 'aurelia'; - } + Authentication.prototype.getToken = function getToken() { + return this.getAccessToken(); + }; - BaseConfig.prototype.joinBase = function joinBase(url) { - return (0, _aureliaPath.join)(this.baseUrl, url); + Authentication.prototype.getResponseObject = function getResponseObject() { + return JSON.parse(this.storage.get(this.config.storageKey)); }; - BaseConfig.prototype.configure = function configure(incomming) { - for (var key in incomming) { - var value = incomming[key]; - if (value !== undefined) { - if (Array.isArray(value) || (typeof value === 'undefined' ? 'undefined' : _typeof(value)) !== 'object' || value === null) { - this[key] = value; - } else { - (0, _extend2.default)(true, this[key], value); - } - } + Authentication.prototype.setResponseObject = function setResponseObject(response) { + if (response) { + this.getDataFromResponse(response); + this.storage.set(this.config.storageKey, JSON.stringify(response)); + return; } + this.accessToken = null; + this.refreshToken = null; + this.payload = null; + this.exp = null; + + this.hasDataStored = false; + + this.storage.remove(this.config.storageKey); }; - _createClass(BaseConfig, [{ - key: 'authToken', - set: function set(authToken) { - LogManager.getLogger('authentication').warn('BaseConfig.authToken is deprecated. Use BaseConfig.authTokenType instead.'); - this._authTokenType = authToken; - this.authTokenType = authToken; - return authToken; - }, - get: function get() { - return this._authTokenType; - } - }, { - key: 'responseTokenProp', - set: function set(responseTokenProp) { - LogManager.getLogger('authentication').warn('BaseConfig.responseTokenProp is deprecated. Use BaseConfig.accessTokenProp instead.'); - this._responseTokenProp = responseTokenProp; - this.accessTokenProp = responseTokenProp; - return responseTokenProp; - }, - get: function get() { - return this._responseTokenProp; - } - }, { - key: 'tokenRoot', - set: function set(tokenRoot) { - LogManager.getLogger('authentication').warn('BaseConfig.tokenRoot is deprecated. Use BaseConfig.accessTokenRoot instead.'); - this._tokenRoot = tokenRoot; - this.accessTokenRoot = tokenRoot; - return tokenRoot; - }, - get: function get() { - return this._tokenRoot; - } - }, { - key: 'tokenName', - set: function set(tokenName) { - LogManager.getLogger('authentication').warn('BaseConfig.tokenName is deprecated. Use BaseConfig.accessTokenName instead.'); - this._tokenName = tokenName; - this.accessTokenName = tokenName; - return tokenName; - }, - get: function get() { - return this._tokenName; - } - }, { - key: 'tokenPrefix', - set: function set(tokenPrefix) { - LogManager.getLogger('authentication').warn('BaseConfig.tokenPrefix is obsolete. Use BaseConfig.storageKey instead.'); - this._tokenPrefix = tokenPrefix; - return tokenPrefix; - }, - get: function get() { - return this._tokenPrefix || 'aurelia'; + Authentication.prototype.getAccessToken = function getAccessToken() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.accessToken; + }; + + Authentication.prototype.getRefreshToken = function getRefreshToken() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.refreshToken; + }; + + Authentication.prototype.getPayload = function getPayload() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.payload; + }; + + Authentication.prototype.getExp = function getExp() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.exp; + }; + + Authentication.prototype.getTtl = function getTtl() { + var exp = this.getExp(); + return Number.isNaN(exp) ? NaN : exp - Math.round(new Date().getTime() / 1000); + }; + + Authentication.prototype.isTokenExpired = function isTokenExpired() { + var timeLeft = this.getTtl(); + return Number.isNaN(timeLeft) ? undefined : timeLeft < 0; + }; + + Authentication.prototype.isAuthenticated = function isAuthenticated() { + var isTokenExpired = this.isTokenExpired(); + if (isTokenExpired === undefined) return this.accessToken ? true : false; + return !isTokenExpired; + }; + + Authentication.prototype.getDataFromResponse = function getDataFromResponse(response) { + var config = this.config; + + this.accessToken = this.getTokenFromResponse(response, config.accessTokenProp, config.accessTokenName, config.accessTokenRoot); + + this.refreshToken = null; + if (config.useRefreshToken) { + try { + this.refreshToken = this.getTokenFromResponse(response, config.refreshTokenProp, config.refreshTokenName, config.refreshTokenRoot); + } catch (e) { + this.refreshToken = null; + } } - }, { - key: 'current', - get: function get() { - LogManager.getLogger('authentication').warn('Getter BaseConfig.current is deprecated. Use BaseConfig directly instead.'); - return this; - }, - set: function set(_) { - throw new Error('Setter BaseConfig.current is obsolete. Use BaseConfig directly instead.'); + + this.payload = null; + + try { + this.payload = this.accessToken ? (0, _jwtDecode2.default)(this.accessToken) : null; + } catch (_) { + _; } - }, { - key: '_current', - get: function get() { - LogManager.getLogger('authentication').warn('Getter BaseConfig._current is deprecated. Use BaseConfig directly instead.'); - return this; - }, - set: function set(_) { - throw new Error('Setter BaseConfig._current is obsolete. Use BaseConfig directly instead.'); + + this.exp = this.payload ? parseInt(this.payload.exp, 10) : NaN; + + this.hasDataStored = true; + + return { + accessToken: this.accessToken, + refreshToken: this.refreshToken, + payload: this.payload, + exp: this.exp + }; + }; + + Authentication.prototype.getTokenFromResponse = function getTokenFromResponse(response, tokenProp, tokenName, tokenRoot) { + if (!response) return undefined; + + var responseTokenProp = tokenProp.split('.').reduce(function (o, x) { + return o[x]; + }, response); + + if (typeof responseTokenProp === 'string') { + return responseTokenProp; } - }]); - return BaseConfig; - }(); + if ((typeof responseTokenProp === 'undefined' ? 'undefined' : _typeof(responseTokenProp)) === 'object') { + var tokenRootData = tokenRoot && tokenRoot.split('.').reduce(function (o, x) { + return o[x]; + }, responseTokenProp); + var _token = tokenRootData ? tokenRootData[tokenName] : responseTokenProp[tokenName]; - function randomState() { - var rand = Math.random().toString(36).substr(2); - return encodeURIComponent(rand); - } + if (!_token) throw new Error('Token not found in response'); - var Storage = exports.Storage = (_dec = (0, _aureliaDependencyInjection.inject)(BaseConfig), _dec(_class2 = function () { - function Storage(config) { - _classCallCheck(this, Storage); + return _token; + } - this.config = config; - } + var token = response[tokenName] === undefined ? null : response[tokenName]; - Storage.prototype.get = function get(key) { - return _aureliaPal.PLATFORM.global[this.config.storage].getItem(key); + if (!token) throw new Error('Token not found in response'); + + return token; }; - Storage.prototype.set = function set(key, value) { - _aureliaPal.PLATFORM.global[this.config.storage].setItem(key, value); + Authentication.prototype.toUpdateTokenCallstack = function toUpdateTokenCallstack() { + var _this2 = this; + + return new Promise(function (resolve) { + return _this2.updateTokenCallstack.push(resolve); + }); }; - Storage.prototype.remove = function remove(key) { - _aureliaPal.PLATFORM.global[this.config.storage].removeItem(key); + Authentication.prototype.resolveUpdateTokenCallstack = function resolveUpdateTokenCallstack(response) { + this.updateTokenCallstack.map(function (resolve) { + return resolve(response); + }); + this.updateTokenCallstack = []; }; - return Storage; - }()) || _class2); - var Auth0Lock = exports.Auth0Lock = (_dec2 = (0, _aureliaDependencyInjection.inject)(Storage, BaseConfig), _dec2(_class3 = function () { - function Auth0Lock(storage, config) { - _classCallCheck(this, Auth0Lock); + Authentication.prototype.authenticate = function authenticate(name) { + var userData = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - 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' - }; - } + var oauthType = this.config.providers[name].type; - Auth0Lock.prototype.open = function open(options, userData) { - var _this3 = this; + if (oauthType) { + LogManager.getLogger('authentication').warn('DEPRECATED: Setting provider.type is deprecated and replaced by provider.oauthType'); + } else { + oauthType = this.config.providers[name].oauthType; + } - if (typeof _aureliaPal.PLATFORM.global.Auth0Lock !== 'function') { - throw new Error('Auth0Lock was not found in global scope. Please load it before using this provider.'); + var providerLogin = void 0; + if (oauthType === 'auth0-lock') { + providerLogin = this.auth0Lock; + } else { + providerLogin = oauthType === '1.0' ? this.oAuth1 : this.oAuth2; } - var provider = (0, _extend2.default)(true, {}, this.defaults, options); - var 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); + return providerLogin.open(this.config.providers[name], userData); + }; + + Authentication.prototype.redirect = function redirect(redirectUrl, defaultRedirectUrl) { + if (redirectUrl === true) { + LogManager.getLogger('authentication').warn('DEPRECATED: Setting redirectUrl === true to actually *not redirect* is deprecated. Set redirectUrl === 0 instead.'); + return; } - this.lock = this.lock || new _aureliaPal.PLATFORM.global.Auth0Lock(provider.clientId, provider.clientDomain); + if (redirectUrl === false) { + LogManager.getLogger('authentication').warn('BREAKING CHANGE: Setting redirectUrl === false to actually *do redirect* is deprecated. Set redirectUrl to undefined or null to use the defaultRedirectUrl if so desired.'); + } - var openPopup = new Promise(function (resolve, reject) { - var 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 = _this3.storage.get(provider.name + '_state'); + if (redirectUrl === 0) { + return; + } + if (typeof redirectUrl === 'string') { + _aureliaPal.PLATFORM.location.href = encodeURI(redirectUrl); + } else if (defaultRedirectUrl) { + _aureliaPal.PLATFORM.location.href = defaultRedirectUrl; + } + }; - _this3.lock.show(provider.lockOptions, function (err, profile, tokenOrCode) { - if (err) { - reject(err); - } else { - resolve({ - access_token: tokenOrCode - }); - } - }); - }); + _createClass(Authentication, [{ + key: 'responseObject', + get: function get() { + LogManager.getLogger('authentication').warn('Getter Authentication.responseObject is deprecated. Use Authentication.getResponseObject() instead.'); + return this.getResponseObject(); + }, + set: function set(response) { + LogManager.getLogger('authentication').warn('Setter Authentication.responseObject is deprecated. Use AuthServive.setResponseObject(response) instead.'); + this.setResponseObject(response); + } + }]); - return openPopup.then(function (lockResponse) { - if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { - return lockResponse; + return Authentication; + }(), (_applyDecoratedDescriptor(_class4.prototype, 'getLoginRoute', [_dec4], Object.getOwnPropertyDescriptor(_class4.prototype, 'getLoginRoute'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getLoginRedirect', [_dec5], Object.getOwnPropertyDescriptor(_class4.prototype, 'getLoginRedirect'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getLoginUrl', [_dec6], Object.getOwnPropertyDescriptor(_class4.prototype, 'getLoginUrl'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getSignupUrl', [_dec7], Object.getOwnPropertyDescriptor(_class4.prototype, 'getSignupUrl'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getProfileUrl', [_dec8], Object.getOwnPropertyDescriptor(_class4.prototype, 'getProfileUrl'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getToken', [_dec9], Object.getOwnPropertyDescriptor(_class4.prototype, 'getToken'), _class4.prototype)), _class4)) || _class3); + var AuthorizeStep = exports.AuthorizeStep = (_dec10 = (0, _aureliaDependencyInjection.inject)(AuthService), _dec10(_class5 = function () { + function AuthorizeStep(authService) { + + + LogManager.getLogger('authentication').warn('AuthorizeStep is deprecated. Use AuthenticationStep instead.'); + + this.authService = authService; + } + + AuthorizeStep.prototype.run = function run(routingContext, next) { + var isLoggedIn = this.authService.isAuthenticated(); + var loginRoute = this.authService.config.loginRoute; + + if (routingContext.getAllInstructions().some(function (route) { + return route.config.auth; + })) { + if (!isLoggedIn) { + return next.cancel(new _aureliaRouter.Redirect(loginRoute)); } + } else if (isLoggedIn && routingContext.getAllInstructions().some(function (route) { + return route.fragment === loginRoute; + })) { + return next.cancel(new _aureliaRouter.Redirect(this.authService.config.loginRedirect)); + } - throw new Error('Only `token` responseType is supported'); - }); + return next(); }; - return Auth0Lock; - }()) || _class3); - var OAuth1 = exports.OAuth1 = (_dec3 = (0, _aureliaDependencyInjection.inject)(Storage, Popup, BaseConfig), _dec3(_class4 = function () { - function OAuth1(storage, popup, config) { - _classCallCheck(this, OAuth1); + return AuthorizeStep; + }()) || _class5); + var AuthService = exports.AuthService = (_dec11 = (0, _aureliaDependencyInjection.inject)(Authentication, BaseConfig), _dec12 = (0, _aureliaMetadata.deprecated)({ message: 'Use .getAccessToken() instead.' }), _dec11(_class6 = (_class7 = function () { + function AuthService(authentication, config) { + - this.storage = storage; - this.config = config; - this.popup = popup; - this.defaults = { - url: null, - name: null, - popupOptions: null, - redirectUri: null, - authorizationEndpoint: null - }; - } + this.authenticated = false; + this.timeoutID = 0; - OAuth1.prototype.open = function open(options, userData) { - var _this4 = this; + this.authentication = authentication; + this.config = config; - var provider = (0, _extend2.default)(true, {}, this.defaults, options); - var serverUrl = this.config.joinBase(provider.url); + var oldStorageKey = config.tokenPrefix ? config.tokenPrefix + '_' + config.tokenName : config.tokenName; + var oldToken = authentication.storage.get(oldStorageKey); - if (this.config.platform !== 'mobile') { - this.popup = this.popup.open('', provider.name, provider.popupOptions); + if (oldToken) { + LogManager.getLogger('authentication').info('Found token with deprecated format in storage. Converting it to new format. No further action required.'); + var fakeOldResponse = {}; + fakeOldResponse[config.accessTokenProp] = oldToken; + this.setResponseObject(fakeOldResponse); + authentication.storage.remove(oldStorageKey); } - return this.config.client.post(serverUrl).then(function (response) { - var url = provider.authorizationEndpoint + '?' + (0, _aureliaPath.buildQueryString)(response); + this.setResponseObject(this.authentication.getResponseObject()); + } - if (_this4.config.platform === 'mobile') { - _this4.popup = _this4.popup.open(url, provider.name, provider.popupOptions); - } else { - _this4.popup.popupWindow.location = url; - } + AuthService.prototype.setTimeout = function setTimeout(ttl) { + var _this3 = this; - var popupListener = _this4.config.platform === 'mobile' ? _this4.popup.eventListener(provider.redirectUri) : _this4.popup.pollPopup(); + this.clearTimeout(); - return popupListener.then(function (result) { - return _this4.exchangeForToken(result, userData, provider); - }); - }); + this.timeoutID = _aureliaPal.PLATFORM.global.setTimeout(function () { + if (_this3.config.autoUpdateToken && _this3.authentication.getAccessToken() && _this3.authentication.getRefreshToken()) { + _this3.updateToken(); + } else { + _this3.logout(_this3.config.expiredRedirect); + } + }, ttl); }; - OAuth1.prototype.exchangeForToken = function exchangeForToken(oauthData, userData, provider) { - var data = (0, _extend2.default)(true, {}, userData, oauthData); - var serverUrl = this.config.joinBase(provider.url); - var credentials = this.config.withCredentials ? 'include' : 'same-origin'; - - return this.config.client.post(serverUrl, data, { credentials: credentials }); + AuthService.prototype.clearTimeout = function clearTimeout() { + if (this.timeoutID) { + _aureliaPal.PLATFORM.global.clearTimeout(this.timeoutID); + } + this.timeoutID = 0; }; - return OAuth1; - }()) || _class4); - var OAuth2 = exports.OAuth2 = (_dec4 = (0, _aureliaDependencyInjection.inject)(Storage, Popup, BaseConfig), _dec4(_class5 = function () { - function OAuth2(storage, popup, config) { - _classCallCheck(this, OAuth2); - - this.storage = storage; - this.config = config; - this.popup = popup; - this.defaults = { - url: null, - name: null, - state: null, - scope: null, - scopeDelimiter: null, - redirectUri: null, - popupOptions: null, - authorizationEndpoint: null, - responseParams: null, - requiredUrlParams: null, - optionalUrlParams: null, - defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], - responseType: 'code' - }; - } - - OAuth2.prototype.open = function open(options, userData) { - var _this5 = this; + AuthService.prototype.setResponseObject = function setResponseObject(response) { + this.clearTimeout(); - var provider = (0, _extend2.default)(true, {}, this.defaults, options); - var stateName = provider.name + '_state'; + this.authentication.setResponseObject(response); - if (typeof provider.state === 'function') { - this.storage.set(stateName, provider.state()); - } else if (typeof provider.state === 'string') { - this.storage.set(stateName, provider.state); + this.authenticated = this.authentication.isAuthenticated(); + if (this.authenticated && !Number.isNaN(this.authentication.exp)) { + this.setTimeout(this.getTtl() * 1000); } + }; - var url = provider.authorizationEndpoint + '?' + (0, _aureliaPath.buildQueryString)(this.buildQuery(provider)); - var popup = this.popup.open(url, provider.name, provider.popupOptions); - var openPopup = this.config.platform === 'mobile' ? popup.eventListener(provider.redirectUri) : popup.pollPopup(); - - return openPopup.then(function (oauthData) { - if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { - return oauthData; - } - if (oauthData.state && oauthData.state !== _this5.storage.get(stateName)) { - return Promise.reject('OAuth 2.0 state parameter mismatch.'); - } - return _this5.exchangeForToken(oauthData, userData, provider); - }); + AuthService.prototype.getMe = function getMe(criteriaOrId) { + if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { + criteriaOrId = { id: criteriaOrId }; + } + return this.client.find(this.config.joinBase(this.config.profileUrl), criteriaOrId); }; - OAuth2.prototype.exchangeForToken = function exchangeForToken(oauthData, userData, provider) { - var data = (0, _extend2.default)(true, {}, userData, { - clientId: provider.clientId, - redirectUri: provider.redirectUri - }, oauthData); + AuthService.prototype.updateMe = function updateMe(body, criteriaOrId) { + if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { + criteriaOrId = { id: criteriaOrId }; + } + if (this.config.profileMethod === 'put') { + return this.client.update(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); + } + return this.client.patch(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); + }; - var serverUrl = this.config.joinBase(provider.url); - var credentials = this.config.withCredentials ? 'include' : 'same-origin'; + AuthService.prototype.getAccessToken = function getAccessToken() { + return this.authentication.getAccessToken(); + }; - return this.config.client.post(serverUrl, data, { credentials: credentials }); + AuthService.prototype.getCurrentToken = function getCurrentToken() { + return this.getAccessToken(); }; - OAuth2.prototype.buildQuery = function buildQuery(provider) { - var _this6 = this; + AuthService.prototype.getRefreshToken = function getRefreshToken() { + return this.authentication.getRefreshToken(); + }; - var query = {}; - var urlParams = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams']; + AuthService.prototype.isAuthenticated = function isAuthenticated() { + var authenticated = this.authentication.isAuthenticated(); - urlParams.forEach(function (params) { - (provider[params] || []).forEach(function (paramName) { - var camelizedName = camelCase(paramName); - var paramValue = typeof provider[paramName] === 'function' ? provider[paramName]() : provider[camelizedName]; + if (!authenticated && this.config.autoUpdateToken && this.authentication.getAccessToken() && this.authentication.getRefreshToken()) { + this.updateToken(); + authenticated = true; + } - if (paramName === 'state') { - paramValue = encodeURIComponent(_this6.storage.get(provider.name + '_state')); - } + return authenticated; + }; - if (paramName === 'scope' && Array.isArray(paramValue)) { - paramValue = paramValue.join(provider.scopeDelimiter); + AuthService.prototype.getExp = function getExp() { + return this.authentication.getExp(); + }; - if (provider.scopePrefix) { - paramValue = provider.scopePrefix + provider.scopeDelimiter + paramValue; - } - } + AuthService.prototype.getTtl = function getTtl() { + return this.authentication.getTtl(); + }; - query[paramName] = paramValue; - }); - }); - return query; + AuthService.prototype.isTokenExpired = function isTokenExpired() { + return this.authentication.isTokenExpired(); }; - return OAuth2; - }()) || _class5); + AuthService.prototype.getTokenPayload = function getTokenPayload() { + return this.authentication.getPayload(); + }; + AuthService.prototype.updateToken = function updateToken() { + var _this4 = this; - var camelCase = function camelCase(name) { - return name.replace(/([\:\-\_]+(.))/g, function (_, separator, letter, offset) { - return offset ? letter.toUpperCase() : letter; - }); - }; + if (!this.authentication.getRefreshToken()) { + return Promise.reject(new Error('refreshToken not set')); + } - var Authentication = exports.Authentication = (_dec5 = (0, _aureliaDependencyInjection.inject)(Storage, BaseConfig, OAuth1, OAuth2, Auth0Lock), _dec6 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.loginRoute instead.' }), _dec7 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.loginRedirect instead.' }), _dec8 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.joinBase(baseConfig.loginUrl) instead.' }), _dec9 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.joinBase(baseConfig.signupUrl) instead.' }), _dec10 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.joinBase(baseConfig.profileUrl) instead.' }), _dec11 = (0, _aureliaMetadata.deprecated)({ message: 'Use .getAccessToken() instead.' }), _dec5(_class6 = (_class7 = function () { - function Authentication(storage, config, oAuth1, oAuth2, auth0Lock) { - _classCallCheck(this, Authentication); + if (this.authentication.updateTokenCallstack.length === 0) { + var content = { + grant_type: 'refresh_token', + refresh_token: this.authentication.getRefreshToken(), + client_id: this.config.clientId ? this.config.clientId : undefined + }; - this.storage = storage; - this.config = config; - this.oAuth1 = oAuth1; - this.oAuth2 = oAuth2; - this.auth0Lock = auth0Lock; - this.updateTokenCallstack = []; - this.accessToken = null; - this.refreshToken = null; - this.payload = null; - this.exp = null; - this.hasDataStored = false; - } + this.client.post(this.config.joinBase(this.config.refreshTokenUrl ? this.config.refreshTokenUrl : this.config.loginUrl), content).then(function (response) { + _this4.setResponseObject(response); + _this4.authentication.resolveUpdateTokenCallstack(_this4.isAuthenticated()); + }).catch(function (err) { + _this4.setResponseObject(null); + _this4.authentication.resolveUpdateTokenCallstack(Promise.reject(err)); + }); + } - Authentication.prototype.getLoginRoute = function getLoginRoute() { - return this.config.loginRoute; + return this.authentication.toUpdateTokenCallstack(); }; - Authentication.prototype.getLoginRedirect = function getLoginRedirect() { - return this.config.loginRedirect; - }; + AuthService.prototype.signup = function signup(displayNameOrCredentials, emailOrOptions, passwordOrRedirectUri, options, redirectUri) { + var _this5 = this; - Authentication.prototype.getLoginUrl = function getLoginUrl() { - return this.Config.joinBase(this.config.loginUrl); - }; + var content = void 0; - Authentication.prototype.getSignupUrl = function getSignupUrl() { - return this.Config.joinBase(this.config.signupUrl); - }; + if (_typeof(arguments[0]) === 'object') { + content = arguments[0]; + options = arguments[1]; + redirectUri = arguments[2]; + } else { + content = { + 'displayName': displayNameOrCredentials, + 'email': emailOrOptions, + 'password': passwordOrRedirectUri + }; + } + return this.client.post(this.config.joinBase(this.config.signupUrl), content, options).then(function (response) { + if (_this5.config.loginOnSignup) { + _this5.setResponseObject(response); + } + _this5.authentication.redirect(redirectUri, _this5.config.signupRedirect); - Authentication.prototype.getProfileUrl = function getProfileUrl() { - return this.Config.joinBase(this.config.profileUrl); + return response; + }); }; - Authentication.prototype.getToken = function getToken() { - return this.getAccessToken(); - }; + AuthService.prototype.login = function login(emailOrCredentials, passwordOrOptions, optionsOrRedirectUri, redirectUri) { + var _this6 = this; - Authentication.prototype.getResponseObject = function getResponseObject() { - return JSON.parse(this.storage.get(this.config.storageKey)); - }; + var content = void 0; - Authentication.prototype.setResponseObject = function setResponseObject(response) { - if (response) { - this.getDataFromResponse(response); - this.storage.set(this.config.storageKey, JSON.stringify(response)); - return; + if (_typeof(arguments[0]) === 'object') { + content = arguments[0]; + optionsOrRedirectUri = arguments[1]; + redirectUri = arguments[2]; + } else { + content = { + 'email': emailOrCredentials, + 'password': passwordOrOptions + }; + optionsOrRedirectUri = optionsOrRedirectUri; + } + + if (this.config.clientId) { + content.client_id = this.config.clientId; } - this.accessToken = null; - this.refreshToken = null; - this.payload = null; - this.exp = null; - this.hasDataStored = false; + return this.client.post(this.config.joinBase(this.config.loginUrl), content, optionsOrRedirectUri).then(function (response) { + _this6.setResponseObject(response); - this.storage.remove(this.config.storageKey); - }; + _this6.authentication.redirect(redirectUri, _this6.config.loginRedirect); - Authentication.prototype.getAccessToken = function getAccessToken() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.accessToken; + return response; + }); }; - Authentication.prototype.getRefreshToken = function getRefreshToken() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.refreshToken; - }; + AuthService.prototype.logout = function logout(redirectUri) { + var _this7 = this; - Authentication.prototype.getPayload = function getPayload() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.payload; - }; + var localLogout = function localLogout(response) { + return new Promise(function (resolve) { + _this7.setResponseObject(null); - Authentication.prototype.getExp = function getExp() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.exp; - }; + _this7.authentication.redirect(redirectUri, _this7.config.logoutRedirect); - Authentication.prototype.getTtl = function getTtl() { - var exp = this.getExp(); - return Number.isNaN(exp) ? NaN : exp - Math.round(new Date().getTime() / 1000); - }; + if (typeof _this7.onLogout === 'function') { + _this7.onLogout(response); + } - Authentication.prototype.isTokenExpired = function isTokenExpired() { - var timeLeft = this.getTtl(); - return Number.isNaN(timeLeft) ? undefined : timeLeft < 0; - }; + resolve(response); + }); + }; - Authentication.prototype.isAuthenticated = function isAuthenticated() { - var isTokenExpired = this.isTokenExpired(); - if (isTokenExpired === undefined) return this.accessToken ? true : false; - return !isTokenExpired; + return this.config.logoutUrl ? this.client.request(this.config.logoutMethod, this.config.joinBase(this.config.logoutUrl)).then(localLogout) : localLogout(); }; - Authentication.prototype.getDataFromResponse = function getDataFromResponse(response) { - var config = this.config; + AuthService.prototype.authenticate = function authenticate(name, redirectUri) { + var _this8 = this; - this.accessToken = this.getTokenFromResponse(response, config.accessTokenProp, config.accessTokenName, config.accessTokenRoot); + var userData = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - this.refreshToken = null; - if (config.useRefreshToken) { - try { - this.refreshToken = this.getTokenFromResponse(response, config.refreshTokenProp, config.refreshTokenName, config.refreshTokenRoot); - } catch (e) { - this.refreshToken = null; - } - } + return this.authentication.authenticate(name, userData).then(function (response) { + _this8.setResponseObject(response); - this.payload = null; + _this8.authentication.redirect(redirectUri, _this8.config.loginRedirect); - try { - this.payload = this.accessToken ? (0, _jwtDecode2.default)(this.accessToken) : null; - } catch (_) { - _; + return response; + }); + }; + + AuthService.prototype.unlink = function unlink(name, redirectUri) { + var _this9 = this; + + var unlinkUrl = this.config.joinBase(this.config.unlinkUrl) + name; + return this.client.request(this.config.unlinkMethod, unlinkUrl).then(function (response) { + _this9.authentication.redirect(redirectUri); + + return response; + }); + }; + + _createClass(AuthService, [{ + key: 'client', + get: function get() { + return this.config.client; + } + }, { + key: 'auth', + get: function get() { + LogManager.getLogger('authentication').warn('AuthService.auth is deprecated. Use .authentication instead.'); + return this.authentication; } + }]); - this.exp = this.payload ? parseInt(this.payload.exp, 10) : NaN; + return AuthService; + }(), (_applyDecoratedDescriptor(_class7.prototype, 'getCurrentToken', [_dec12], Object.getOwnPropertyDescriptor(_class7.prototype, 'getCurrentToken'), _class7.prototype)), _class7)) || _class6); - this.hasDataStored = true; + var BaseConfig = exports.BaseConfig = function () { + function BaseConfig() { + - return { - accessToken: this.accessToken, - refreshToken: this.refreshToken, - payload: this.payload, - exp: this.exp + this.client = null; + this.endpoint = null; + this.configureEndpoints = null; + this.loginRedirect = '#/'; + this.logoutRedirect = '#/'; + this.loginRoute = '/login'; + this.loginOnSignup = true; + this.signupRedirect = '#/login'; + this.expiredRedirect = 0; + this.baseUrl = ''; + this.loginUrl = '/auth/login'; + this.logoutUrl = null; + this.logoutMethod = 'get'; + this.signupUrl = '/auth/signup'; + this.profileUrl = '/auth/me'; + this.profileMethod = 'put'; + this.unlinkUrl = '/auth/unlink/'; + this.unlinkMethod = 'get'; + this.refreshTokenUrl = null; + this.authHeader = 'Authorization'; + this.authTokenType = 'Bearer'; + this.accessTokenProp = 'access_token'; + this.accessTokenName = 'token'; + this.accessTokenRoot = false; + this.useRefreshToken = false; + this.autoUpdateToken = true; + this.clientId = false; + this.refreshTokenProp = 'refresh_token'; + this.refreshTokenName = 'token'; + this.refreshTokenRoot = false; + this.httpInterceptor = true; + this.withCredentials = true; + this.platform = 'browser'; + this.storage = 'localStorage'; + this.storageKey = 'aurelia_authentication'; + this.globalValueConverters = ['authFilterValueConverter']; + this.providers = { + facebook: { + name: 'facebook', + url: '/auth/facebook', + authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', + redirectUri: _aureliaPal.PLATFORM.location.origin + '/', + requiredUrlParams: ['display', 'scope'], + scope: ['email'], + scopeDelimiter: ',', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 580, height: 400 } + }, + google: { + name: 'google', + url: '/auth/google', + authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth', + redirectUri: _aureliaPal.PLATFORM.location.origin, + requiredUrlParams: ['scope'], + optionalUrlParams: ['display', 'state'], + scope: ['profile', 'email'], + scopePrefix: 'openid', + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 452, height: 633 }, + state: randomState + }, + github: { + name: 'github', + url: '/auth/github', + authorizationEndpoint: 'https://github.com/login/oauth/authorize', + redirectUri: _aureliaPal.PLATFORM.location.origin, + optionalUrlParams: ['scope'], + scope: ['user:email'], + scopeDelimiter: ' ', + oauthType: '2.0', + popupOptions: { width: 1020, height: 618 } + }, + instagram: { + name: 'instagram', + url: '/auth/instagram', + authorizationEndpoint: 'https://api.instagram.com/oauth/authorize', + redirectUri: _aureliaPal.PLATFORM.location.origin, + requiredUrlParams: ['scope'], + scope: ['basic'], + scopeDelimiter: '+', + oauthType: '2.0' + }, + linkedin: { + name: 'linkedin', + url: '/auth/linkedin', + authorizationEndpoint: 'https://www.linkedin.com/uas/oauth2/authorization', + redirectUri: _aureliaPal.PLATFORM.location.origin, + requiredUrlParams: ['state'], + scope: ['r_emailaddress'], + scopeDelimiter: ' ', + state: 'STATE', + oauthType: '2.0', + popupOptions: { width: 527, height: 582 } + }, + twitter: { + name: 'twitter', + url: '/auth/twitter', + authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', + redirectUri: _aureliaPal.PLATFORM.location.origin, + oauthType: '1.0', + popupOptions: { width: 495, height: 645 } + }, + twitch: { + name: 'twitch', + url: '/auth/twitch', + authorizationEndpoint: 'https://api.twitch.tv/kraken/oauth2/authorize', + redirectUri: _aureliaPal.PLATFORM.location.origin, + requiredUrlParams: ['scope'], + scope: ['user_read'], + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 500, height: 560 } + }, + live: { + name: 'live', + url: '/auth/live', + authorizationEndpoint: 'https://login.live.com/oauth20_authorize.srf', + redirectUri: _aureliaPal.PLATFORM.location.origin, + requiredUrlParams: ['display', 'scope'], + scope: ['wl.emails'], + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 500, height: 560 } + }, + yahoo: { + name: 'yahoo', + url: '/auth/yahoo', + authorizationEndpoint: 'https://api.login.yahoo.com/oauth2/request_auth', + redirectUri: _aureliaPal.PLATFORM.location.origin, + scope: [], + scopeDelimiter: ',', + oauthType: '2.0', + popupOptions: { width: 559, height: 519 } + }, + bitbucket: { + name: 'bitbucket', + url: '/auth/bitbucket', + authorizationEndpoint: 'https://bitbucket.org/site/oauth2/authorize', + redirectUri: _aureliaPal.PLATFORM.location.origin + '/', + requiredUrlParams: ['scope'], + scope: ['email'], + 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: randomState + } }; - }; - - Authentication.prototype.getTokenFromResponse = function getTokenFromResponse(response, tokenProp, tokenName, tokenRoot) { - if (!response) return undefined; - - var responseTokenProp = tokenProp.split('.').reduce(function (o, x) { - return o[x]; - }, response); - - if (typeof responseTokenProp === 'string') { - return responseTokenProp; - } - - if ((typeof responseTokenProp === 'undefined' ? 'undefined' : _typeof(responseTokenProp)) === 'object') { - var tokenRootData = tokenRoot && tokenRoot.split('.').reduce(function (o, x) { - return o[x]; - }, responseTokenProp); - var _token = tokenRootData ? tokenRootData[tokenName] : responseTokenProp[tokenName]; - - if (!_token) throw new Error('Token not found in response'); - - return _token; - } - - var token = response[tokenName] === undefined ? null : response[tokenName]; - - if (!token) throw new Error('Token not found in response'); - - return token; - }; - - Authentication.prototype.toUpdateTokenCallstack = function toUpdateTokenCallstack() { - var _this7 = this; + this._authToken = 'Bearer'; + this._responseTokenProp = 'access_token'; + this._tokenName = 'token'; + this._tokenRoot = false; + this._tokenPrefix = 'aurelia'; + } - return new Promise(function (resolve) { - return _this7.updateTokenCallstack.push(resolve); - }); + BaseConfig.prototype.joinBase = function joinBase(url) { + return (0, _aureliaPath.join)(this.baseUrl, url); }; - Authentication.prototype.resolveUpdateTokenCallstack = function resolveUpdateTokenCallstack(response) { - this.updateTokenCallstack.map(function (resolve) { - return resolve(response); - }); - this.updateTokenCallstack = []; + BaseConfig.prototype.configure = function configure(incomming) { + for (var key in incomming) { + var value = incomming[key]; + if (value !== undefined) { + if (Array.isArray(value) || (typeof value === 'undefined' ? 'undefined' : _typeof(value)) !== 'object' || value === null) { + this[key] = value; + } else { + (0, _extend2.default)(true, this[key], value); + } + } + } }; - Authentication.prototype.authenticate = function authenticate(name) { - var userData = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - - var oauthType = this.config.providers[name].type; - - if (oauthType) { - LogManager.getLogger('authentication').warn('DEPRECATED: Setting provider.type is deprecated and replaced by provider.oauthType'); - } else { - oauthType = this.config.providers[name].oauthType; + _createClass(BaseConfig, [{ + key: 'authToken', + set: function set(authToken) { + LogManager.getLogger('authentication').warn('BaseConfig.authToken is deprecated. Use BaseConfig.authTokenType instead.'); + this._authTokenType = authToken; + this.authTokenType = authToken; + return authToken; + }, + get: function get() { + return this._authTokenType; } - - var providerLogin = void 0; - if (oauthType === 'auth0-lock') { - providerLogin = this.auth0Lock; - } else { - providerLogin = oauthType === '1.0' ? this.oAuth1 : this.oAuth2; + }, { + key: 'responseTokenProp', + set: function set(responseTokenProp) { + LogManager.getLogger('authentication').warn('BaseConfig.responseTokenProp is deprecated. Use BaseConfig.accessTokenProp instead.'); + this._responseTokenProp = responseTokenProp; + this.accessTokenProp = responseTokenProp; + return responseTokenProp; + }, + get: function get() { + return this._responseTokenProp; } - - return providerLogin.open(this.config.providers[name], userData); - }; - - Authentication.prototype.redirect = function redirect(redirectUrl, defaultRedirectUrl) { - if (redirectUrl === true) { - LogManager.getLogger('authentication').warn('DEPRECATED: Setting redirectUrl === true to actually *not redirect* is deprecated. Set redirectUrl === 0 instead.'); - return; + }, { + key: 'tokenRoot', + set: function set(tokenRoot) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenRoot is deprecated. Use BaseConfig.accessTokenRoot instead.'); + this._tokenRoot = tokenRoot; + this.accessTokenRoot = tokenRoot; + return tokenRoot; + }, + get: function get() { + return this._tokenRoot; } - - if (redirectUrl === false) { - LogManager.getLogger('authentication').warn('BREAKING CHANGE: Setting redirectUrl === false to actually *do redirect* is deprecated. Set redirectUrl to undefined or null to use the defaultRedirectUrl if so desired.'); + }, { + key: 'tokenName', + set: function set(tokenName) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenName is deprecated. Use BaseConfig.accessTokenName instead.'); + this._tokenName = tokenName; + this.accessTokenName = tokenName; + return tokenName; + }, + get: function get() { + return this._tokenName; } - - if (redirectUrl === 0) { - return; + }, { + key: 'tokenPrefix', + set: function set(tokenPrefix) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenPrefix is obsolete. Use BaseConfig.storageKey instead.'); + this._tokenPrefix = tokenPrefix; + return tokenPrefix; + }, + get: function get() { + return this._tokenPrefix || 'aurelia'; } - if (typeof redirectUrl === 'string') { - _aureliaPal.PLATFORM.location.href = encodeURI(redirectUrl); - } else if (defaultRedirectUrl) { - _aureliaPal.PLATFORM.location.href = defaultRedirectUrl; + }, { + key: 'current', + get: function get() { + LogManager.getLogger('authentication').warn('Getter BaseConfig.current is deprecated. Use BaseConfig directly instead.'); + return this; + }, + set: function set(_) { + throw new Error('Setter BaseConfig.current is obsolete. Use BaseConfig directly instead.'); } - }; - - _createClass(Authentication, [{ - key: 'responseObject', + }, { + key: '_current', get: function get() { - LogManager.getLogger('authentication').warn('Getter Authentication.responseObject is deprecated. Use Authentication.getResponseObject() instead.'); - return this.getResponseObject(); + LogManager.getLogger('authentication').warn('Getter BaseConfig._current is deprecated. Use BaseConfig directly instead.'); + return this; }, - set: function set(response) { - LogManager.getLogger('authentication').warn('Setter Authentication.responseObject is deprecated. Use AuthServive.setResponseObject(response) instead.'); - this.setResponseObject(response); + set: function set(_) { + throw new Error('Setter BaseConfig._current is obsolete. Use BaseConfig directly instead.'); } }]); - return Authentication; - }(), (_applyDecoratedDescriptor(_class7.prototype, 'getLoginRoute', [_dec6], Object.getOwnPropertyDescriptor(_class7.prototype, 'getLoginRoute'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getLoginRedirect', [_dec7], Object.getOwnPropertyDescriptor(_class7.prototype, 'getLoginRedirect'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getLoginUrl', [_dec8], Object.getOwnPropertyDescriptor(_class7.prototype, 'getLoginUrl'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getSignupUrl', [_dec9], Object.getOwnPropertyDescriptor(_class7.prototype, 'getSignupUrl'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getProfileUrl', [_dec10], Object.getOwnPropertyDescriptor(_class7.prototype, 'getProfileUrl'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getToken', [_dec11], Object.getOwnPropertyDescriptor(_class7.prototype, 'getToken'), _class7.prototype)), _class7)) || _class6); - var AuthService = exports.AuthService = (_dec12 = (0, _aureliaDependencyInjection.inject)(Authentication, BaseConfig), _dec13 = (0, _aureliaMetadata.deprecated)({ message: 'Use .getAccessToken() instead.' }), _dec12(_class8 = (_class9 = function () { - function AuthService(authentication, config) { - _classCallCheck(this, AuthService); - - this.authenticated = false; - this.timeoutID = 0; - - this.authentication = authentication; - this.config = config; + return BaseConfig; + }(); - var oldStorageKey = config.tokenPrefix ? config.tokenPrefix + '_' + config.tokenName : config.tokenName; - var oldToken = authentication.storage.get(oldStorageKey); + function randomState() { + var rand = Math.random().toString(36).substr(2); + return encodeURIComponent(rand); + } - if (oldToken) { - LogManager.getLogger('authentication').info('Found token with deprecated format in storage. Converting it to new format. No further action required.'); - var fakeOldResponse = {}; - fakeOldResponse[config.accessTokenProp] = oldToken; - this.setResponseObject(fakeOldResponse); - authentication.storage.remove(oldStorageKey); - } + var FetchConfig = exports.FetchConfig = (_dec13 = (0, _aureliaDependencyInjection.inject)(_aureliaFetchClient.HttpClient, _aureliaApi.Config, AuthService, BaseConfig), _dec13(_class10 = function () { + function FetchConfig(httpClient, clientConfig, authService, config) { + - this.setResponseObject(this.authentication.getResponseObject()); + this.httpClient = httpClient; + this.clientConfig = clientConfig; + this.authService = authService; + this.config = config; } - AuthService.prototype.setTimeout = function setTimeout(ttl) { - var _this8 = this; - - this.clearTimeout(); - - this.timeoutID = _aureliaPal.PLATFORM.global.setTimeout(function () { - if (_this8.config.autoUpdateToken && _this8.authentication.getAccessToken() && _this8.authentication.getRefreshToken()) { - _this8.updateToken(); - } else { - _this8.logout(_this8.config.expiredRedirect); - } - }, ttl); - }; - - AuthService.prototype.clearTimeout = function clearTimeout() { - if (this.timeoutID) { - _aureliaPal.PLATFORM.global.clearTimeout(this.timeoutID); - } - this.timeoutID = 0; - }; - - AuthService.prototype.setResponseObject = function setResponseObject(response) { - this.clearTimeout(); - - this.authentication.setResponseObject(response); - - this.authenticated = this.authentication.isAuthenticated(); - if (this.authenticated && !Number.isNaN(this.authentication.exp)) { - this.setTimeout(this.getTtl() * 1000); - } - }; - - AuthService.prototype.getMe = function getMe(criteriaOrId) { - if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { - criteriaOrId = { id: criteriaOrId }; - } - return this.client.find(this.config.joinBase(this.config.profileUrl), criteriaOrId); - }; - - AuthService.prototype.updateMe = function updateMe(body, criteriaOrId) { - if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { - criteriaOrId = { id: criteriaOrId }; - } - if (this.config.profileMethod === 'put') { - return this.client.update(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); - } - return this.client.patch(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); - }; - - AuthService.prototype.getAccessToken = function getAccessToken() { - return this.authentication.getAccessToken(); - }; - - AuthService.prototype.getCurrentToken = function getCurrentToken() { - return this.getAccessToken(); - }; + FetchConfig.prototype.configure = function configure(client) { + var _this10 = this; - AuthService.prototype.getRefreshToken = function getRefreshToken() { - return this.authentication.getRefreshToken(); - }; + if (Array.isArray(client)) { + var _ret = function () { + var configuredClients = []; + client.forEach(function (toConfigure) { + configuredClients.push(_this10.configure(toConfigure)); + }); - AuthService.prototype.isAuthenticated = function isAuthenticated() { - var authenticated = this.authentication.isAuthenticated(); + return { + v: configuredClients + }; + }(); - if (!authenticated && this.config.autoUpdateToken && this.authentication.getAccessToken() && this.authentication.getRefreshToken()) { - this.updateToken(); - authenticated = true; + if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; } - return authenticated; - }; - - AuthService.prototype.getExp = function getExp() { - return this.authentication.getExp(); - }; + if (typeof client === 'string') { + var endpoint = this.clientConfig.getEndpoint(client); + if (!endpoint) { + throw new Error('There is no \'' + (client || 'default') + '\' endpoint registered.'); + } + client = endpoint.client; + } else if (client instanceof _aureliaApi.Rest) { + client = client.client; + } else if (!(client instanceof _aureliaFetchClient.HttpClient)) { + client = this.httpClient; + } - AuthService.prototype.getTtl = function getTtl() { - return this.authentication.getTtl(); - }; + client.interceptors.push(this.interceptor); - AuthService.prototype.isTokenExpired = function isTokenExpired() { - return this.authentication.isTokenExpired(); + return client; }; - AuthService.prototype.getTokenPayload = function getTokenPayload() { - return this.authentication.getPayload(); - }; + _createClass(FetchConfig, [{ + key: 'interceptor', + get: function get() { + var _this11 = this; - AuthService.prototype.updateToken = function updateToken() { - var _this9 = this; + return { + request: function request(_request) { + if (!_this11.config.httpInterceptor || !_this11.authService.isAuthenticated()) { + return _request; + } + var token = _this11.authService.getAccessToken(); - if (!this.authentication.getRefreshToken()) { - return Promise.reject(new Error('refreshToken not set')); - } + if (_this11.config.authTokenType) { + token = _this11.config.authTokenType + ' ' + token; + } - if (this.authentication.updateTokenCallstack.length === 0) { - var content = { - grant_type: 'refresh_token', - refresh_token: this.authentication.getRefreshToken(), - client_id: this.config.clientId ? this.config.clientId : undefined - }; + _request.headers.set(_this11.config.authHeader, token); - this.client.post(this.config.joinBase(this.config.refreshTokenUrl ? this.config.refreshTokenUrl : this.config.loginUrl), content).then(function (response) { - _this9.setResponseObject(response); - _this9.authentication.resolveUpdateTokenCallstack(_this9.isAuthenticated()); - }).catch(function (err) { - _this9.setResponseObject(null); - _this9.authentication.resolveUpdateTokenCallstack(Promise.reject(err)); - }); - } + return _request; + }, + response: function response(_response, request) { + return new Promise(function (resolve, reject) { + if (_response.ok) { + return resolve(_response); + } + if (_response.status !== 401) { + return resolve(_response); + } + if (!_this11.config.httpInterceptor || !_this11.authService.isTokenExpired()) { + return resolve(_response); + } + if (!_this11.config.useRefreshToken || !_this11.authService.getRefreshToken()) { + return resolve(_response); + } - return this.authentication.toUpdateTokenCallstack(); - }; + return _this11.authService.updateToken().then(function () { + var token = _this11.authService.getAccessToken(); - AuthService.prototype.signup = function signup(displayNameOrCredentials, emailOrOptions, passwordOrRedirectUri, options, redirectUri) { - var _this10 = this; + if (_this11.config.authTokenType) { + token = _this11.config.authTokenType + ' ' + token; + } - var content = void 0; + request.headers.set(_this11.config.authHeader, token); - if (_typeof(arguments[0]) === 'object') { - content = arguments[0]; - options = arguments[1]; - redirectUri = arguments[2]; - } else { - content = { - 'displayName': displayNameOrCredentials, - 'email': emailOrOptions, - 'password': passwordOrRedirectUri + return _this11.client.fetch(request).then(resolve); + }); + }); + } }; } - return this.client.post(this.config.joinBase(this.config.signupUrl), content, options).then(function (response) { - if (_this10.config.loginOnSignup) { - _this10.setResponseObject(response); - } - _this10.authentication.redirect(redirectUri, _this10.config.signupRedirect); + }]); - return response; - }); - }; + return FetchConfig; + }()) || _class10); + var OAuth1 = exports.OAuth1 = (_dec14 = (0, _aureliaDependencyInjection.inject)(Storage, Popup, BaseConfig), _dec14(_class11 = function () { + function OAuth1(storage, popup, config) { + - AuthService.prototype.login = function login(emailOrCredentials, passwordOrOptions, optionsOrRedirectUri, redirectUri) { - var _this11 = this; + this.storage = storage; + this.config = config; + this.popup = popup; + this.defaults = { + url: null, + name: null, + popupOptions: null, + redirectUri: null, + authorizationEndpoint: null + }; + } - var content = void 0; + OAuth1.prototype.open = function open(options, userData) { + var _this12 = this; - if (_typeof(arguments[0]) === 'object') { - content = arguments[0]; - optionsOrRedirectUri = arguments[1]; - redirectUri = arguments[2]; - } else { - content = { - 'email': emailOrCredentials, - 'password': passwordOrOptions - }; - optionsOrRedirectUri = optionsOrRedirectUri; - } + var provider = (0, _extend2.default)(true, {}, this.defaults, options); + var serverUrl = this.config.joinBase(provider.url); - if (this.config.clientId) { - content.client_id = this.config.clientId; + if (this.config.platform !== 'mobile') { + this.popup = this.popup.open('', provider.name, provider.popupOptions); } - return this.client.post(this.config.joinBase(this.config.loginUrl), content, optionsOrRedirectUri).then(function (response) { - _this11.setResponseObject(response); + return this.config.client.post(serverUrl).then(function (response) { + var url = provider.authorizationEndpoint + '?' + (0, _aureliaPath.buildQueryString)(response); + + if (_this12.config.platform === 'mobile') { + _this12.popup = _this12.popup.open(url, provider.name, provider.popupOptions); + } else { + _this12.popup.popupWindow.location = url; + } - _this11.authentication.redirect(redirectUri, _this11.config.loginRedirect); + var popupListener = _this12.config.platform === 'mobile' ? _this12.popup.eventListener(provider.redirectUri) : _this12.popup.pollPopup(); - return response; + return popupListener.then(function (result) { + return _this12.exchangeForToken(result, userData, provider); + }); }); }; - AuthService.prototype.logout = function logout(redirectUri) { - var _this12 = this; - - var localLogout = function localLogout(response) { - return new Promise(function (resolve) { - _this12.setResponseObject(null); + OAuth1.prototype.exchangeForToken = function exchangeForToken(oauthData, userData, provider) { + var data = (0, _extend2.default)(true, {}, userData, oauthData); + var serverUrl = this.config.joinBase(provider.url); + var credentials = this.config.withCredentials ? 'include' : 'same-origin'; - _this12.authentication.redirect(redirectUri, _this12.config.logoutRedirect); + return this.config.client.post(serverUrl, data, { credentials: credentials }); + }; - if (typeof _this12.onLogout === 'function') { - _this12.onLogout(response); - } + return OAuth1; + }()) || _class11); + var OAuth2 = exports.OAuth2 = (_dec15 = (0, _aureliaDependencyInjection.inject)(Storage, Popup, BaseConfig), _dec15(_class12 = function () { + function OAuth2(storage, popup, config) { + - resolve(response); - }); + this.storage = storage; + this.config = config; + this.popup = popup; + this.defaults = { + url: null, + name: null, + state: null, + scope: null, + scopeDelimiter: null, + redirectUri: null, + popupOptions: null, + authorizationEndpoint: null, + responseParams: null, + requiredUrlParams: null, + optionalUrlParams: null, + defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], + responseType: 'code' }; + } - return this.config.logoutUrl ? this.client.request(this.config.logoutMethod, this.config.joinBase(this.config.logoutUrl)).then(localLogout) : localLogout(); - }; - - AuthService.prototype.authenticate = function authenticate(name, redirectUri) { + OAuth2.prototype.open = function open(options, userData) { var _this13 = this; - var userData = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - - return this.authentication.authenticate(name, userData).then(function (response) { - _this13.setResponseObject(response); - - _this13.authentication.redirect(redirectUri, _this13.config.loginRedirect); - - return response; - }); - }; + var provider = (0, _extend2.default)(true, {}, this.defaults, options); + var stateName = provider.name + '_state'; - AuthService.prototype.unlink = function unlink(name, redirectUri) { - var _this14 = this; + if (typeof provider.state === 'function') { + this.storage.set(stateName, provider.state()); + } else if (typeof provider.state === 'string') { + this.storage.set(stateName, provider.state); + } - var unlinkUrl = this.config.joinBase(this.config.unlinkUrl) + name; - return this.client.request(this.config.unlinkMethod, unlinkUrl).then(function (response) { - _this14.authentication.redirect(redirectUri); + var url = provider.authorizationEndpoint + '?' + (0, _aureliaPath.buildQueryString)(this.buildQuery(provider)); + var popup = this.popup.open(url, provider.name, provider.popupOptions); + var openPopup = this.config.platform === 'mobile' ? popup.eventListener(provider.redirectUri) : popup.pollPopup(); - return response; + return openPopup.then(function (oauthData) { + if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { + return oauthData; + } + if (oauthData.state && oauthData.state !== _this13.storage.get(stateName)) { + return Promise.reject('OAuth 2.0 state parameter mismatch.'); + } + return _this13.exchangeForToken(oauthData, userData, provider); }); }; - _createClass(AuthService, [{ - key: 'client', - get: function get() { - return this.config.client; - } - }, { - key: 'auth', - get: function get() { - LogManager.getLogger('authentication').warn('AuthService.auth is deprecated. Use .authentication instead.'); - return this.authentication; - } - }]); - - return AuthService; - }(), (_applyDecoratedDescriptor(_class9.prototype, 'getCurrentToken', [_dec13], Object.getOwnPropertyDescriptor(_class9.prototype, 'getCurrentToken'), _class9.prototype)), _class9)) || _class8); - var AuthenticateStep = exports.AuthenticateStep = (_dec14 = (0, _aureliaDependencyInjection.inject)(AuthService), _dec14(_class11 = function () { - function AuthenticateStep(authService) { - _classCallCheck(this, AuthenticateStep); - - this.authService = authService; - } - - AuthenticateStep.prototype.run = function run(routingContext, next) { - var isLoggedIn = this.authService.authenticated; - var loginRoute = this.authService.config.loginRoute; + OAuth2.prototype.exchangeForToken = function exchangeForToken(oauthData, userData, provider) { + var data = (0, _extend2.default)(true, {}, userData, { + clientId: provider.clientId, + redirectUri: provider.redirectUri + }, oauthData); - if (routingContext.getAllInstructions().some(function (route) { - return route.config.auth === true; - })) { - if (!isLoggedIn) { - return next.cancel(new _aureliaRouter.Redirect(loginRoute)); - } - } else if (isLoggedIn && routingContext.getAllInstructions().some(function (route) { - return route.fragment === loginRoute; - })) { - return next.cancel(new _aureliaRouter.Redirect(this.authService.config.loginRedirect)); - } + var serverUrl = this.config.joinBase(provider.url); + var credentials = this.config.withCredentials ? 'include' : 'same-origin'; - return next(); + return this.config.client.post(serverUrl, data, { credentials: credentials }); }; - return AuthenticateStep; - }()) || _class11); - var AuthorizeStep = exports.AuthorizeStep = (_dec15 = (0, _aureliaDependencyInjection.inject)(AuthService), _dec15(_class12 = function () { - function AuthorizeStep(authService) { - _classCallCheck(this, AuthorizeStep); + OAuth2.prototype.buildQuery = function buildQuery(provider) { + var _this14 = this; - LogManager.getLogger('authentication').warn('AuthorizeStep is deprecated. Use AuthenticationStep instead.'); + var query = {}; + var urlParams = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams']; - this.authService = authService; - } + urlParams.forEach(function (params) { + (provider[params] || []).forEach(function (paramName) { + var camelizedName = camelCase(paramName); + var paramValue = typeof provider[paramName] === 'function' ? provider[paramName]() : provider[camelizedName]; - AuthorizeStep.prototype.run = function run(routingContext, next) { - var isLoggedIn = this.authService.isAuthenticated(); - var loginRoute = this.authService.config.loginRoute; + if (paramName === 'state') { + paramValue = encodeURIComponent(_this14.storage.get(provider.name + '_state')); + } - if (routingContext.getAllInstructions().some(function (route) { - return route.config.auth; - })) { - if (!isLoggedIn) { - return next.cancel(new _aureliaRouter.Redirect(loginRoute)); - } - } else if (isLoggedIn && routingContext.getAllInstructions().some(function (route) { - return route.fragment === loginRoute; - })) { - return next.cancel(new _aureliaRouter.Redirect(this.authService.config.loginRedirect)); - } + if (paramName === 'scope' && Array.isArray(paramValue)) { + paramValue = paramValue.join(provider.scopeDelimiter); - return next(); + if (provider.scopePrefix) { + paramValue = provider.scopePrefix + provider.scopeDelimiter + paramValue; + } + } + + query[paramName] = paramValue; + }); + }); + return query; }; - return AuthorizeStep; + return OAuth2; }()) || _class12); - var FetchConfig = exports.FetchConfig = (_dec16 = (0, _aureliaDependencyInjection.inject)(_aureliaFetchClient.HttpClient, _aureliaApi.Config, AuthService, BaseConfig), _dec16(_class13 = function () { - function FetchConfig(httpClient, clientConfig, authService, config) { - _classCallCheck(this, FetchConfig); - this.httpClient = httpClient; - this.clientConfig = clientConfig; - this.authService = authService; - this.config = config; - } - FetchConfig.prototype.configure = function configure(client) { - var _this15 = this; + var camelCase = function camelCase(name) { + return name.replace(/([\:\-\_]+(.))/g, function (_, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }); + }; - if (Array.isArray(client)) { - var _ret = function () { - var configuredClients = []; - client.forEach(function (toConfigure) { - configuredClients.push(_this15.configure(toConfigure)); - }); + var Popup = exports.Popup = function () { + function Popup() { + - return { - v: configuredClients - }; - }(); + this.popupWindow = null; + this.polling = null; + this.url = ''; + } - if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; - } + Popup.prototype.open = function open(url, windowName, options) { + this.url = url; + var optionsString = buildPopupWindowOptions(options || {}); - if (typeof client === 'string') { - var endpoint = this.clientConfig.getEndpoint(client); - if (!endpoint) { - throw new Error('There is no \'' + (client || 'default') + '\' endpoint registered.'); - } - client = endpoint.client; - } else if (client instanceof _aureliaApi.Rest) { - client = client.client; - } else if (!(client instanceof _aureliaFetchClient.HttpClient)) { - client = this.httpClient; - } + this.popupWindow = _aureliaPal.PLATFORM.global.open(url, windowName, optionsString); - client.interceptors.push(this.interceptor); + if (this.popupWindow && this.popupWindow.focus) { + this.popupWindow.focus(); + } - return client; + return this; }; - _createClass(FetchConfig, [{ - key: 'interceptor', - get: function get() { - var _this16 = this; + Popup.prototype.eventListener = function eventListener(redirectUri) { + var _this15 = this; - return { - request: function request(_request) { - if (!_this16.config.httpInterceptor || !_this16.authService.isAuthenticated()) { - return _request; - } - var token = _this16.authService.getAccessToken(); + return new Promise(function (resolve, reject) { + _this15.popupWindow.addEventListener('loadstart', function (event) { + if (event.url.indexOf(redirectUri) !== 0) { + return; + } + + var parser = _aureliaPal.DOM.createElement('a'); + parser.href = event.url; + + if (parser.search || parser.hash) { + var qs = parseUrl(parser); - if (_this16.config.authTokenType) { - token = _this16.config.authTokenType + ' ' + token; + if (qs.error) { + reject({ error: qs.error }); + } else { + resolve(qs); } - _request.headers.set(_this16.config.authHeader, token); + _this15.popupWindow.close(); + } + }); - return _request; - }, - response: function response(_response, request) { - return new Promise(function (resolve, reject) { - if (_response.ok) { - return resolve(_response); - } - if (_response.status !== 401) { - return resolve(_response); - } - if (!_this16.config.httpInterceptor || !_this16.authService.isTokenExpired()) { - return resolve(_response); - } - if (!_this16.config.useRefreshToken || !_this16.authService.getRefreshToken()) { - return resolve(_response); - } + _this15.popupWindow.addEventListener('exit', function () { + reject({ data: 'Provider Popup was closed' }); + }); - _this16.authService.updateToken().then(function () { - var token = _this16.authService.getAccessToken(); + _this15.popupWindow.addEventListener('loaderror', function () { + reject({ data: 'Authorization Failed' }); + }); + }); + }; - if (_this16.config.authTokenType) { - token = _this16.config.authTokenType + ' ' + token; - } + Popup.prototype.pollPopup = function pollPopup() { + var _this16 = this; - request.headers.set(_this16.config.authHeader, token); + return new Promise(function (resolve, reject) { + _this16.polling = _aureliaPal.PLATFORM.global.setInterval(function () { + var errorData = void 0; - return _this16.client.fetch(request).then(resolve); - }); - }); - } - }; - } - }]); + try { + if (_this16.popupWindow.location.host === _aureliaPal.PLATFORM.global.document.location.host && (_this16.popupWindow.location.search || _this16.popupWindow.location.hash)) { + var qs = parseUrl(_this16.popupWindow.location); - return FetchConfig; - }()) || _class13); + if (qs.error) { + reject({ error: qs.error }); + } else { + resolve(qs); + } - function configure(aurelia, config) { - if (!_aureliaPal.PLATFORM.location.origin) { - _aureliaPal.PLATFORM.location.origin = _aureliaPal.PLATFORM.location.protocol + '//' + _aureliaPal.PLATFORM.location.hostname + (_aureliaPal.PLATFORM.location.port ? ':' + _aureliaPal.PLATFORM.location.port : ''); - } + _this16.popupWindow.close(); + _aureliaPal.PLATFORM.global.clearInterval(_this16.polling); + } + } catch (error) { + errorData = error; + } - var baseConfig = aurelia.container.get(BaseConfig); + if (!_this16.popupWindow) { + _aureliaPal.PLATFORM.global.clearInterval(_this16.polling); + reject({ + error: errorData, + data: 'Provider Popup Blocked' + }); + } else if (_this16.popupWindow.closed) { + _aureliaPal.PLATFORM.global.clearInterval(_this16.polling); + reject({ + error: errorData, + data: 'Problem poll popup' + }); + } + }, 35); + }); + }; - if (typeof config === 'function') { - config(baseConfig); - } else if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') { - baseConfig.configure(config); - } + return Popup; + }(); - for (var _iterator = baseConfig.globalValueConverters, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; + var buildPopupWindowOptions = function buildPopupWindowOptions(options) { + var width = options.width || 500; + var height = options.height || 500; - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } + var extended = (0, _extend2.default)({ + width: width, + height: height, + left: _aureliaPal.PLATFORM.global.screenX + (_aureliaPal.PLATFORM.global.outerWidth - width) / 2, + top: _aureliaPal.PLATFORM.global.screenY + (_aureliaPal.PLATFORM.global.outerHeight - height) / 2.5 + }, options); - var converter = _ref; + var parts = []; + Object.keys(extended).map(function (key) { + return parts.push(key + '=' + extended[key]); + }); - aurelia.globalResources('./' + converter); - LogManager.getLogger('authentication').info('Add globalResources value-converter: ' + converter); - } - var fetchConfig = aurelia.container.get(FetchConfig); - var clientConfig = aurelia.container.get(_aureliaApi.Config); + return parts.join(','); + }; - if (Array.isArray(baseConfig.configureEndpoints)) { - baseConfig.configureEndpoints.forEach(function (endpointToPatch) { - fetchConfig.configure(endpointToPatch); - }); - } + var parseUrl = function parseUrl(url) { + return (0, _extend2.default)(true, {}, (0, _aureliaPath.parseQueryString)(url.search), (0, _aureliaPath.parseQueryString)(url.hash)); + }; - var client = void 0; + var Storage = exports.Storage = (_dec16 = (0, _aureliaDependencyInjection.inject)(BaseConfig), _dec16(_class13 = function () { + function Storage(config) { + - if (baseConfig.endpoint !== null) { - if (typeof baseConfig.endpoint === 'string') { - var endpoint = clientConfig.getEndpoint(baseConfig.endpoint); - if (!endpoint) { - throw new Error('There is no \'' + (baseConfig.endpoint || 'default') + '\' endpoint registered.'); - } - client = endpoint; - } else if (baseConfig.endpoint instanceof _aureliaFetchClient.HttpClient) { - client = new _aureliaApi.Rest(baseConfig.endpoint); - } + this.config = config; } - if (!(client instanceof _aureliaApi.Rest)) { - client = new _aureliaApi.Rest(aurelia.container.get(_aureliaFetchClient.HttpClient)); - } + Storage.prototype.get = function get(key) { + return _aureliaPal.PLATFORM.global[this.config.storage].getItem(key); + }; - baseConfig.client = client; - } + Storage.prototype.set = function set(key, value) { + _aureliaPal.PLATFORM.global[this.config.storage].setItem(key, value); + }; - exports.configure = configure; - exports.FetchConfig = FetchConfig; - exports.AuthService = AuthService; - exports.AuthorizeStep = AuthorizeStep; - exports.AuthenticateStep = AuthenticateStep; + Storage.prototype.remove = function remove(key) { + _aureliaPal.PLATFORM.global[this.config.storage].removeItem(key); + }; + + return Storage; + }()) || _class13); }); \ No newline at end of file diff --git a/dist/amd/authFilterValueConverter.js b/dist/amd/authFilterValueConverter.js index 2db97e9..070d997 100644 --- a/dist/amd/authFilterValueConverter.js +++ b/dist/amd/authFilterValueConverter.js @@ -5,15 +5,11 @@ define(['exports'], function (exports) { value: true }); - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } + var AuthFilterValueConverter = exports.AuthFilterValueConverter = function () { function AuthFilterValueConverter() { - _classCallCheck(this, AuthFilterValueConverter); + } AuthFilterValueConverter.prototype.toView = function toView(routes, isAuthenticated) { diff --git a/dist/amd/authenticatedFilterValueConverter.js b/dist/amd/authenticatedFilterValueConverter.js index eb07591..3afe943 100644 --- a/dist/amd/authenticatedFilterValueConverter.js +++ b/dist/amd/authenticatedFilterValueConverter.js @@ -6,17 +6,13 @@ define(['exports', 'aurelia-dependency-injection', './aurelia-authentication'], }); exports.AuthenticatedFilterValueConverter = undefined; - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } + var _dec, _class; var AuthenticatedFilterValueConverter = exports.AuthenticatedFilterValueConverter = (_dec = (0, _aureliaDependencyInjection.inject)(_aureliaAuthentication.AuthService), _dec(_class = function () { function AuthenticatedFilterValueConverter(authService) { - _classCallCheck(this, AuthenticatedFilterValueConverter); + this.authService = authService; } diff --git a/dist/amd/authenticatedValueConverter.js b/dist/amd/authenticatedValueConverter.js index 6f164ce..630d256 100644 --- a/dist/amd/authenticatedValueConverter.js +++ b/dist/amd/authenticatedValueConverter.js @@ -6,17 +6,13 @@ define(['exports', 'aurelia-dependency-injection', './aurelia-authentication'], }); exports.AuthenticatedValueConverter = undefined; - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } + var _dec, _class; var AuthenticatedValueConverter = exports.AuthenticatedValueConverter = (_dec = (0, _aureliaDependencyInjection.inject)(_aureliaAuthentication.AuthService), _dec(_class = function () { function AuthenticatedValueConverter(authService) { - _classCallCheck(this, AuthenticatedValueConverter); + this.authService = authService; } diff --git a/dist/amd/index.js b/dist/amd/index.js new file mode 100644 index 0000000..a0aa480 --- /dev/null +++ b/dist/amd/index.js @@ -0,0 +1,16 @@ +define(['exports', './aurelia-authentication'], function (exports, _aureliaAuthentication) { + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + Object.keys(_aureliaAuthentication).forEach(function (key) { + if (key === "default") return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _aureliaAuthentication[key]; + } + }); + }); +}); \ No newline at end of file diff --git a/dist/aurelia-authentication.d.ts b/dist/aurelia-authentication.d.ts new file mode 100644 index 0000000..ab6db42 --- /dev/null +++ b/dist/aurelia-authentication.d.ts @@ -0,0 +1,505 @@ +import * as LogManager from 'aurelia-logging'; +import {PLATFORM,DOM} from 'aurelia-pal'; +import {HttpClient} from 'aurelia-fetch-client'; +import {Config,Rest} from 'aurelia-api'; +import {inject} from 'aurelia-dependency-injection'; +import {Redirect} from 'aurelia-router'; +import {deprecated} from 'aurelia-metadata'; +import {join,buildQueryString,parseQueryString} from 'aurelia-path'; + +// import to ensure value-converters get bundled +import './authFilterValueConverter'; + +/** + * Configure the plugin. + * + * @param {{globalResources: Function, container: {Container}}} aurelia + * @param {{}|Function} config + */ +export declare function configure(aurelia?: any, config?: any): any; +export declare class Auth0Lock { + constructor(storage?: any, config?: any); + open(options?: any, userData?: any): any; +} +export declare class AuthenticateStep { + constructor(authService?: any); + run(routingContext?: any, next?: any): any; +} +export declare class Authentication { + constructor(storage?: any, config?: any, oAuth1?: any, oAuth2?: any, auth0Lock?: any); + + /* deprecated methods */ + getLoginRoute(): any; + getLoginRedirect(): any; + getLoginUrl(): any; + getSignupUrl(): any; + getProfileUrl(): any; + getToken(): any; + responseObject: any; + + /* get/set responseObject */ + getResponseObject(): any; + setResponseObject(response?: any): any; + + /* get data, update if needed first */ + getAccessToken(): any; + getRefreshToken(): any; + getPayload(): any; + getExp(): any; + + /* get status from data */ + getTtl(): any; + isTokenExpired(): any; + isAuthenticated(): any; + + /* get and set from response */ + getDataFromResponse(response?: any): any; + getTokenFromResponse(response?: any, tokenProp?: any, tokenName?: any, tokenRoot?: any): any; + toUpdateTokenCallstack(): any; + resolveUpdateTokenCallstack(response?: any): any; + + /** + * Authenticate with third-party + * + * @param {String} name of the provider + * @param {[{}]} [userData] + * + * @return {Promise} + */ + authenticate(name?: any, userData?: any): any; + redirect(redirectUrl?: any, defaultRedirectUrl?: any): any; +} +export declare class AuthorizeStep { + constructor(authService?: any); + run(routingContext?: any, next?: any): any; +} +export declare class AuthService { + + /** + * The Authentication instance that handles the token + * + * @param {Authentication} + */ + authentication: any; + + /** + * The Config instance that contains the current configuration setting + * + * @param {Config} + */ + config: any; + + /** + * The current login status + * + * @param {Boolean} + */ + authenticated: any; + + /** + * The currently set timeoutID + * + * @param {Number} + */ + timeoutID: any; + + /** + * Create an AuthService instance + * + * @param {Authentication} authentication The Authentication instance to be used + * @param {Config} config The Config instance to be used + */ + constructor(authentication?: any, config?: any); + + /** + * Getter: The configured client for all aurelia-authentication requests + * + * @return {HttpClient} + */ + client: any; + auth: any; + + /** + * Sets the login timeout + * + * @param {Number} ttl Timeout time in ms + */ + setTimeout(ttl?: any): any; + + /** + * Clears the login timeout + */ + clearTimeout(): any; + + /** + * Stores and analyses the servers responseObject. Sets login status and timeout + * + * @param {Object} response The servers response as GOJO + */ + setResponseObject(response?: any): any; + + /** + * Get current user profile from server + * + * @param {[{}|number|string]} [criteriaOrId object or a Number|String converted to {id: criteriaOrId}] + * + * @return {Promise} + */ + getMe(criteriaOrId?: any): any; + + /** + * Send current user profile update to server + + * @param {any} Request body with data. + * @param {[{}|Number|String]} [criteriaOrId object or a Number|String converted to {id: criteriaOrId}] + * + * @return {Promise} + */ + updateMe(body?: any, criteriaOrId?: any): any; + + /** + * Get accessToken from storage + * + * @returns {String} Current accessToken + */ + getAccessToken(): any; + getCurrentToken(): any; + + /** + * Get refreshToken from storage + * + * @returns {String} Current refreshToken + */ + getRefreshToken(): any; + + /** + * Gets authentication status + * + * @returns {Boolean} For Non-JWT and unexpired JWT: true, else: false + */ + isAuthenticated(): any; + + /** + * Gets exp in milliseconds + * + * @returns {Number} Exp for JWT tokens, NaN for all other tokens + */ + getExp(): any; + + /** + * Gets ttl in seconds + * + * @returns {Number} Ttl for JWT tokens, NaN for all other tokens + */ + getTtl(): any; + + /** + * Gets exp from token payload and compares to current time + * + * @returns {Boolean} Returns (ttl > 0)? for JWT, undefined other tokens + */ + isTokenExpired(): any; + + /** + * Get payload from tokens + * + * @returns {Object} Payload for JWT, else null + */ + getTokenPayload(): any; + + /** + * Request new accesss token + * + * @returns {Promise} Requests new token. can be called multiple times + */ + updateToken(): any; + + /** + * Signup locally. Login and redirect depending on config + * + * @param {String|{}} displayNameOrCredentials displayName | object with signup data. + * @param {[String]|{}} emailOrOptions [email | options for post request] + * @param {[String]} passwordOrRedirectUri [password | optional redirectUri overwrite] + * @param {[{}]} options [options] + * @param {[String]} redirectUri [optional redirectUri overwrite] + * + * @return {Promise|Promise} Server response as Object + */ + signup(displayNameOrCredentials?: any, emailOrOptions?: any, passwordOrRedirectUri?: any, options?: any, redirectUri?: any): any; + + /** + * login locally. Redirect depending on config + * + * @param {[String]|{}} emailOrCredentials email | object with signup data. + * @param {[String]} [passwordOrOptions] [password | options for post request] + * @param {[{}]} [optionsOrRedirectUri] [options | redirectUri overwrite]] + * @param {[String]} [redirectUri] [optional redirectUri overwrite] + * + * @return {Promise|Promise} Server response as Object + */ + login(emailOrCredentials?: any, passwordOrOptions?: any, optionsOrRedirectUri?: any, redirectUri?: any): any; + + /** + * logout locally and redirect to redirectUri (if set) or redirectUri of config. Sends logout request first, if set in config + * + * @param {[String]} [redirectUri] [optional redirectUri overwrite] + * + * @return {Promise<>|Promise|Promise} Server response as Object + */ + logout(redirectUri?: any): any; + + /** + * Authenticate with third-party and redirect to redirectUri (if set) or redirectUri of config + * + * @param {String} name Name of the provider + * @param {[String]} [redirectUri] [optional redirectUri overwrite] + * @param {[{}]} [userData] [optional userData for the local authentication server] + * + * @return {Promise|Promise} Server response as Object + */ + authenticate(name?: any, redirectUri?: any, userData?: any): any; + + /** + * Unlink third-party + * + * @param {String} name Name of the provider + * + * @return {Promise|Promise} Server response as Object + */ + unlink(name?: any, redirectUri?: any): any; +} +export declare class BaseConfig { + + /** + * Prepends baseUrl to a given url + * @param {String} url The relative url to append + * @return {String} joined baseUrl and url + */ + joinBase(url?: any): any; + + /** + * Merge current settings with incomming settings + * @param {Object} incomming Settings object to be merged into the current configuration + * @return {Config} this + */ + configure(incomming?: any): any; + + /* ----------- default config ----------- */ + // Used internally. The used Rest instance; set during configuration (see index.js) + client: any; + + // If using aurelia-api: + // ===================== + // This is the name of the endpoint used for any requests made in relation to authentication (login, logout, etc.). An empty string selects the default endpoint of aurelia-api. + endpoint: any; + + // When authenticated, these endpoints will have the token added to the header of any requests (for authorization). Accepts an array of endpoint names. An empty string selects the default endpoint of aurelia-api. + configureEndpoints: any; + + // SPA related options + // =================== + // The SPA url to which the user is redirected after a successful login + loginRedirect: any; + + // The SPA url to which the user is redirected after a successful logout + logoutRedirect: any; + + // The SPA route used when an unauthenticated user tries to access an SPA page that requires authentication + loginRoute: any; + + // Whether or not an authentication token is provided in the response to a successful signup + loginOnSignup: any; + + // If loginOnSignup == false: The SPA url to which the user is redirected after a successful signup (else loginRedirect is used) + signupRedirect: any; + + // redirect when token expires. 0 = don't redirect (default), 1 = use logoutRedirect, string = redirect there + expiredRedirect: any; + + // API related options + // =================== + // The base url used for all authentication related requests, including provider.url below. + // This appends to the httpClient/endpoint base url, it does not override it. + baseUrl: any; + + // The API endpoint to which login requests are sent + loginUrl: any; + + // The API endpoint to which logout requests are sent (not needed for jwt) + logoutUrl: any; + + // The HTTP method used for 'unlink' requests (Options: 'get' or 'post') + logoutMethod: any; + + // The API endpoint to which signup requests are sent + signupUrl: any; + + // The API endpoint used in profile requests (inc. `find/get` and `update`) + profileUrl: any; + + // The method used to update the profile ('put' or 'patch') + profileMethod: any; + + // The API endpoint used with oAuth to unlink authentication + unlinkUrl: any; + + // The HTTP method used for 'unlink' requests (Options: 'get' or 'post') + unlinkMethod: any; + + // The API endpoint to which refreshToken requests are sent. null = loginUrl + refreshTokenUrl: any; + + // Token Options + // ============= + // The header property used to contain the authToken in the header of API requests that require authentication + authHeader: any; + + // The token name used in the header of API requests that require authentication + authTokenType: any; + + // The the property from which to get the access token after a successful login or signup. Can also be dotted eg "accessTokenProp.accessTokenName" + accessTokenProp: any; + + // If the property defined by `accessTokenProp` is an object: + // ------------------------------------------------------------ + //This is the property from which to get the token `{ "accessTokenProp": { "accessTokenName" : '...' } }` + accessTokenName: any; + + // This allows the token to be a further object deeper `{ "accessTokenProp": { "accessTokenRoot" : { "accessTokenName" : '...' } } }` + accessTokenRoot: any; + + // Refresh Token Options + // ===================== + // Option to turn refresh tokens On/Off + useRefreshToken: any; + + // The option to enable/disable the automatic refresh of Auth tokens using Refresh Tokens + autoUpdateToken: any; + + // Oauth Client Id + clientId: any; + + // The the property from which to get the refresh token after a successful token refresh. Can also be dotted eg "refreshTokenProp.refreshTokenProp" + refreshTokenProp: any; + + // If the property defined by `refreshTokenProp` is an object: + // ----------------------------------------------------------- + // This is the property from which to get the token `{ "refreshTokenProp": { "refreshTokenName" : '...' } }` + refreshTokenName: any; + + // This allows the refresh token to be a further object deeper `{ "refreshTokenProp": { "refreshTokenRoot" : { "refreshTokenName" : '...' } } }` + refreshTokenRoot: any; + + // Miscellaneous Options + // ===================== + // Whether to enable the fetch interceptor which automatically adds the authentication headers + // (or not... e.g. if using a session based API or you want to override the default behaviour) + httpInterceptor: any; + + // For OAuth only: Tell the API whether or not to include token cookies in the response (for session based APIs) + withCredentials: any; + + // Controls how the popup is shown for different devices (Options: 'browser' or 'mobile') + platform: any; + + // Determines the `PLATFORM` property name upon which aurelia-authentication data is stored (Default: `PLATFORM.localStorage`) + storage: any; + + // The key used for storing the authentication response locally + storageKey: any; + + // List of value-converters to make global + globalValueConverters: any; + + //OAuth provider specific related configuration + // ============================================ + providers: any; + authToken: any; + responseTokenProp: any; + tokenRoot: any; + tokenName: any; + tokenPrefix: any; + current: any; +} +export declare class FetchConfig { + + /** + * Construct the FetchConfig + * + * @param {HttpClient} httpClient + * @param {Config} clientConfig + * @param {Authentication} authService + * @param {BaseConfig} config + */ + constructor(httpClient?: any, clientConfig?: any, authService?: any, config?: any); + + /** + * Interceptor for HttpClient + * + * @return {{request: Function, response: Function}} + */ + interceptor: any; + + /** + * Configure client(s) with authorization interceptor + * + * @param {HttpClient|Rest|string[]} client HttpClient, rest client or api endpoint name, or an array thereof + * + * @return {HttpClient[]} + */ + configure(client?: any): any; +} +export declare class OAuth1 { + constructor(storage?: any, popup?: any, config?: any); + open(options?: any, userData?: any): any; + exchangeForToken(oauthData?: any, userData?: any, provider?: any): any; +} +export declare class OAuth2 { + constructor(storage?: any, popup?: any, config?: any); + open(options?: any, userData?: any): any; + exchangeForToken(oauthData?: any, userData?: any, provider?: any): any; + buildQuery(provider?: any): any; +} +export declare class Popup { + constructor(); + open(url?: any, windowName?: any, options?: any): any; + eventListener(redirectUri?: any): any; + pollPopup(): any; +} +export declare class Storage { + constructor(config?: any); + get(key?: any): any; + set(key?: any, value?: any): any; + remove(key?: any): any; +} +export declare class AuthenticatedFilterValueConverter { + constructor(authService?: any); + + /** + * route toView predictator on route.config.auth === (parameter || authService.isAuthenticated()) + * @param {RouteConfig} routes the routes array to convert + * @param {[Boolean]} [isAuthenticated] optional isAuthenticated value. default: this.authService.authenticated + * @return {Boolean} show/hide element + */ + toView(routes?: any, isAuthenticated?: any): any; +} +export declare class AuthenticatedValueConverter { + constructor(authService?: any); + + /** + * element toView predictator on authService.isAuthenticated() + * @return {Boolean} show/hide element + */ + toView(): any; +} +export declare class AuthFilterValueConverter { + + /** + * route toView predictator on route.config.auth === isAuthenticated + * @param {RouteConfig} routes the routes array to convert + * @param {Boolean} isAuthenticated authentication status + * @return {Boolean} show/hide element + */ + toView(routes?: any, isAuthenticated?: any): any; +} +export * from 'aurelia-authentication/aurelia-authentication'; \ No newline at end of file diff --git a/dist/aurelia-authentication.js b/dist/aurelia-authentication.js index f61c8ee..8e70e88 100644 --- a/dist/aurelia-authentication.js +++ b/dist/aurelia-authentication.js @@ -1,1586 +1,1578 @@ -import extend from 'extend'; import * as LogManager from 'aurelia-logging'; +import extend from 'extend'; import jwtDecode from 'jwt-decode'; import {PLATFORM,DOM} from 'aurelia-pal'; -import {parseQueryString,join,buildQueryString} from 'aurelia-path'; -import {inject} from 'aurelia-dependency-injection'; -import {deprecated} from 'aurelia-metadata'; -import {Redirect} from 'aurelia-router'; import {HttpClient} from 'aurelia-fetch-client'; import {Config,Rest} from 'aurelia-api'; +import {inject} from 'aurelia-dependency-injection'; +import {Redirect} from 'aurelia-router'; +import {deprecated} from 'aurelia-metadata'; +import {join,buildQueryString,parseQueryString} from 'aurelia-path'; -export class Popup { - constructor() { - this.popupWindow = null; - this.polling = null; - this.url = ''; - } - - open(url, windowName, options) { - this.url = url; - const optionsString = buildPopupWindowOptions(options || {}); +// import to ensure value-converters get bundled +import './authFilterValueConverter'; - this.popupWindow = PLATFORM.global.open(url, windowName, optionsString); +/** + * Configure the plugin. + * + * @param {{globalResources: Function, container: {Container}}} aurelia + * @param {{}|Function} config + */ +export function configure(aurelia, config) { + // ie9 polyfill + if (!PLATFORM.location.origin) { + PLATFORM.location.origin = PLATFORM.location.protocol + '//' + PLATFORM.location.hostname + (PLATFORM.location.port ? ':' + PLATFORM.location.port : ''); + } - if (this.popupWindow && this.popupWindow.focus) { - this.popupWindow.focus(); - } + const baseConfig = aurelia.container.get(BaseConfig); - return this; + if (typeof config === 'function') { + config(baseConfig); + } else if (typeof config === 'object') { + baseConfig.configure(config); } - eventListener(redirectUri) { - return new Promise((resolve, reject) => { - this.popupWindow.addEventListener('loadstart', event => { - if (event.url.indexOf(redirectUri) !== 0) { - return; - } + // after baseConfig was configured + for (let converter of baseConfig.globalValueConverters) { + aurelia.globalResources(`./${converter}`); + LogManager.getLogger('authentication').info(`Add globalResources value-converter: ${converter}`); + } + const fetchConfig = aurelia.container.get(FetchConfig); + const clientConfig = aurelia.container.get(Config); - const parser = DOM.createElement('a'); - parser.href = event.url; + // Array? Configure the provided endpoints. + if (Array.isArray(baseConfig.configureEndpoints)) { + baseConfig.configureEndpoints.forEach(endpointToPatch => { + fetchConfig.configure(endpointToPatch); + }); + } - if (parser.search || parser.hash) { - const qs = parseUrl(parser); + let client; - if (qs.error) { - reject({error: qs.error}); - } else { - resolve(qs); - } + // Let's see if there's a configured named or default endpoint or a HttpClient. + if (baseConfig.endpoint !== null) { + if (typeof baseConfig.endpoint === 'string') { + const endpoint = clientConfig.getEndpoint(baseConfig.endpoint); + if (!endpoint) { + throw new Error(`There is no '${baseConfig.endpoint || 'default'}' endpoint registered.`); + } + client = endpoint; + } else if (baseConfig.endpoint instanceof HttpClient) { + client = new Rest(baseConfig.endpoint); + } + } - this.popupWindow.close(); - } - }); + // No? Fine. Default to HttpClient. BC all the way. + if (!(client instanceof Rest)) { + client = new Rest(aurelia.container.get(HttpClient)); + } - this.popupWindow.addEventListener('exit', () => { - reject({data: 'Provider Popup was closed'}); - }); + // Set the client on the config, for use throughout the plugin. + baseConfig.client = client; +} - this.popupWindow.addEventListener('loaderror', () => { - reject({data: 'Authorization Failed'}); - }); - }); +@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' + }; } - pollPopup() { - return new Promise((resolve, reject) => { - this.polling = PLATFORM.global.setInterval(() => { - let errorData; + 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'; - try { - if (this.popupWindow.location.host === PLATFORM.global.document.location.host - && (this.popupWindow.location.search || this.popupWindow.location.hash)) { - const qs = parseUrl(this.popupWindow.location); + if (typeof provider.state === 'function') { + this.storage.set(stateName, provider.state()); + } else if (typeof provider.state === 'string') { + this.storage.set(stateName, provider.state); + } - if (qs.error) { - reject({error: qs.error}); - } else { - resolve(qs); - } + this.lock = this.lock || new PLATFORM.global.Auth0Lock(provider.clientId, provider.clientDomain); - this.popupWindow.close(); - PLATFORM.global.clearInterval(this.polling); - } - } catch (error) { - errorData = error; - } + const openPopup = new Promise((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'); - if (!this.popupWindow) { - PLATFORM.global.clearInterval(this.polling); - reject({ - error: errorData, - data: 'Provider Popup Blocked' - }); - } else if (this.popupWindow.closed) { - PLATFORM.global.clearInterval(this.polling); - reject({ - error: errorData, - data: 'Problem poll popup' + 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 }); } - }, 35); + }); }); + + 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'); + }); } } -const buildPopupWindowOptions = options => { - const width = options.width || 500; - const height = options.height || 500; +@inject(AuthService) +export class AuthenticateStep { + constructor(authService) { + this.authService = authService; + } - const extended = extend({ - width: width, - height: height, - left: PLATFORM.global.screenX + ((PLATFORM.global.outerWidth - width) / 2), - top: PLATFORM.global.screenY + ((PLATFORM.global.outerHeight - height) / 2.5) - }, options); + run(routingContext, next) { + const isLoggedIn = this.authService.authenticated; + const loginRoute = this.authService.config.loginRoute; - let parts = []; - Object.keys(extended).map(key => parts.push(key + '=' + extended[key])); + if (routingContext.getAllInstructions().some(route => route.config.auth === true)) { + if (!isLoggedIn) { + return next.cancel(new Redirect(loginRoute)); + } + } else if (isLoggedIn && routingContext.getAllInstructions().some(route => route.fragment === loginRoute)) { + return next.cancel(new Redirect( this.authService.config.loginRedirect )); + } - return parts.join(','); -}; + return next(); + } +} -const parseUrl = url => { - return extend(true, {}, parseQueryString(url.search), parseQueryString(url.hash)); -}; +@inject(Storage, BaseConfig, OAuth1, OAuth2, Auth0Lock) +export class Authentication { + 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; + this.payload = null; + this.exp = null; + this.hasDataStored = false; + } -export class BaseConfig { - /** - * Prepends baseUrl to a given url - * @param {String} url The relative url to append - * @return {String} joined baseUrl and url - */ - joinBase(url) { - return join(this.baseUrl, url); + + /* deprecated methods */ + + @deprecated({message: 'Use baseConfig.loginRoute instead.'}) + getLoginRoute() { + return this.config.loginRoute; } - /** - * Merge current settings with incomming settings - * @param {Object} incomming Settings object to be merged into the current configuration - * @return {Config} this - */ - configure(incomming) { - for (let key in incomming) { - const value = incomming[key]; - if (value !== undefined) { - if (Array.isArray(value) || typeof value !== 'object' || value === null) { - this[key] = value; - } else { - extend(true, this[key], value); - } - } + @deprecated({message: 'Use baseConfig.loginRedirect instead.'}) + getLoginRedirect() { + return this.config.loginRedirect; + } + + @deprecated({message: 'Use baseConfig.joinBase(baseConfig.loginUrl) instead.'}) + getLoginUrl() { + return this.Config.joinBase(this.config.loginUrl); + } + + @deprecated({message: 'Use baseConfig.joinBase(baseConfig.signupUrl) instead.'}) + getSignupUrl() { + return this.Config.joinBase(this.config.signupUrl); + } + + @deprecated({message: 'Use baseConfig.joinBase(baseConfig.profileUrl) instead.'}) + getProfileUrl() { + return this.Config.joinBase(this.config.profileUrl); + } + + @deprecated({message: 'Use .getAccessToken() instead.'}) + getToken() { + return this.getAccessToken(); + } + + get responseObject() { + LogManager.getLogger('authentication').warn('Getter Authentication.responseObject is deprecated. Use Authentication.getResponseObject() instead.'); + return this.getResponseObject(); + } + + set responseObject(response) { + LogManager.getLogger('authentication').warn('Setter Authentication.responseObject is deprecated. Use AuthServive.setResponseObject(response) instead.'); + this.setResponseObject(response); + } + + /* get/set responseObject */ + + getResponseObject() { + return JSON.parse(this.storage.get(this.config.storageKey)); + } + + setResponseObject(response) { + if (response) { + this.getDataFromResponse(response); + this.storage.set(this.config.storageKey, JSON.stringify(response)); + return; } + this.accessToken = null; + this.refreshToken = null; + this.payload = null; + this.exp = null; + + this.hasDataStored = false; + + this.storage.remove(this.config.storageKey); } - /* ----------- default config ----------- */ - // Used internally. The used Rest instance; set during configuration (see index.js) - client = null; + /* get data, update if needed first */ - // If using aurelia-api: - // ===================== + getAccessToken() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.accessToken; + } - // This is the name of the endpoint used for any requests made in relation to authentication (login, logout, etc.). An empty string selects the default endpoint of aurelia-api. - endpoint = null; - // When authenticated, these endpoints will have the token added to the header of any requests (for authorization). Accepts an array of endpoint names. An empty string selects the default endpoint of aurelia-api. - configureEndpoints = null; + getRefreshToken() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.refreshToken; + } + getPayload() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.payload; + } - // SPA related options - // =================== + getExp() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.exp; + } - // The SPA url to which the user is redirected after a successful login - loginRedirect = '#/'; - // The SPA url to which the user is redirected after a successful logout - logoutRedirect = '#/'; - // The SPA route used when an unauthenticated user tries to access an SPA page that requires authentication - loginRoute = '/login'; - // Whether or not an authentication token is provided in the response to a successful signup - loginOnSignup = true; - // If loginOnSignup == false: The SPA url to which the user is redirected after a successful signup (else loginRedirect is used) - signupRedirect = '#/login'; - // redirect when token expires. 0 = don't redirect (default), 1 = use logoutRedirect, string = redirect there - expiredRedirect = 0; + /* get status from data */ - // API related options - // =================== + getTtl() { + const exp = this.getExp(); + return Number.isNaN(exp) ? NaN : exp - Math.round(new Date().getTime() / 1000); + } - // The base url used for all authentication related requests, including provider.url below. - // This appends to the httpClient/endpoint base url, it does not override it. - baseUrl = ''; - // The API endpoint to which login requests are sent - loginUrl = '/auth/login'; - // The API endpoint to which logout requests are sent (not needed for jwt) - logoutUrl = null; - // The HTTP method used for 'unlink' requests (Options: 'get' or 'post') - logoutMethod = 'get'; - // The API endpoint to which signup requests are sent - signupUrl = '/auth/signup'; - // The API endpoint used in profile requests (inc. `find/get` and `update`) - profileUrl = '/auth/me'; - // The method used to update the profile ('put' or 'patch') - profileMethod = 'put'; - // The API endpoint used with oAuth to unlink authentication - unlinkUrl = '/auth/unlink/'; - // The HTTP method used for 'unlink' requests (Options: 'get' or 'post') - unlinkMethod = 'get'; - // The API endpoint to which refreshToken requests are sent. null = loginUrl - refreshTokenUrl = null; + isTokenExpired() { + const timeLeft = this.getTtl(); + return Number.isNaN(timeLeft) ? undefined : timeLeft < 0; + } + isAuthenticated() { + const isTokenExpired = this.isTokenExpired(); + if (isTokenExpired === undefined ) return this.accessToken ? true : false; + return !isTokenExpired; + } - // Token Options - // ============= - // The header property used to contain the authToken in the header of API requests that require authentication - authHeader = 'Authorization'; - // The token name used in the header of API requests that require authentication - authTokenType = 'Bearer'; - // The the property from which to get the access token after a successful login or signup. Can also be dotted eg "accessTokenProp.accessTokenName" - accessTokenProp = 'access_token'; + /* get and set from response */ + getDataFromResponse(response) { + const config = this.config; - // If the property defined by `accessTokenProp` is an object: - // ------------------------------------------------------------ + this.accessToken = this.getTokenFromResponse(response, config.accessTokenProp, config.accessTokenName, config.accessTokenRoot); - //This is the property from which to get the token `{ "accessTokenProp": { "accessTokenName" : '...' } }` - accessTokenName = 'token'; - // This allows the token to be a further object deeper `{ "accessTokenProp": { "accessTokenRoot" : { "accessTokenName" : '...' } } }` - accessTokenRoot = false; + this.refreshToken = null; + if (config.useRefreshToken) { + try { + this.refreshToken = this.getTokenFromResponse(response, config.refreshTokenProp, config.refreshTokenName, config.refreshTokenRoot); + } catch (e) { + this.refreshToken = null; + } + } + this.payload = null; - // Refresh Token Options - // ===================== + try { + this.payload = this.accessToken ? jwtDecode(this.accessToken) : null; + } catch (_) {_;} - // Option to turn refresh tokens On/Off - useRefreshToken = false; - // The option to enable/disable the automatic refresh of Auth tokens using Refresh Tokens - autoUpdateToken = true; - // Oauth Client Id - clientId = false; - // The the property from which to get the refresh token after a successful token refresh. Can also be dotted eg "refreshTokenProp.refreshTokenProp" - refreshTokenProp = 'refresh_token'; + this.exp = this.payload ? parseInt(this.payload.exp, 10) : NaN; - // If the property defined by `refreshTokenProp` is an object: - // ----------------------------------------------------------- + this.hasDataStored = true; - // This is the property from which to get the token `{ "refreshTokenProp": { "refreshTokenName" : '...' } }` - refreshTokenName = 'token'; - // This allows the refresh token to be a further object deeper `{ "refreshTokenProp": { "refreshTokenRoot" : { "refreshTokenName" : '...' } } }` - refreshTokenRoot = false; + return { + accessToken: this.accessToken, + refreshToken: this.refreshToken, + payload: this.payload, + exp: this.exp + }; + } + getTokenFromResponse(response, tokenProp, tokenName, tokenRoot) { + if (!response) return undefined; - // Miscellaneous Options - // ===================== + const responseTokenProp = tokenProp.split('.').reduce((o, x) => o[x], response); - // Whether to enable the fetch interceptor which automatically adds the authentication headers - // (or not... e.g. if using a session based API or you want to override the default behaviour) - httpInterceptor = true; - // For OAuth only: Tell the API whether or not to include token cookies in the response (for session based APIs) - withCredentials = true; - // Controls how the popup is shown for different devices (Options: 'browser' or 'mobile') - platform = 'browser'; - // Determines the `PLATFORM` property name upon which aurelia-authentication data is stored (Default: `PLATFORM.localStorage`) - storage = 'localStorage'; - // The key used for storing the authentication response locally - storageKey = 'aurelia_authentication'; + if (typeof responseTokenProp === 'string') { + return responseTokenProp; + } - // List of value-converters to make global - globalValueConverters = ['authFilterValueConverter']; + if (typeof responseTokenProp === 'object') { + const tokenRootData = tokenRoot && tokenRoot.split('.').reduce((o, x) => o[x], responseTokenProp); + const token = tokenRootData ? tokenRootData[tokenName] : responseTokenProp[tokenName]; -//OAuth provider specific related configuration - // ============================================ - providers = { - facebook: { - name: 'facebook', - url: '/auth/facebook', - authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', - redirectUri: PLATFORM.location.origin + '/', - requiredUrlParams: ['display', 'scope'], - scope: ['email'], - scopeDelimiter: ',', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 580, height: 400 } - }, - google: { - name: 'google', - url: '/auth/google', - authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth', - redirectUri: PLATFORM.location.origin, - requiredUrlParams: ['scope'], - optionalUrlParams: ['display', 'state'], - scope: ['profile', 'email'], - scopePrefix: 'openid', - scopeDelimiter: ' ', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 452, height: 633 }, - state: randomState - }, - github: { - name: 'github', - url: '/auth/github', - authorizationEndpoint: 'https://github.com/login/oauth/authorize', - redirectUri: PLATFORM.location.origin, - optionalUrlParams: ['scope'], - scope: ['user:email'], - scopeDelimiter: ' ', - oauthType: '2.0', - popupOptions: { width: 1020, height: 618 } - }, - instagram: { - name: 'instagram', - url: '/auth/instagram', - authorizationEndpoint: 'https://api.instagram.com/oauth/authorize', - redirectUri: PLATFORM.location.origin, - requiredUrlParams: ['scope'], - scope: ['basic'], - scopeDelimiter: '+', - oauthType: '2.0' - }, - linkedin: { - name: 'linkedin', - url: '/auth/linkedin', - authorizationEndpoint: 'https://www.linkedin.com/uas/oauth2/authorization', - redirectUri: PLATFORM.location.origin, - requiredUrlParams: ['state'], - scope: ['r_emailaddress'], - scopeDelimiter: ' ', - state: 'STATE', - oauthType: '2.0', - popupOptions: { width: 527, height: 582 } - }, - twitter: { - name: 'twitter', - url: '/auth/twitter', - authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', - redirectUri: PLATFORM.location.origin, - oauthType: '1.0', - popupOptions: { width: 495, height: 645 } - }, - twitch: { - name: 'twitch', - url: '/auth/twitch', - authorizationEndpoint: 'https://api.twitch.tv/kraken/oauth2/authorize', - redirectUri: PLATFORM.location.origin, - requiredUrlParams: ['scope'], - scope: ['user_read'], - scopeDelimiter: ' ', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 500, height: 560 } - }, - live: { - name: 'live', - url: '/auth/live', - authorizationEndpoint: 'https://login.live.com/oauth20_authorize.srf', - redirectUri: PLATFORM.location.origin, - requiredUrlParams: ['display', 'scope'], - scope: ['wl.emails'], - scopeDelimiter: ' ', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 500, height: 560 } - }, - yahoo: { - name: 'yahoo', - url: '/auth/yahoo', - authorizationEndpoint: 'https://api.login.yahoo.com/oauth2/request_auth', - redirectUri: PLATFORM.location.origin, - scope: [], - scopeDelimiter: ',', - oauthType: '2.0', - popupOptions: { width: 559, height: 519 } - }, - bitbucket: { - name: 'bitbucket', - url: '/auth/bitbucket', - authorizationEndpoint: 'https://bitbucket.org/site/oauth2/authorize', - redirectUri: PLATFORM.location.origin + '/', - requiredUrlParams: ['scope'], - scope: ['email'], - 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: randomState + if (!token) throw new Error('Token not found in response'); + + return token; } - }; - /* deprecated defaults */ - _authToken = 'Bearer'; - _responseTokenProp = 'access_token'; - _tokenName = 'token'; - _tokenRoot = false; - _tokenPrefix = 'aurelia'; + const token = response[tokenName] === undefined ? null : response[tokenName]; - /* deprecated methods and parameteres */ - set authToken(authToken) { - LogManager.getLogger('authentication').warn('BaseConfig.authToken is deprecated. Use BaseConfig.authTokenType instead.'); - this._authTokenType = authToken; - this.authTokenType = authToken; - return authToken; - } - get authToken() { - return this._authTokenType; - } + if (!token) throw new Error('Token not found in response'); - set responseTokenProp(responseTokenProp) { - LogManager.getLogger('authentication').warn('BaseConfig.responseTokenProp is deprecated. Use BaseConfig.accessTokenProp instead.'); - this._responseTokenProp = responseTokenProp; - this.accessTokenProp = responseTokenProp; - return responseTokenProp; - } - get responseTokenProp() { - return this._responseTokenProp; + return token; } - set tokenRoot(tokenRoot) { - LogManager.getLogger('authentication').warn('BaseConfig.tokenRoot is deprecated. Use BaseConfig.accessTokenRoot instead.'); - this._tokenRoot = tokenRoot; - this.accessTokenRoot = tokenRoot; - return tokenRoot; - } - get tokenRoot() { - return this._tokenRoot; - } - set tokenName(tokenName) { - LogManager.getLogger('authentication').warn('BaseConfig.tokenName is deprecated. Use BaseConfig.accessTokenName instead.'); - this._tokenName = tokenName; - this.accessTokenName = tokenName; - return tokenName; - } - get tokenName() { - return this._tokenName; + toUpdateTokenCallstack() { + return new Promise(resolve => this.updateTokenCallstack.push(resolve)); } - set tokenPrefix(tokenPrefix) { - LogManager.getLogger('authentication').warn('BaseConfig.tokenPrefix is obsolete. Use BaseConfig.storageKey instead.'); - this._tokenPrefix = tokenPrefix; - return tokenPrefix; - } - get tokenPrefix() { - return this._tokenPrefix || 'aurelia'; + resolveUpdateTokenCallstack(response) { + this.updateTokenCallstack.map(resolve => resolve(response)); + this.updateTokenCallstack = []; } - get current() { - LogManager.getLogger('authentication').warn('Getter BaseConfig.current is deprecated. Use BaseConfig directly instead.'); - return this; - } - set current(_) { - throw new Error('Setter BaseConfig.current is obsolete. Use BaseConfig directly instead.'); - } - get _current() { - LogManager.getLogger('authentication').warn('Getter BaseConfig._current is deprecated. Use BaseConfig directly instead.'); - return this; + /** + * Authenticate with third-party + * + * @param {String} name of the provider + * @param {[{}]} [userData] + * + * @return {Promise} + */ + authenticate(name, userData = {}) { + let oauthType = this.config.providers[name].type; + + if (oauthType) { + LogManager.getLogger('authentication').warn('DEPRECATED: Setting provider.type is deprecated and replaced by provider.oauthType'); + } else { + oauthType = this.config.providers[name].oauthType; + } + + 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); } - set _current(_) { - throw new Error('Setter BaseConfig._current is obsolete. Use BaseConfig directly instead.'); + + redirect(redirectUrl, defaultRedirectUrl) { + // stupid rule to keep it BC + if (redirectUrl === true) { + LogManager.getLogger('authentication').warn('DEPRECATED: Setting redirectUrl === true to actually *not redirect* is deprecated. Set redirectUrl === 0 instead.'); + return; + } + // stupid rule to keep it BC + if (redirectUrl === false) { + LogManager.getLogger('authentication').warn('BREAKING CHANGE: Setting redirectUrl === false to actually *do redirect* is deprecated. Set redirectUrl to undefined or null to use the defaultRedirectUrl if so desired.'); + } + // BC hack. explicit 0 means don't redirect. false will be added later and 0 deprecated + if (redirectUrl === 0) { + return; + } + if (typeof redirectUrl === 'string') { + PLATFORM.location.href = encodeURI(redirectUrl); + } else if (defaultRedirectUrl) { + PLATFORM.location.href = defaultRedirectUrl; + } } } -function randomState() { - let rand = Math.random().toString(36).substr(2); - return encodeURIComponent(rand); -} +@inject(AuthService) +export class AuthorizeStep { + constructor(authService) { + LogManager.getLogger('authentication').warn('AuthorizeStep is deprecated. Use AuthenticationStep instead.'); -@inject(BaseConfig) -export class Storage { - constructor(config) { - this.config = config; + this.authService = authService; } - get(key) { - return PLATFORM.global[this.config.storage].getItem(key); - } + run(routingContext, next) { + const isLoggedIn = this.authService.isAuthenticated(); + const loginRoute = this.authService.config.loginRoute; - set(key, value) { - PLATFORM.global[this.config.storage].setItem(key, value); - } + if (routingContext.getAllInstructions().some(route => route.config.auth)) { + if (!isLoggedIn) { + return next.cancel(new Redirect(loginRoute)); + } + } else if (isLoggedIn && routingContext.getAllInstructions().some(route => route.fragment === loginRoute)) { + return next.cancel(new Redirect( this.authService.config.loginRedirect )); + } - remove(key) { - PLATFORM.global[this.config.storage].removeItem(key); + return next(); } } -@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' - }; - } +@inject(Authentication, BaseConfig) +export class AuthService { + /** + * The Authentication instance that handles the token + * + * @param {Authentication} + */ + authentication; - 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'; + /** + * The Config instance that contains the current configuration setting + * + * @param {Config} + */ + config; - if (typeof provider.state === 'function') { - this.storage.set(stateName, provider.state()); - } else if (typeof provider.state === 'string') { - this.storage.set(stateName, provider.state); - } + /** + * The current login status + * + * @param {Boolean} + */ + authenticated = false; - this.lock = this.lock || new PLATFORM.global.Auth0Lock(provider.clientId, provider.clientDomain); + /** + * The currently set timeoutID + * + * @param {Number} + */ + timeoutID = 0; - const openPopup = new Promise((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'); + /** + * Create an AuthService instance + * + * @param {Authentication} authentication The Authentication instance to be used + * @param {Config} config The Config instance to be used + */ + constructor(authentication, config) { + this.authentication = authentication; + this.config = config; - 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 - }); - } - }); - }); + // get token stored in previous format over + const oldStorageKey = config.tokenPrefix + ? config.tokenPrefix + '_' + config.tokenName + : config.tokenName; + const oldToken = authentication.storage.get(oldStorageKey); - 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'); - }); + if (oldToken) { + LogManager.getLogger('authentication').info('Found token with deprecated format in storage. Converting it to new format. No further action required.'); + let fakeOldResponse = {}; + fakeOldResponse[config.accessTokenProp] = oldToken; + this.setResponseObject(fakeOldResponse); + authentication.storage.remove(oldStorageKey); + } + + // initialize status by resetting if existing stored responseObject + this.setResponseObject(this.authentication.getResponseObject()); } -} -@inject(Storage, Popup, BaseConfig) -export class OAuth1 { - constructor(storage, popup, config) { - this.storage = storage; - this.config = config; - this.popup = popup; - this.defaults = { - url: null, - name: null, - popupOptions: null, - redirectUri: null, - authorizationEndpoint: null - }; + /** + * Getter: The configured client for all aurelia-authentication requests + * + * @return {HttpClient} + */ + get client() { + return this.config.client; } - open(options, userData) { - const provider = extend(true, {}, this.defaults, options); - const serverUrl = this.config.joinBase(provider.url); + get auth() { + LogManager.getLogger('authentication').warn('AuthService.auth is deprecated. Use .authentication instead.'); + return this.authentication; + } - if (this.config.platform !== 'mobile') { - this.popup = this.popup.open('', provider.name, provider.popupOptions); + /** + * Sets the login timeout + * + * @param {Number} ttl Timeout time in ms + */ + setTimeout(ttl) { + this.clearTimeout(); + + this.timeoutID = PLATFORM.global.setTimeout(() => { + if (this.config.autoUpdateToken + && this.authentication.getAccessToken() + && this.authentication.getRefreshToken()) { + this.updateToken(); + } else { + this.logout(this.config.expiredRedirect); + } + }, ttl); + } + + /** + * Clears the login timeout + */ + clearTimeout() { + if (this.timeoutID) { + PLATFORM.global.clearTimeout(this.timeoutID); } + this.timeoutID = 0; + } - return this.config.client.post(serverUrl) - .then(response => { - const url = provider.authorizationEndpoint + '?' + buildQueryString(response); + /** + * Stores and analyses the servers responseObject. Sets login status and timeout + * + * @param {Object} response The servers response as GOJO + */ + setResponseObject(response) { + this.clearTimeout(); - if (this.config.platform === 'mobile') { - this.popup = this.popup.open(url, provider.name, provider.popupOptions); - } else { - this.popup.popupWindow.location = url; - } + this.authentication.setResponseObject(response); - const popupListener = this.config.platform === 'mobile' - ? this.popup.eventListener(provider.redirectUri) - : this.popup.pollPopup(); + this.authenticated = this.authentication.isAuthenticated(); + if (this.authenticated && !Number.isNaN(this.authentication.exp)) { + this.setTimeout(this.getTtl() * 1000); + } + } - return popupListener.then(result => this.exchangeForToken(result, userData, provider)); - }); + /** + * Get current user profile from server + * + * @param {[{}|number|string]} [criteriaOrId object or a Number|String converted to {id: criteriaOrId}] + * + * @return {Promise} + */ + getMe(criteriaOrId) { + if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { + criteriaOrId = {id: criteriaOrId}; + } + return this.client.find(this.config.joinBase(this.config.profileUrl), criteriaOrId); } - exchangeForToken(oauthData, userData, provider) { - const data = extend(true, {}, userData, oauthData); - const serverUrl = this.config.joinBase(provider.url); - const credentials = this.config.withCredentials ? 'include' : 'same-origin'; + /** + * Send current user profile update to server - return this.config.client.post(serverUrl, data, {credentials: credentials}); + * @param {any} Request body with data. + * @param {[{}|Number|String]} [criteriaOrId object or a Number|String converted to {id: criteriaOrId}] + * + * @return {Promise} + */ + updateMe(body, criteriaOrId) { + if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { + criteriaOrId = { id: criteriaOrId }; + } + if (this.config.profileMethod === 'put') { + return this.client.update(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); + } + return this.client.patch(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); } -} -@inject(Storage, Popup, BaseConfig) -export class OAuth2 { - constructor(storage, popup, config) { - this.storage = storage; - this.config = config; - this.popup = popup; - this.defaults = { - url: null, - name: null, - state: null, - scope: null, - scopeDelimiter: null, - redirectUri: null, - popupOptions: null, - authorizationEndpoint: null, - responseParams: null, - requiredUrlParams: null, - optionalUrlParams: null, - defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], - responseType: 'code' - }; + /** + * Get accessToken from storage + * + * @returns {String} Current accessToken + */ + getAccessToken() { + return this.authentication.getAccessToken(); } - open(options, userData) { - const provider = extend(true, {}, this.defaults, options); - const stateName = provider.name + '_state'; + @deprecated({message: 'Use .getAccessToken() instead.'}) + getCurrentToken() { + return this.getAccessToken(); + } - if (typeof provider.state === 'function') { - this.storage.set(stateName, provider.state()); - } else if (typeof provider.state === 'string') { - this.storage.set(stateName, provider.state); + /** + * Get refreshToken from storage + * + * @returns {String} Current refreshToken + */ + getRefreshToken() { + return this.authentication.getRefreshToken(); + } + + /** + * Gets authentication status + * + * @returns {Boolean} For Non-JWT and unexpired JWT: true, else: false + */ + isAuthenticated() { + let authenticated = this.authentication.isAuthenticated(); + + // auto-update token? + if (!authenticated + && this.config.autoUpdateToken + && this.authentication.getAccessToken() + && this.authentication.getRefreshToken()) { + this.updateToken(); + authenticated = true; } - const url = provider.authorizationEndpoint - + '?' + buildQueryString(this.buildQuery(provider)); - const popup = this.popup.open(url, provider.name, provider.popupOptions); - const openPopup = (this.config.platform === 'mobile') - ? popup.eventListener(provider.redirectUri) - : popup.pollPopup(); + return authenticated; + } - return openPopup - .then(oauthData => { - if (provider.responseType === 'token' || - provider.responseType === 'id_token%20token' || - provider.responseType === 'token%20id_token' - ) { - return oauthData; - } - if (oauthData.state && oauthData.state !== this.storage.get(stateName)) { - return Promise.reject('OAuth 2.0 state parameter mismatch.'); - } - return this.exchangeForToken(oauthData, userData, provider); - }); + /** + * Gets exp in milliseconds + * + * @returns {Number} Exp for JWT tokens, NaN for all other tokens + */ + getExp() { + return this.authentication.getExp(); } - exchangeForToken(oauthData, userData, provider) { - const data = extend(true, {}, userData, { - clientId: provider.clientId, - redirectUri: provider.redirectUri - }, oauthData); + /** + * Gets ttl in seconds + * + * @returns {Number} Ttl for JWT tokens, NaN for all other tokens + */ + getTtl() { + return this.authentication.getTtl(); + } - const serverUrl = this.config.joinBase(provider.url); - const credentials = this.config.withCredentials ? 'include' : 'same-origin'; + /** + * Gets exp from token payload and compares to current time + * + * @returns {Boolean} Returns (ttl > 0)? for JWT, undefined other tokens + */ + isTokenExpired() { + return this.authentication.isTokenExpired(); + } - return this.config.client.post(serverUrl, data, {credentials: credentials}); + /** + * Get payload from tokens + * + * @returns {Object} Payload for JWT, else null + */ + getTokenPayload() { + return this.authentication.getPayload(); } - buildQuery(provider) { - let query = {}; - const urlParams = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams']; + /** + * Request new accesss token + * + * @returns {Promise} Requests new token. can be called multiple times + */ + updateToken() { + if (!this.authentication.getRefreshToken()) { + return Promise.reject(new Error('refreshToken not set')); + } - urlParams.forEach( params => { - (provider[params] || []).forEach( paramName => { - const camelizedName = camelCase(paramName); - let paramValue = (typeof provider[paramName] === 'function') - ? provider[paramName]() - : provider[camelizedName]; + if (this.authentication.updateTokenCallstack.length === 0) { + const content = { + grant_type: 'refresh_token', + refresh_token: this.authentication.getRefreshToken(), + client_id: this.config.clientId ? this.config.clientId : undefined + }; - if (paramName === 'state') { - paramValue = encodeURIComponent(this.storage.get(provider.name + '_state')); - } + this.client.post(this.config.joinBase(this.config.refreshTokenUrl + ? this.config.refreshTokenUrl + : this.config.loginUrl), content) + .then(response => { + this.setResponseObject(response); + this.authentication.resolveUpdateTokenCallstack(this.isAuthenticated()); + }) + .catch(err => { + this.setResponseObject(null); + this.authentication.resolveUpdateTokenCallstack(Promise.reject(err)); + }); + } - if (paramName === 'scope' && Array.isArray(paramValue)) { - paramValue = paramValue.join(provider.scopeDelimiter); + return this.authentication.toUpdateTokenCallstack(); + } - if (provider.scopePrefix) { - paramValue = provider.scopePrefix + provider.scopeDelimiter + paramValue; - } + /** + * Signup locally. Login and redirect depending on config + * + * @param {String|{}} displayNameOrCredentials displayName | object with signup data. + * @param {[String]|{}} emailOrOptions [email | options for post request] + * @param {[String]} passwordOrRedirectUri [password | optional redirectUri overwrite] + * @param {[{}]} options [options] + * @param {[String]} redirectUri [optional redirectUri overwrite] + * + * @return {Promise|Promise} Server response as Object + */ + signup(displayNameOrCredentials, emailOrOptions, passwordOrRedirectUri, options, redirectUri) { + let content; + + if (typeof arguments[0] === 'object') { + content = arguments[0]; + options = arguments[1]; + redirectUri = arguments[2]; + } else { + content = { + 'displayName': displayNameOrCredentials, + 'email': emailOrOptions, + 'password': passwordOrRedirectUri + }; + } + return this.client.post(this.config.joinBase(this.config.signupUrl), content, options) + .then(response => { + if (this.config.loginOnSignup) { + this.setResponseObject(response); } + this.authentication.redirect(redirectUri, this.config.signupRedirect); - query[paramName] = paramValue; + return response; }); - }); - return query; } -} - -const camelCase = function(name) { - return name.replace(/([\:\-\_]+(.))/g, function(_, separator, letter, offset) { - return offset ? letter.toUpperCase() : letter; - }); -}; -@inject(Storage, BaseConfig, OAuth1, OAuth2, Auth0Lock) -export class Authentication { - 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; - this.payload = null; - this.exp = null; - this.hasDataStored = false; - } + /** + * login locally. Redirect depending on config + * + * @param {[String]|{}} emailOrCredentials email | object with signup data. + * @param {[String]} [passwordOrOptions] [password | options for post request] + * @param {[{}]} [optionsOrRedirectUri] [options | redirectUri overwrite]] + * @param {[String]} [redirectUri] [optional redirectUri overwrite] + * + * @return {Promise|Promise} Server response as Object + */ + login(emailOrCredentials, passwordOrOptions, optionsOrRedirectUri, redirectUri) { + let content; + if (typeof arguments[0] === 'object') { + content = arguments[0]; + optionsOrRedirectUri = arguments[1]; + redirectUri = arguments[2]; + } else { + content = { + 'email': emailOrCredentials, + 'password': passwordOrOptions + }; + optionsOrRedirectUri = optionsOrRedirectUri; + } - /* deprecated methods */ + if (this.config.clientId) { + content.client_id = this.config.clientId; + } - @deprecated({message: 'Use baseConfig.loginRoute instead.'}) - getLoginRoute() { - return this.config.loginRoute; - } + return this.client.post(this.config.joinBase(this.config.loginUrl), content, optionsOrRedirectUri) + .then(response => { + this.setResponseObject(response); - @deprecated({message: 'Use baseConfig.loginRedirect instead.'}) - getLoginRedirect() { - return this.config.loginRedirect; - } + this.authentication.redirect(redirectUri, this.config.loginRedirect); - @deprecated({message: 'Use baseConfig.joinBase(baseConfig.loginUrl) instead.'}) - getLoginUrl() { - return this.Config.joinBase(this.config.loginUrl); + return response; + }); } - @deprecated({message: 'Use baseConfig.joinBase(baseConfig.signupUrl) instead.'}) - getSignupUrl() { - return this.Config.joinBase(this.config.signupUrl); - } + /** + * logout locally and redirect to redirectUri (if set) or redirectUri of config. Sends logout request first, if set in config + * + * @param {[String]} [redirectUri] [optional redirectUri overwrite] + * + * @return {Promise<>|Promise|Promise} Server response as Object + */ + logout(redirectUri) { + let localLogout = response => new Promise(resolve => { + this.setResponseObject(null); - @deprecated({message: 'Use baseConfig.joinBase(baseConfig.profileUrl) instead.'}) - getProfileUrl() { - return this.Config.joinBase(this.config.profileUrl); - } + this.authentication.redirect(redirectUri, this.config.logoutRedirect); - @deprecated({message: 'Use .getAccessToken() instead.'}) - getToken() { - return this.getAccessToken(); - } + if (typeof this.onLogout === 'function') { + this.onLogout(response); + } - get responseObject() { - LogManager.getLogger('authentication').warn('Getter Authentication.responseObject is deprecated. Use Authentication.getResponseObject() instead.'); - return this.getResponseObject(); - } - - set responseObject(response) { - LogManager.getLogger('authentication').warn('Setter Authentication.responseObject is deprecated. Use AuthServive.setResponseObject(response) instead.'); - this.setResponseObject(response); - } - - /* get/set responseObject */ + resolve(response); + }); - getResponseObject() { - return JSON.parse(this.storage.get(this.config.storageKey)); + return (this.config.logoutUrl + ? this.client.request(this.config.logoutMethod, this.config.joinBase(this.config.logoutUrl)).then(localLogout) + : localLogout()); } - setResponseObject(response) { - if (response) { - this.getDataFromResponse(response); - this.storage.set(this.config.storageKey, JSON.stringify(response)); - return; - } - this.accessToken = null; - this.refreshToken = null; - this.payload = null; - this.exp = null; + /** + * Authenticate with third-party and redirect to redirectUri (if set) or redirectUri of config + * + * @param {String} name Name of the provider + * @param {[String]} [redirectUri] [optional redirectUri overwrite] + * @param {[{}]} [userData] [optional userData for the local authentication server] + * + * @return {Promise|Promise} Server response as Object + */ + authenticate(name, redirectUri, userData = {}) { + return this.authentication.authenticate(name, userData) + .then(response => { + this.setResponseObject(response); - this.hasDataStored = false; + this.authentication.redirect(redirectUri, this.config.loginRedirect); - this.storage.remove(this.config.storageKey); + return response; + }); } + /** + * Unlink third-party + * + * @param {String} name Name of the provider + * + * @return {Promise|Promise} Server response as Object + */ + unlink(name, redirectUri) { + const unlinkUrl = this.config.joinBase(this.config.unlinkUrl) + name; + return this.client.request(this.config.unlinkMethod, unlinkUrl) + .then(response => { + this.authentication.redirect(redirectUri); - /* get data, update if needed first */ - - getAccessToken() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.accessToken; - } - - getRefreshToken() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.refreshToken; + return response; + }); } +} - getPayload() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.payload; +export class BaseConfig { + /** + * Prepends baseUrl to a given url + * @param {String} url The relative url to append + * @return {String} joined baseUrl and url + */ + joinBase(url) { + return join(this.baseUrl, url); } - getExp() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.exp; + /** + * Merge current settings with incomming settings + * @param {Object} incomming Settings object to be merged into the current configuration + * @return {Config} this + */ + configure(incomming) { + for (let key in incomming) { + const value = incomming[key]; + if (value !== undefined) { + if (Array.isArray(value) || typeof value !== 'object' || value === null) { + this[key] = value; + } else { + extend(true, this[key], value); + } + } + } } + /* ----------- default config ----------- */ - /* get status from data */ - - getTtl() { - const exp = this.getExp(); - return Number.isNaN(exp) ? NaN : exp - Math.round(new Date().getTime() / 1000); - } - - isTokenExpired() { - const timeLeft = this.getTtl(); - return Number.isNaN(timeLeft) ? undefined : timeLeft < 0; - } + // Used internally. The used Rest instance; set during configuration (see index.js) + client = null; - isAuthenticated() { - const isTokenExpired = this.isTokenExpired(); - if (isTokenExpired === undefined ) return this.accessToken ? true : false; - return !isTokenExpired; - } + // If using aurelia-api: + // ===================== + // This is the name of the endpoint used for any requests made in relation to authentication (login, logout, etc.). An empty string selects the default endpoint of aurelia-api. + endpoint = null; + // When authenticated, these endpoints will have the token added to the header of any requests (for authorization). Accepts an array of endpoint names. An empty string selects the default endpoint of aurelia-api. + configureEndpoints = null; - /* get and set from response */ - getDataFromResponse(response) { - const config = this.config; + // SPA related options + // =================== - this.accessToken = this.getTokenFromResponse(response, config.accessTokenProp, config.accessTokenName, config.accessTokenRoot); + // The SPA url to which the user is redirected after a successful login + loginRedirect = '#/'; + // The SPA url to which the user is redirected after a successful logout + logoutRedirect = '#/'; + // The SPA route used when an unauthenticated user tries to access an SPA page that requires authentication + loginRoute = '/login'; + // Whether or not an authentication token is provided in the response to a successful signup + loginOnSignup = true; + // If loginOnSignup == false: The SPA url to which the user is redirected after a successful signup (else loginRedirect is used) + signupRedirect = '#/login'; + // redirect when token expires. 0 = don't redirect (default), 1 = use logoutRedirect, string = redirect there + expiredRedirect = 0; - this.refreshToken = null; - if (config.useRefreshToken) { - try { - this.refreshToken = this.getTokenFromResponse(response, config.refreshTokenProp, config.refreshTokenName, config.refreshTokenRoot); - } catch (e) { - this.refreshToken = null; - } - } - this.payload = null; + // API related options + // =================== - try { - this.payload = this.accessToken ? jwtDecode(this.accessToken) : null; - } catch (_) {_;} + // The base url used for all authentication related requests, including provider.url below. + // This appends to the httpClient/endpoint base url, it does not override it. + baseUrl = ''; + // The API endpoint to which login requests are sent + loginUrl = '/auth/login'; + // The API endpoint to which logout requests are sent (not needed for jwt) + logoutUrl = null; + // The HTTP method used for 'unlink' requests (Options: 'get' or 'post') + logoutMethod = 'get'; + // The API endpoint to which signup requests are sent + signupUrl = '/auth/signup'; + // The API endpoint used in profile requests (inc. `find/get` and `update`) + profileUrl = '/auth/me'; + // The method used to update the profile ('put' or 'patch') + profileMethod = 'put'; + // The API endpoint used with oAuth to unlink authentication + unlinkUrl = '/auth/unlink/'; + // The HTTP method used for 'unlink' requests (Options: 'get' or 'post') + unlinkMethod = 'get'; + // The API endpoint to which refreshToken requests are sent. null = loginUrl + refreshTokenUrl = null; - this.exp = this.payload ? parseInt(this.payload.exp, 10) : NaN; - this.hasDataStored = true; + // Token Options + // ============= - return { - accessToken: this.accessToken, - refreshToken: this.refreshToken, - payload: this.payload, - exp: this.exp - }; - } + // The header property used to contain the authToken in the header of API requests that require authentication + authHeader = 'Authorization'; + // The token name used in the header of API requests that require authentication + authTokenType = 'Bearer'; + // The the property from which to get the access token after a successful login or signup. Can also be dotted eg "accessTokenProp.accessTokenName" + accessTokenProp = 'access_token'; - getTokenFromResponse(response, tokenProp, tokenName, tokenRoot) { - if (!response) return undefined; - const responseTokenProp = tokenProp.split('.').reduce((o, x) => o[x], response); + // If the property defined by `accessTokenProp` is an object: + // ------------------------------------------------------------ - if (typeof responseTokenProp === 'string') { - return responseTokenProp; - } + //This is the property from which to get the token `{ "accessTokenProp": { "accessTokenName" : '...' } }` + accessTokenName = 'token'; + // This allows the token to be a further object deeper `{ "accessTokenProp": { "accessTokenRoot" : { "accessTokenName" : '...' } } }` + accessTokenRoot = false; - if (typeof responseTokenProp === 'object') { - const tokenRootData = tokenRoot && tokenRoot.split('.').reduce((o, x) => o[x], responseTokenProp); - const token = tokenRootData ? tokenRootData[tokenName] : responseTokenProp[tokenName]; - if (!token) throw new Error('Token not found in response'); + // Refresh Token Options + // ===================== - return token; - } + // Option to turn refresh tokens On/Off + useRefreshToken = false; + // The option to enable/disable the automatic refresh of Auth tokens using Refresh Tokens + autoUpdateToken = true; + // Oauth Client Id + clientId = false; + // The the property from which to get the refresh token after a successful token refresh. Can also be dotted eg "refreshTokenProp.refreshTokenProp" + refreshTokenProp = 'refresh_token'; - const token = response[tokenName] === undefined ? null : response[tokenName]; + // If the property defined by `refreshTokenProp` is an object: + // ----------------------------------------------------------- - if (!token) throw new Error('Token not found in response'); + // This is the property from which to get the token `{ "refreshTokenProp": { "refreshTokenName" : '...' } }` + refreshTokenName = 'token'; + // This allows the refresh token to be a further object deeper `{ "refreshTokenProp": { "refreshTokenRoot" : { "refreshTokenName" : '...' } } }` + refreshTokenRoot = false; - return token; - } + // Miscellaneous Options + // ===================== - toUpdateTokenCallstack() { - return new Promise(resolve => this.updateTokenCallstack.push(resolve)); - } + // Whether to enable the fetch interceptor which automatically adds the authentication headers + // (or not... e.g. if using a session based API or you want to override the default behaviour) + httpInterceptor = true; + // For OAuth only: Tell the API whether or not to include token cookies in the response (for session based APIs) + withCredentials = true; + // Controls how the popup is shown for different devices (Options: 'browser' or 'mobile') + platform = 'browser'; + // Determines the `PLATFORM` property name upon which aurelia-authentication data is stored (Default: `PLATFORM.localStorage`) + storage = 'localStorage'; + // The key used for storing the authentication response locally + storageKey = 'aurelia_authentication'; - resolveUpdateTokenCallstack(response) { - this.updateTokenCallstack.map(resolve => resolve(response)); - this.updateTokenCallstack = []; - } + // List of value-converters to make global + globalValueConverters = ['authFilterValueConverter']; +//OAuth provider specific related configuration + // ============================================ + providers = { + facebook: { + name: 'facebook', + url: '/auth/facebook', + authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', + redirectUri: PLATFORM.location.origin + '/', + requiredUrlParams: ['display', 'scope'], + scope: ['email'], + scopeDelimiter: ',', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 580, height: 400 } + }, + google: { + name: 'google', + url: '/auth/google', + authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['scope'], + optionalUrlParams: ['display', 'state'], + scope: ['profile', 'email'], + scopePrefix: 'openid', + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 452, height: 633 }, + state: randomState + }, + github: { + name: 'github', + url: '/auth/github', + authorizationEndpoint: 'https://github.com/login/oauth/authorize', + redirectUri: PLATFORM.location.origin, + optionalUrlParams: ['scope'], + scope: ['user:email'], + scopeDelimiter: ' ', + oauthType: '2.0', + popupOptions: { width: 1020, height: 618 } + }, + instagram: { + name: 'instagram', + url: '/auth/instagram', + authorizationEndpoint: 'https://api.instagram.com/oauth/authorize', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['scope'], + scope: ['basic'], + scopeDelimiter: '+', + oauthType: '2.0' + }, + linkedin: { + name: 'linkedin', + url: '/auth/linkedin', + authorizationEndpoint: 'https://www.linkedin.com/uas/oauth2/authorization', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['state'], + scope: ['r_emailaddress'], + scopeDelimiter: ' ', + state: 'STATE', + oauthType: '2.0', + popupOptions: { width: 527, height: 582 } + }, + twitter: { + name: 'twitter', + url: '/auth/twitter', + authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', + redirectUri: PLATFORM.location.origin, + oauthType: '1.0', + popupOptions: { width: 495, height: 645 } + }, + twitch: { + name: 'twitch', + url: '/auth/twitch', + authorizationEndpoint: 'https://api.twitch.tv/kraken/oauth2/authorize', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['scope'], + scope: ['user_read'], + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 500, height: 560 } + }, + live: { + name: 'live', + url: '/auth/live', + authorizationEndpoint: 'https://login.live.com/oauth20_authorize.srf', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['display', 'scope'], + scope: ['wl.emails'], + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 500, height: 560 } + }, + yahoo: { + name: 'yahoo', + url: '/auth/yahoo', + authorizationEndpoint: 'https://api.login.yahoo.com/oauth2/request_auth', + redirectUri: PLATFORM.location.origin, + scope: [], + scopeDelimiter: ',', + oauthType: '2.0', + popupOptions: { width: 559, height: 519 } + }, + bitbucket: { + name: 'bitbucket', + url: '/auth/bitbucket', + authorizationEndpoint: 'https://bitbucket.org/site/oauth2/authorize', + redirectUri: PLATFORM.location.origin + '/', + requiredUrlParams: ['scope'], + scope: ['email'], + 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: randomState + } + }; - /** - * Authenticate with third-party - * - * @param {String} name of the provider - * @param {[{}]} [userData] - * - * @return {Promise} - */ - authenticate(name, userData = {}) { - let oauthType = this.config.providers[name].type; - - if (oauthType) { - LogManager.getLogger('authentication').warn('DEPRECATED: Setting provider.type is deprecated and replaced by provider.oauthType'); - } else { - oauthType = this.config.providers[name].oauthType; - } - - 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); - } - - redirect(redirectUrl, defaultRedirectUrl) { - // stupid rule to keep it BC - if (redirectUrl === true) { - LogManager.getLogger('authentication').warn('DEPRECATED: Setting redirectUrl === true to actually *not redirect* is deprecated. Set redirectUrl === 0 instead.'); - return; - } - // stupid rule to keep it BC - if (redirectUrl === false) { - LogManager.getLogger('authentication').warn('BREAKING CHANGE: Setting redirectUrl === false to actually *do redirect* is deprecated. Set redirectUrl to undefined or null to use the defaultRedirectUrl if so desired.'); - } - // BC hack. explicit 0 means don't redirect. false will be added later and 0 deprecated - if (redirectUrl === 0) { - return; - } - if (typeof redirectUrl === 'string') { - PLATFORM.location.href = encodeURI(redirectUrl); - } else if (defaultRedirectUrl) { - PLATFORM.location.href = defaultRedirectUrl; - } - } -} - -@inject(Authentication, BaseConfig) -export class AuthService { - /** - * The Authentication instance that handles the token - * - * @param {Authentication} - */ - authentication; - - /** - * The Config instance that contains the current configuration setting - * - * @param {Config} - */ - config; - - /** - * The current login status - * - * @param {Boolean} - */ - authenticated = false; - - /** - * The currently set timeoutID - * - * @param {Number} - */ - timeoutID = 0; - - /** - * Create an AuthService instance - * - * @param {Authentication} authentication The Authentication instance to be used - * @param {Config} config The Config instance to be used - */ - constructor(authentication, config) { - this.authentication = authentication; - this.config = config; - - // get token stored in previous format over - const oldStorageKey = config.tokenPrefix - ? config.tokenPrefix + '_' + config.tokenName - : config.tokenName; - const oldToken = authentication.storage.get(oldStorageKey); - - if (oldToken) { - LogManager.getLogger('authentication').info('Found token with deprecated format in storage. Converting it to new format. No further action required.'); - let fakeOldResponse = {}; - fakeOldResponse[config.accessTokenProp] = oldToken; - this.setResponseObject(fakeOldResponse); - authentication.storage.remove(oldStorageKey); - } - - // initialize status by resetting if existing stored responseObject - this.setResponseObject(this.authentication.getResponseObject()); - } - - /** - * Getter: The configured client for all aurelia-authentication requests - * - * @return {HttpClient} - */ - get client() { - return this.config.client; - } - - get auth() { - LogManager.getLogger('authentication').warn('AuthService.auth is deprecated. Use .authentication instead.'); - return this.authentication; - } - - /** - * Sets the login timeout - * - * @param {Number} ttl Timeout time in ms - */ - setTimeout(ttl) { - this.clearTimeout(); - - this.timeoutID = PLATFORM.global.setTimeout(() => { - if (this.config.autoUpdateToken - && this.authentication.getAccessToken() - && this.authentication.getRefreshToken()) { - this.updateToken(); - } else { - this.logout(this.config.expiredRedirect); - } - }, ttl); - } - - /** - * Clears the login timeout - */ - clearTimeout() { - if (this.timeoutID) { - PLATFORM.global.clearTimeout(this.timeoutID); - } - this.timeoutID = 0; - } - - /** - * Stores and analyses the servers responseObject. Sets login status and timeout - * - * @param {Object} response The servers response as GOJO - */ - setResponseObject(response) { - this.clearTimeout(); - - this.authentication.setResponseObject(response); + /* deprecated defaults */ + _authToken = 'Bearer'; + _responseTokenProp = 'access_token'; + _tokenName = 'token'; + _tokenRoot = false; + _tokenPrefix = 'aurelia'; - this.authenticated = this.authentication.isAuthenticated(); - if (this.authenticated && !Number.isNaN(this.authentication.exp)) { - this.setTimeout(this.getTtl() * 1000); - } + /* deprecated methods and parameteres */ + set authToken(authToken) { + LogManager.getLogger('authentication').warn('BaseConfig.authToken is deprecated. Use BaseConfig.authTokenType instead.'); + this._authTokenType = authToken; + this.authTokenType = authToken; + return authToken; } - - /** - * Get current user profile from server - * - * @param {[{}|number|string]} [criteriaOrId object or a Number|String converted to {id: criteriaOrId}] - * - * @return {Promise} - */ - getMe(criteriaOrId) { - if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { - criteriaOrId = {id: criteriaOrId}; - } - return this.client.find(this.config.joinBase(this.config.profileUrl), criteriaOrId); + get authToken() { + return this._authTokenType; } - /** - * Send current user profile update to server - - * @param {any} Request body with data. - * @param {[{}|Number|String]} [criteriaOrId object or a Number|String converted to {id: criteriaOrId}] - * - * @return {Promise} - */ - updateMe(body, criteriaOrId) { - if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { - criteriaOrId = { id: criteriaOrId }; - } - if (this.config.profileMethod === 'put') { - return this.client.update(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); - } - return this.client.patch(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); + set responseTokenProp(responseTokenProp) { + LogManager.getLogger('authentication').warn('BaseConfig.responseTokenProp is deprecated. Use BaseConfig.accessTokenProp instead.'); + this._responseTokenProp = responseTokenProp; + this.accessTokenProp = responseTokenProp; + return responseTokenProp; } - - /** - * Get accessToken from storage - * - * @returns {String} Current accessToken - */ - getAccessToken() { - return this.authentication.getAccessToken(); + get responseTokenProp() { + return this._responseTokenProp; } - @deprecated({message: 'Use .getAccessToken() instead.'}) - getCurrentToken() { - return this.getAccessToken(); + set tokenRoot(tokenRoot) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenRoot is deprecated. Use BaseConfig.accessTokenRoot instead.'); + this._tokenRoot = tokenRoot; + this.accessTokenRoot = tokenRoot; + return tokenRoot; } - - /** - * Get refreshToken from storage - * - * @returns {String} Current refreshToken - */ - getRefreshToken() { - return this.authentication.getRefreshToken(); + get tokenRoot() { + return this._tokenRoot; } - /** - * Gets authentication status - * - * @returns {Boolean} For Non-JWT and unexpired JWT: true, else: false - */ - isAuthenticated() { - let authenticated = this.authentication.isAuthenticated(); - - // auto-update token? - if (!authenticated - && this.config.autoUpdateToken - && this.authentication.getAccessToken() - && this.authentication.getRefreshToken()) { - this.updateToken(); - authenticated = true; - } - - return authenticated; + set tokenName(tokenName) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenName is deprecated. Use BaseConfig.accessTokenName instead.'); + this._tokenName = tokenName; + this.accessTokenName = tokenName; + return tokenName; } - - /** - * Gets exp in milliseconds - * - * @returns {Number} Exp for JWT tokens, NaN for all other tokens - */ - getExp() { - return this.authentication.getExp(); + get tokenName() { + return this._tokenName; } - /** - * Gets ttl in seconds - * - * @returns {Number} Ttl for JWT tokens, NaN for all other tokens - */ - getTtl() { - return this.authentication.getTtl(); + set tokenPrefix(tokenPrefix) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenPrefix is obsolete. Use BaseConfig.storageKey instead.'); + this._tokenPrefix = tokenPrefix; + return tokenPrefix; + } + get tokenPrefix() { + return this._tokenPrefix || 'aurelia'; } - /** - * Gets exp from token payload and compares to current time - * - * @returns {Boolean} Returns (ttl > 0)? for JWT, undefined other tokens - */ - isTokenExpired() { - return this.authentication.isTokenExpired(); + get current() { + LogManager.getLogger('authentication').warn('Getter BaseConfig.current is deprecated. Use BaseConfig directly instead.'); + return this; + } + set current(_) { + throw new Error('Setter BaseConfig.current is obsolete. Use BaseConfig directly instead.'); } - /** - * Get payload from tokens - * - * @returns {Object} Payload for JWT, else null - */ - getTokenPayload() { - return this.authentication.getPayload(); + get _current() { + LogManager.getLogger('authentication').warn('Getter BaseConfig._current is deprecated. Use BaseConfig directly instead.'); + return this; } + set _current(_) { + throw new Error('Setter BaseConfig._current is obsolete. Use BaseConfig directly instead.'); + } +} +function randomState() { + let rand = Math.random().toString(36).substr(2); + return encodeURIComponent(rand); +} + +@inject(HttpClient, Config, AuthService, BaseConfig) +export class FetchConfig { /** - * Request new accesss token + * Construct the FetchConfig * - * @returns {Promise} Requests new token. can be called multiple times + * @param {HttpClient} httpClient + * @param {Config} clientConfig + * @param {Authentication} authService + * @param {BaseConfig} config */ - updateToken() { - if (!this.authentication.getRefreshToken()) { - return Promise.reject(new Error('refreshToken not set')); - } - - if (this.authentication.updateTokenCallstack.length === 0) { - const content = { - grant_type: 'refresh_token', - refresh_token: this.authentication.getRefreshToken(), - client_id: this.config.clientId ? this.config.clientId : undefined - }; - - this.client.post(this.config.joinBase(this.config.refreshTokenUrl - ? this.config.refreshTokenUrl - : this.config.loginUrl), content) - .then(response => { - this.setResponseObject(response); - this.authentication.resolveUpdateTokenCallstack(this.isAuthenticated()); - }) - .catch(err => { - this.setResponseObject(null); - this.authentication.resolveUpdateTokenCallstack(Promise.reject(err)); - }); - } - - return this.authentication.toUpdateTokenCallstack(); + constructor(httpClient, clientConfig, authService, config) { + this.httpClient = httpClient; + this.clientConfig = clientConfig; + this.authService = authService; + this.config = config; } /** - * Signup locally. Login and redirect depending on config - * - * @param {String|{}} displayNameOrCredentials displayName | object with signup data. - * @param {[String]|{}} emailOrOptions [email | options for post request] - * @param {[String]} passwordOrRedirectUri [password | optional redirectUri overwrite] - * @param {[{}]} options [options] - * @param {[String]} redirectUri [optional redirectUri overwrite] + * Interceptor for HttpClient * - * @return {Promise|Promise} Server response as Object + * @return {{request: Function, response: Function}} */ - signup(displayNameOrCredentials, emailOrOptions, passwordOrRedirectUri, options, redirectUri) { - let content; + get interceptor() { + return { + request: request => { + if (!this.config.httpInterceptor || !this.authService.isAuthenticated()) { + return request; + } + let token = this.authService.getAccessToken(); - if (typeof arguments[0] === 'object') { - content = arguments[0]; - options = arguments[1]; - redirectUri = arguments[2]; - } else { - content = { - 'displayName': displayNameOrCredentials, - 'email': emailOrOptions, - 'password': passwordOrRedirectUri - }; - } - return this.client.post(this.config.joinBase(this.config.signupUrl), content, options) - .then(response => { - if (this.config.loginOnSignup) { - this.setResponseObject(response); + if (this.config.authTokenType) { + token = `${this.config.authTokenType} ${token}`; } - this.authentication.redirect(redirectUri, this.config.signupRedirect); - return response; - }); + request.headers.set(this.config.authHeader, token); + + return request; + }, + response: (response, request) => { + return new Promise((resolve, reject) => { + if (response.ok) { + return resolve(response); + } + if (response.status !== 401) { + return resolve(response); + } + if (!this.config.httpInterceptor || !this.authService.isTokenExpired()) { + return resolve(response); + } + if (!this.config.useRefreshToken || !this.authService.getRefreshToken()) { + return resolve(response); + } + + return this.authService.updateToken().then(() => { + let token = this.authService.getAccessToken(); + + if (this.config.authTokenType) { + token = `${this.config.authTokenType} ${token}`; + } + + request.headers.set(this.config.authHeader, token); + + return this.client.fetch(request).then(resolve); + }); + }); + } + }; } /** - * login locally. Redirect depending on config + * Configure client(s) with authorization interceptor * - * @param {[String]|{}} emailOrCredentials email | object with signup data. - * @param {[String]} [passwordOrOptions] [password | options for post request] - * @param {[{}]} [optionsOrRedirectUri] [options | redirectUri overwrite]] - * @param {[String]} [redirectUri] [optional redirectUri overwrite] + * @param {HttpClient|Rest|string[]} client HttpClient, rest client or api endpoint name, or an array thereof * - * @return {Promise|Promise} Server response as Object + * @return {HttpClient[]} */ - login(emailOrCredentials, passwordOrOptions, optionsOrRedirectUri, redirectUri) { - let content; + configure(client) { + if (Array.isArray(client)) { + let configuredClients = []; + client.forEach(toConfigure => { + configuredClients.push(this.configure(toConfigure)); + }); - if (typeof arguments[0] === 'object') { - content = arguments[0]; - optionsOrRedirectUri = arguments[1]; - redirectUri = arguments[2]; - } else { - content = { - 'email': emailOrCredentials, - 'password': passwordOrOptions - }; - optionsOrRedirectUri = optionsOrRedirectUri; + return configuredClients; } - if (this.config.clientId) { - content.client_id = this.config.clientId; + if (typeof client === 'string') { + const endpoint = this.clientConfig.getEndpoint(client); + if (!endpoint) { + throw new Error(`There is no '${client || 'default'}' endpoint registered.`); + } + client = endpoint.client; + } else if (client instanceof Rest) { + client = client.client; + } else if (!(client instanceof HttpClient)) { + client = this.httpClient; } - return this.client.post(this.config.joinBase(this.config.loginUrl), content, optionsOrRedirectUri) - .then(response => { - this.setResponseObject(response); + client.interceptors.push(this.interceptor); - this.authentication.redirect(redirectUri, this.config.loginRedirect); + return client; + } +} - return response; - }); +@inject(Storage, Popup, BaseConfig) +export class OAuth1 { + constructor(storage, popup, config) { + this.storage = storage; + this.config = config; + this.popup = popup; + this.defaults = { + url: null, + name: null, + popupOptions: null, + redirectUri: null, + authorizationEndpoint: null + }; } - /** - * logout locally and redirect to redirectUri (if set) or redirectUri of config. Sends logout request first, if set in config - * - * @param {[String]} [redirectUri] [optional redirectUri overwrite] - * - * @return {Promise<>|Promise|Promise} Server response as Object - */ - logout(redirectUri) { - let localLogout = response => new Promise(resolve => { - this.setResponseObject(null); + open(options, userData) { + const provider = extend(true, {}, this.defaults, options); + const serverUrl = this.config.joinBase(provider.url); - this.authentication.redirect(redirectUri, this.config.logoutRedirect); + if (this.config.platform !== 'mobile') { + this.popup = this.popup.open('', provider.name, provider.popupOptions); + } - if (typeof this.onLogout === 'function') { - this.onLogout(response); - } + return this.config.client.post(serverUrl) + .then(response => { + const url = provider.authorizationEndpoint + '?' + buildQueryString(response); - resolve(response); - }); + if (this.config.platform === 'mobile') { + this.popup = this.popup.open(url, provider.name, provider.popupOptions); + } else { + this.popup.popupWindow.location = url; + } - return (this.config.logoutUrl - ? this.client.request(this.config.logoutMethod, this.config.joinBase(this.config.logoutUrl)).then(localLogout) - : localLogout()); + const popupListener = this.config.platform === 'mobile' + ? this.popup.eventListener(provider.redirectUri) + : this.popup.pollPopup(); + + return popupListener.then(result => this.exchangeForToken(result, userData, provider)); + }); } - /** - * Authenticate with third-party and redirect to redirectUri (if set) or redirectUri of config - * - * @param {String} name Name of the provider - * @param {[String]} [redirectUri] [optional redirectUri overwrite] - * @param {[{}]} [userData] [optional userData for the local authentication server] - * - * @return {Promise|Promise} Server response as Object - */ - authenticate(name, redirectUri, userData = {}) { - return this.authentication.authenticate(name, userData) - .then(response => { - this.setResponseObject(response); + exchangeForToken(oauthData, userData, provider) { + const data = extend(true, {}, userData, oauthData); + const serverUrl = this.config.joinBase(provider.url); + const credentials = this.config.withCredentials ? 'include' : 'same-origin'; - this.authentication.redirect(redirectUri, this.config.loginRedirect); + return this.config.client.post(serverUrl, data, {credentials: credentials}); + } +} - return response; - }); +@inject(Storage, Popup, BaseConfig) +export class OAuth2 { + constructor(storage, popup, config) { + this.storage = storage; + this.config = config; + this.popup = popup; + this.defaults = { + url: null, + name: null, + state: null, + scope: null, + scopeDelimiter: null, + redirectUri: null, + popupOptions: null, + authorizationEndpoint: null, + responseParams: null, + requiredUrlParams: null, + optionalUrlParams: null, + defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], + responseType: 'code' + }; } - /** - * Unlink third-party - * - * @param {String} name Name of the provider - * - * @return {Promise|Promise} Server response as Object - */ - unlink(name, redirectUri) { - const unlinkUrl = this.config.joinBase(this.config.unlinkUrl) + name; - return this.client.request(this.config.unlinkMethod, unlinkUrl) - .then(response => { - this.authentication.redirect(redirectUri); + open(options, userData) { + const provider = extend(true, {}, this.defaults, options); + const stateName = provider.name + '_state'; - return response; + if (typeof provider.state === 'function') { + this.storage.set(stateName, provider.state()); + } else if (typeof provider.state === 'string') { + this.storage.set(stateName, provider.state); + } + + const url = provider.authorizationEndpoint + + '?' + buildQueryString(this.buildQuery(provider)); + const popup = this.popup.open(url, provider.name, provider.popupOptions); + const openPopup = (this.config.platform === 'mobile') + ? popup.eventListener(provider.redirectUri) + : popup.pollPopup(); + + return openPopup + .then(oauthData => { + if (provider.responseType === 'token' || + provider.responseType === 'id_token%20token' || + provider.responseType === 'token%20id_token' + ) { + return oauthData; + } + if (oauthData.state && oauthData.state !== this.storage.get(stateName)) { + return Promise.reject('OAuth 2.0 state parameter mismatch.'); + } + return this.exchangeForToken(oauthData, userData, provider); }); } -} -@inject(AuthService) -export class AuthenticateStep { - constructor(authService) { - this.authService = authService; + exchangeForToken(oauthData, userData, provider) { + const data = extend(true, {}, userData, { + clientId: provider.clientId, + redirectUri: provider.redirectUri + }, oauthData); + + const serverUrl = this.config.joinBase(provider.url); + const credentials = this.config.withCredentials ? 'include' : 'same-origin'; + + return this.config.client.post(serverUrl, data, {credentials: credentials}); } - run(routingContext, next) { - const isLoggedIn = this.authService.authenticated; - const loginRoute = this.authService.config.loginRoute; + buildQuery(provider) { + let query = {}; + const urlParams = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams']; - if (routingContext.getAllInstructions().some(route => route.config.auth === true)) { - if (!isLoggedIn) { - return next.cancel(new Redirect(loginRoute)); - } - } else if (isLoggedIn && routingContext.getAllInstructions().some(route => route.fragment === loginRoute)) { - return next.cancel(new Redirect( this.authService.config.loginRedirect )); - } + urlParams.forEach( params => { + (provider[params] || []).forEach( paramName => { + const camelizedName = camelCase(paramName); + let paramValue = (typeof provider[paramName] === 'function') + ? provider[paramName]() + : provider[camelizedName]; - return next(); + if (paramName === 'state') { + paramValue = encodeURIComponent(this.storage.get(provider.name + '_state')); + } + + if (paramName === 'scope' && Array.isArray(paramValue)) { + paramValue = paramValue.join(provider.scopeDelimiter); + + if (provider.scopePrefix) { + paramValue = provider.scopePrefix + provider.scopeDelimiter + paramValue; + } + } + + query[paramName] = paramValue; + }); + }); + return query; } } -@inject(AuthService) -export class AuthorizeStep { - constructor(authService) { - LogManager.getLogger('authentication').warn('AuthorizeStep is deprecated. Use AuthenticationStep instead.'); +const camelCase = function(name) { + return name.replace(/([\:\-\_]+(.))/g, function(_, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }); +}; - this.authService = authService; +export class Popup { + constructor() { + this.popupWindow = null; + this.polling = null; + this.url = ''; } - run(routingContext, next) { - const isLoggedIn = this.authService.isAuthenticated(); - const loginRoute = this.authService.config.loginRoute; + open(url, windowName, options) { + this.url = url; + const optionsString = buildPopupWindowOptions(options || {}); - if (routingContext.getAllInstructions().some(route => route.config.auth)) { - if (!isLoggedIn) { - return next.cancel(new Redirect(loginRoute)); - } - } else if (isLoggedIn && routingContext.getAllInstructions().some(route => route.fragment === loginRoute)) { - return next.cancel(new Redirect( this.authService.config.loginRedirect )); - } + this.popupWindow = PLATFORM.global.open(url, windowName, optionsString); - return next(); - } -} + if (this.popupWindow && this.popupWindow.focus) { + this.popupWindow.focus(); + } -@inject(HttpClient, Config, AuthService, BaseConfig) -export class FetchConfig { - /** - * Construct the FetchConfig - * - * @param {HttpClient} httpClient - * @param {Config} clientConfig - * @param {Authentication} authService - * @param {BaseConfig} config - */ - constructor(httpClient, clientConfig, authService, config) { - this.httpClient = httpClient; - this.clientConfig = clientConfig; - this.authService = authService; - this.config = config; + return this; } - /** - * Interceptor for HttpClient - * - * @return {{request: Function, response: Function}} - */ - get interceptor() { - return { - request: request => { - if (!this.config.httpInterceptor || !this.authService.isAuthenticated()) { - return request; + eventListener(redirectUri) { + return new Promise((resolve, reject) => { + this.popupWindow.addEventListener('loadstart', event => { + if (event.url.indexOf(redirectUri) !== 0) { + return; } - let token = this.authService.getAccessToken(); - if (this.config.authTokenType) { - token = `${this.config.authTokenType} ${token}`; - } + const parser = DOM.createElement('a'); + parser.href = event.url; - request.headers.set(this.config.authHeader, token); + if (parser.search || parser.hash) { + const qs = parseUrl(parser); - return request; - }, - response: (response, request) => { - return new Promise((resolve, reject) => { - if (response.ok) { - return resolve(response); - } - if (response.status !== 401) { - return resolve(response); - } - if (!this.config.httpInterceptor || !this.authService.isTokenExpired()) { - return resolve(response); - } - if (!this.config.useRefreshToken || !this.authService.getRefreshToken()) { - return resolve(response); + if (qs.error) { + reject({error: qs.error}); + } else { + resolve(qs); } - this.authService.updateToken().then(() => { - let token = this.authService.getAccessToken(); - - if (this.config.authTokenType) { - token = `${this.config.authTokenType} ${token}`; - } + this.popupWindow.close(); + } + }); - request.headers.set(this.config.authHeader, token); + this.popupWindow.addEventListener('exit', () => { + reject({data: 'Provider Popup was closed'}); + }); - return this.client.fetch(request).then(resolve); - }); - }); - } - }; + this.popupWindow.addEventListener('loaderror', () => { + reject({data: 'Authorization Failed'}); + }); + }); } - /** - * Configure client(s) with authorization interceptor - * - * @param {HttpClient|Rest|string[]} client HttpClient, rest client or api endpoint name, or an array thereof - * - * @return {HttpClient[]} - */ - configure(client) { - if (Array.isArray(client)) { - let configuredClients = []; - client.forEach(toConfigure => { - configuredClients.push(this.configure(toConfigure)); - }); + pollPopup() { + return new Promise((resolve, reject) => { + this.polling = PLATFORM.global.setInterval(() => { + let errorData; - return configuredClients; - } + try { + if (this.popupWindow.location.host === PLATFORM.global.document.location.host + && (this.popupWindow.location.search || this.popupWindow.location.hash)) { + const qs = parseUrl(this.popupWindow.location); - if (typeof client === 'string') { - const endpoint = this.clientConfig.getEndpoint(client); - if (!endpoint) { - throw new Error(`There is no '${client || 'default'}' endpoint registered.`); - } - client = endpoint.client; - } else if (client instanceof Rest) { - client = client.client; - } else if (!(client instanceof HttpClient)) { - client = this.httpClient; - } + if (qs.error) { + reject({error: qs.error}); + } else { + resolve(qs); + } - client.interceptors.push(this.interceptor); + this.popupWindow.close(); + PLATFORM.global.clearInterval(this.polling); + } + } catch (error) { + errorData = error; + } - return client; + if (!this.popupWindow) { + PLATFORM.global.clearInterval(this.polling); + reject({ + error: errorData, + data: 'Provider Popup Blocked' + }); + } else if (this.popupWindow.closed) { + PLATFORM.global.clearInterval(this.polling); + reject({ + error: errorData, + data: 'Problem poll popup' + }); + } + }, 35); + }); } } -// import to ensure value-converters get bundled -import './authFilterValueConverter'; +const buildPopupWindowOptions = options => { + const width = options.width || 500; + const height = options.height || 500; -/** - * Configure the plugin. - * - * @param {{globalResources: Function, container: {Container}}} aurelia - * @param {{}|Function} config - */ -function configure(aurelia, config) { - // ie9 polyfill - if (!PLATFORM.location.origin) { - PLATFORM.location.origin = PLATFORM.location.protocol + '//' + PLATFORM.location.hostname + (PLATFORM.location.port ? ':' + PLATFORM.location.port : ''); - } + const extended = extend({ + width: width, + height: height, + left: PLATFORM.global.screenX + ((PLATFORM.global.outerWidth - width) / 2), + top: PLATFORM.global.screenY + ((PLATFORM.global.outerHeight - height) / 2.5) + }, options); - const baseConfig = aurelia.container.get(BaseConfig); + let parts = []; + Object.keys(extended).map(key => parts.push(key + '=' + extended[key])); - if (typeof config === 'function') { - config(baseConfig); - } else if (typeof config === 'object') { - baseConfig.configure(config); - } + return parts.join(','); +}; - // after baseConfig was configured - for (let converter of baseConfig.globalValueConverters) { - aurelia.globalResources(`./${converter}`); - LogManager.getLogger('authentication').info(`Add globalResources value-converter: ${converter}`); - } - const fetchConfig = aurelia.container.get(FetchConfig); - const clientConfig = aurelia.container.get(Config); +const parseUrl = url => { + return extend(true, {}, parseQueryString(url.search), parseQueryString(url.hash)); +}; - // Array? Configure the provided endpoints. - if (Array.isArray(baseConfig.configureEndpoints)) { - baseConfig.configureEndpoints.forEach(endpointToPatch => { - fetchConfig.configure(endpointToPatch); - }); +@inject(BaseConfig) +export class Storage { + constructor(config) { + this.config = config; } - let client; - - // Let's see if there's a configured named or default endpoint or a HttpClient. - if (baseConfig.endpoint !== null) { - if (typeof baseConfig.endpoint === 'string') { - const endpoint = clientConfig.getEndpoint(baseConfig.endpoint); - if (!endpoint) { - throw new Error(`There is no '${baseConfig.endpoint || 'default'}' endpoint registered.`); - } - client = endpoint; - } else if (baseConfig.endpoint instanceof HttpClient) { - client = new Rest(baseConfig.endpoint); - } + get(key) { + return PLATFORM.global[this.config.storage].getItem(key); } - // No? Fine. Default to HttpClient. BC all the way. - if (!(client instanceof Rest)) { - client = new Rest(aurelia.container.get(HttpClient)); + set(key, value) { + PLATFORM.global[this.config.storage].setItem(key, value); } - // Set the client on the config, for use throughout the plugin. - baseConfig.client = client; + remove(key) { + PLATFORM.global[this.config.storage].removeItem(key); + } } - -export { - configure, - FetchConfig, - AuthService, - AuthorizeStep, - AuthenticateStep -}; diff --git a/dist/commonjs/aurelia-authentication.d.ts b/dist/commonjs/aurelia-authentication.d.ts deleted file mode 100644 index ba2a7d6..0000000 --- a/dist/commonjs/aurelia-authentication.d.ts +++ /dev/null @@ -1,495 +0,0 @@ -declare module 'aurelia-authentication' { - export class Auth0Lock { - constructor(storage?: any, config?: any); - open(options?: any, userData?: any): any; - } - export class AuthenticatedFilterValueConverter { - constructor(authService?: any); - - /** - * route toView predictator on route.config.auth === (parameter || authService.isAuthenticated()) - * @param {RouteConfig} routes the routes array to convert - * @param {[Boolean]} [isAuthenticated] optional isAuthenticated value. default: this.authService.authenticated - * @return {Boolean} show/hide element - */ - toView(routes?: any, isAuthenticated?: any): any; - } - export class AuthenticatedValueConverter { - constructor(authService?: any); - - /** - * element toView predictator on authService.isAuthenticated() - * @return {Boolean} show/hide element - */ - toView(): any; - } - export class AuthenticateStep { - constructor(authService?: any); - run(routingContext?: any, next?: any): any; - } - export class Authentication { - constructor(storage?: any, config?: any, oAuth1?: any, oAuth2?: any, auth0Lock?: any); - - /* deprecated methods */ - getLoginRoute(): any; - getLoginRedirect(): any; - getLoginUrl(): any; - getSignupUrl(): any; - getProfileUrl(): any; - getToken(): any; - responseObject: any; - - /* get/set responseObject */ - getResponseObject(): any; - setResponseObject(response?: any): any; - - /* get data, update if needed first */ - getAccessToken(): any; - getRefreshToken(): any; - getPayload(): any; - getExp(): any; - - /* get status from data */ - getTtl(): any; - isTokenExpired(): any; - isAuthenticated(): any; - - /* get and set from response */ - getDataFromResponse(response?: any): any; - getTokenFromResponse(response?: any, tokenProp?: any, tokenName?: any, tokenRoot?: any): any; - toUpdateTokenCallstack(): any; - resolveUpdateTokenCallstack(response?: any): any; - - /** - * Authenticate with third-party - * - * @param {String} name of the provider - * @param {[{}]} [userData] - * - * @return {Promise} - */ - authenticate(name?: any, userData?: any): any; - redirect(redirectUrl?: any, defaultRedirectUrl?: any): any; - } - export class AuthFilterValueConverter { - - /** - * route toView predictator on route.config.auth === isAuthenticated - * @param {RouteConfig} routes the routes array to convert - * @param {Boolean} isAuthenticated authentication status - * @return {Boolean} show/hide element - */ - toView(routes?: any, isAuthenticated?: any): any; - } - export class AuthorizeStep { - constructor(authService?: any); - run(routingContext?: any, next?: any): any; - } - export class AuthService { - - /** - * The Authentication instance that handles the token - * - * @param {Authentication} - */ - authentication: any; - - /** - * The Config instance that contains the current configuration setting - * - * @param {Config} - */ - config: any; - - /** - * The current login status - * - * @param {Boolean} - */ - authenticated: any; - - /** - * The currently set timeoutID - * - * @param {Number} - */ - timeoutID: any; - - /** - * Create an AuthService instance - * - * @param {Authentication} authentication The Authentication instance to be used - * @param {Config} config The Config instance to be used - */ - constructor(authentication?: any, config?: any); - - /** - * Getter: The configured client for all aurelia-authentication requests - * - * @return {HttpClient} - */ - client: any; - auth: any; - - /** - * Sets the login timeout - * - * @param {Number} ttl Timeout time in ms - */ - setTimeout(ttl?: any): any; - - /** - * Clears the login timeout - */ - clearTimeout(): any; - - /** - * Stores and analyses the servers responseObject. Sets login status and timeout - * - * @param {Object} response The servers response as GOJO - */ - setResponseObject(response?: any): any; - - /** - * Get current user profile from server - * - * @param {[{}|number|string]} [criteriaOrId object or a Number|String converted to {id: criteriaOrId}] - * - * @return {Promise} - */ - getMe(criteriaOrId?: any): any; - - /** - * Send current user profile update to server - - * @param {any} Request body with data. - * @param {[{}|Number|String]} [criteriaOrId object or a Number|String converted to {id: criteriaOrId}] - * - * @return {Promise} - */ - updateMe(body?: any, criteriaOrId?: any): any; - - /** - * Get accessToken from storage - * - * @returns {String} Current accessToken - */ - getAccessToken(): any; - getCurrentToken(): any; - - /** - * Get refreshToken from storage - * - * @returns {String} Current refreshToken - */ - getRefreshToken(): any; - - /** - * Gets authentication status - * - * @returns {Boolean} For Non-JWT and unexpired JWT: true, else: false - */ - isAuthenticated(): any; - - /** - * Gets exp in milliseconds - * - * @returns {Number} Exp for JWT tokens, NaN for all other tokens - */ - getExp(): any; - - /** - * Gets ttl in seconds - * - * @returns {Number} Ttl for JWT tokens, NaN for all other tokens - */ - getTtl(): any; - - /** - * Gets exp from token payload and compares to current time - * - * @returns {Boolean} Returns (ttl > 0)? for JWT, undefined other tokens - */ - isTokenExpired(): any; - - /** - * Get payload from tokens - * - * @returns {Object} Payload for JWT, else null - */ - getTokenPayload(): any; - - /** - * Request new accesss token - * - * @returns {Promise} Requests new token. can be called multiple times - */ - updateToken(): any; - - /** - * Signup locally. Login and redirect depending on config - * - * @param {String|{}} displayNameOrCredentials displayName | object with signup data. - * @param {[String]|{}} emailOrOptions [email | options for post request] - * @param {[String]} passwordOrRedirectUri [password | optional redirectUri overwrite] - * @param {[{}]} options [options] - * @param {[String]} redirectUri [optional redirectUri overwrite] - * - * @return {Promise|Promise} Server response as Object - */ - signup(displayNameOrCredentials?: any, emailOrOptions?: any, passwordOrRedirectUri?: any, options?: any, redirectUri?: any): any; - - /** - * login locally. Redirect depending on config - * - * @param {[String]|{}} emailOrCredentials email | object with signup data. - * @param {[String]} [passwordOrOptions] [password | options for post request] - * @param {[{}]} [optionsOrRedirectUri] [options | redirectUri overwrite]] - * @param {[String]} [redirectUri] [optional redirectUri overwrite] - * - * @return {Promise|Promise} Server response as Object - */ - login(emailOrCredentials?: any, passwordOrOptions?: any, optionsOrRedirectUri?: any, redirectUri?: any): any; - - /** - * logout locally and redirect to redirectUri (if set) or redirectUri of config. Sends logout request first, if set in config - * - * @param {[String]} [redirectUri] [optional redirectUri overwrite] - * - * @return {Promise<>|Promise|Promise} Server response as Object - */ - logout(redirectUri?: any): any; - - /** - * Authenticate with third-party and redirect to redirectUri (if set) or redirectUri of config - * - * @param {String} name Name of the provider - * @param {[String]} [redirectUri] [optional redirectUri overwrite] - * @param {[{}]} [userData] [optional userData for the local authentication server] - * - * @return {Promise|Promise} Server response as Object - */ - authenticate(name?: any, redirectUri?: any, userData?: any): any; - - /** - * Unlink third-party - * - * @param {String} name Name of the provider - * - * @return {Promise|Promise} Server response as Object - */ - unlink(name?: any, redirectUri?: any): any; - } - export class BaseConfig { - - /** - * Prepends baseUrl to a given url - * @param {String} url The relative url to append - * @return {String} joined baseUrl and url - */ - joinBase(url?: any): any; - - /** - * Merge current settings with incomming settings - * @param {Object} incomming Settings object to be merged into the current configuration - * @return {Config} this - */ - configure(incomming?: any): any; - - /* ----------- default config ----------- */ - // Used internally. The used Rest instance; set during configuration (see index.js) - client: any; - - // If using aurelia-api: - // ===================== - // This is the name of the endpoint used for any requests made in relation to authentication (login, logout, etc.). An empty string selects the default endpoint of aurelia-api. - endpoint: any; - - // When authenticated, these endpoints will have the token added to the header of any requests (for authorization). Accepts an array of endpoint names. An empty string selects the default endpoint of aurelia-api. - configureEndpoints: any; - - // SPA related options - // =================== - // The SPA url to which the user is redirected after a successful login - loginRedirect: any; - - // The SPA url to which the user is redirected after a successful logout - logoutRedirect: any; - - // The SPA route used when an unauthenticated user tries to access an SPA page that requires authentication - loginRoute: any; - - // Whether or not an authentication token is provided in the response to a successful signup - loginOnSignup: any; - - // If loginOnSignup == false: The SPA url to which the user is redirected after a successful signup (else loginRedirect is used) - signupRedirect: any; - - // redirect when token expires. 0 = don't redirect (default), 1 = use logoutRedirect, string = redirect there - expiredRedirect: any; - - // API related options - // =================== - // The base url used for all authentication related requests, including provider.url below. - // This appends to the httpClient/endpoint base url, it does not override it. - baseUrl: any; - - // The API endpoint to which login requests are sent - loginUrl: any; - - // The API endpoint to which logout requests are sent (not needed for jwt) - logoutUrl: any; - - // The HTTP method used for 'unlink' requests (Options: 'get' or 'post') - logoutMethod: any; - - // The API endpoint to which signup requests are sent - signupUrl: any; - - // The API endpoint used in profile requests (inc. `find/get` and `update`) - profileUrl: any; - - // The method used to update the profile ('put' or 'patch') - profileMethod: any; - - // The API endpoint used with oAuth to unlink authentication - unlinkUrl: any; - - // The HTTP method used for 'unlink' requests (Options: 'get' or 'post') - unlinkMethod: any; - - // The API endpoint to which refreshToken requests are sent. null = loginUrl - refreshTokenUrl: any; - - // Token Options - // ============= - // The header property used to contain the authToken in the header of API requests that require authentication - authHeader: any; - - // The token name used in the header of API requests that require authentication - authTokenType: any; - - // The the property from which to get the access token after a successful login or signup. Can also be dotted eg "accessTokenProp.accessTokenName" - accessTokenProp: any; - - // If the property defined by `accessTokenProp` is an object: - // ------------------------------------------------------------ - //This is the property from which to get the token `{ "accessTokenProp": { "accessTokenName" : '...' } }` - accessTokenName: any; - - // This allows the token to be a further object deeper `{ "accessTokenProp": { "accessTokenRoot" : { "accessTokenName" : '...' } } }` - accessTokenRoot: any; - - // Refresh Token Options - // ===================== - // Option to turn refresh tokens On/Off - useRefreshToken: any; - - // The option to enable/disable the automatic refresh of Auth tokens using Refresh Tokens - autoUpdateToken: any; - - // Oauth Client Id - clientId: any; - - // The the property from which to get the refresh token after a successful token refresh. Can also be dotted eg "refreshTokenProp.refreshTokenProp" - refreshTokenProp: any; - - // If the property defined by `refreshTokenProp` is an object: - // ----------------------------------------------------------- - // This is the property from which to get the token `{ "refreshTokenProp": { "refreshTokenName" : '...' } }` - refreshTokenName: any; - - // This allows the refresh token to be a further object deeper `{ "refreshTokenProp": { "refreshTokenRoot" : { "refreshTokenName" : '...' } } }` - refreshTokenRoot: any; - - // Miscellaneous Options - // ===================== - // Whether to enable the fetch interceptor which automatically adds the authentication headers - // (or not... e.g. if using a session based API or you want to override the default behaviour) - httpInterceptor: any; - - // For OAuth only: Tell the API whether or not to include token cookies in the response (for session based APIs) - withCredentials: any; - - // Controls how the popup is shown for different devices (Options: 'browser' or 'mobile') - platform: any; - - // Determines the `PLATFORM` property name upon which aurelia-authentication data is stored (Default: `PLATFORM.localStorage`) - storage: any; - - // The key used for storing the authentication response locally - storageKey: any; - - // List of value-converters to make global - globalValueConverters: any; - - //OAuth provider specific related configuration - // ============================================ - providers: any; - - /* deprecated defaults */ - _authToken: any; - _responseTokenProp: any; - _tokenName: any; - _tokenRoot: any; - _tokenPrefix: any; - authToken: any; - responseTokenProp: any; - tokenRoot: any; - tokenName: any; - tokenPrefix: any; - current: any; - _current: any; - } - export class FetchConfig { - - /** - * Construct the FetchConfig - * - * @param {HttpClient} httpClient - * @param {Config} clientConfig - * @param {Authentication} authService - * @param {BaseConfig} config - */ - constructor(httpClient?: any, clientConfig?: any, authService?: any, config?: any); - - /** - * Interceptor for HttpClient - * - * @return {{request: Function, response: Function}} - */ - interceptor: any; - - /** - * Configure client(s) with authorization interceptor - * - * @param {HttpClient|Rest|string[]} client HttpClient, rest client or api endpoint name, or an array thereof - * - * @return {HttpClient[]} - */ - configure(client?: any): any; - } - export class OAuth1 { - constructor(storage?: any, popup?: any, config?: any); - open(options?: any, userData?: any): any; - exchangeForToken(oauthData?: any, userData?: any, provider?: any): any; - } - export class OAuth2 { - constructor(storage?: any, popup?: any, config?: any); - open(options?: any, userData?: any): any; - exchangeForToken(oauthData?: any, userData?: any, provider?: any): any; - buildQuery(provider?: any): any; - } - export class Popup { - constructor(); - open(url?: any, windowName?: any, options?: any): any; - eventListener(redirectUri?: any): any; - pollPopup(): any; - } - export class Storage { - constructor(config?: any); - get(key?: any): any; - set(key?: any, value?: any): any; - remove(key?: any): any; - } -} \ No newline at end of file diff --git a/dist/commonjs/aurelia-authentication.js b/dist/commonjs/aurelia-authentication.js index 012d74a..b4fc952 100644 --- a/dist/commonjs/aurelia-authentication.js +++ b/dist/commonjs/aurelia-authentication.js @@ -3,46 +3,48 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.configure = exports.FetchConfig = exports.AuthorizeStep = exports.AuthenticateStep = exports.AuthService = exports.Authentication = exports.OAuth2 = exports.OAuth1 = exports.Auth0Lock = exports.Storage = exports.BaseConfig = exports.Popup = undefined; - -var _dec, _class2, _dec2, _class3, _dec3, _class4, _dec4, _class5, _dec5, _dec6, _dec7, _dec8, _dec9, _dec10, _dec11, _class6, _desc, _value, _class7, _dec12, _dec13, _class8, _desc2, _value2, _class9, _dec14, _class11, _dec15, _class12, _dec16, _class13; - -var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; +exports.Storage = exports.Popup = exports.OAuth2 = exports.OAuth1 = exports.FetchConfig = exports.BaseConfig = exports.AuthService = exports.AuthorizeStep = exports.Authentication = exports.AuthenticateStep = exports.Auth0Lock = undefined; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); -var _extend = require('extend'); +var _dec, _class, _dec2, _class2, _dec3, _dec4, _dec5, _dec6, _dec7, _dec8, _dec9, _class3, _desc, _value, _class4, _dec10, _class5, _dec11, _dec12, _class6, _desc2, _value2, _class7, _dec13, _class10, _dec14, _class11, _dec15, _class12, _dec16, _class13; -var _extend2 = _interopRequireDefault(_extend); +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; + +exports.configure = configure; var _aureliaLogging = require('aurelia-logging'); var LogManager = _interopRequireWildcard(_aureliaLogging); +var _extend = require('extend'); + +var _extend2 = _interopRequireDefault(_extend); + var _jwtDecode = require('jwt-decode'); var _jwtDecode2 = _interopRequireDefault(_jwtDecode); var _aureliaPal = require('aurelia-pal'); -var _aureliaPath = require('aurelia-path'); +var _aureliaFetchClient = require('aurelia-fetch-client'); -var _aureliaDependencyInjection = require('aurelia-dependency-injection'); +var _aureliaApi = require('aurelia-api'); -var _aureliaMetadata = require('aurelia-metadata'); +var _aureliaDependencyInjection = require('aurelia-dependency-injection'); var _aureliaRouter = require('aurelia-router'); -var _aureliaFetchClient = require('aurelia-fetch-client'); +var _aureliaMetadata = require('aurelia-metadata'); -var _aureliaApi = require('aurelia-api'); +var _aureliaPath = require('aurelia-path'); require('./authFilterValueConverter'); -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object['ke' + 'ys'](descriptor).forEach(function (key) { @@ -72,1356 +74,1351 @@ function _applyDecoratedDescriptor(target, property, decorators, descriptor, con return desc; } -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } -var Popup = exports.Popup = function () { - function Popup() { - _classCallCheck(this, Popup); - this.popupWindow = null; - this.polling = null; - this.url = ''; +function configure(aurelia, config) { + if (!_aureliaPal.PLATFORM.location.origin) { + _aureliaPal.PLATFORM.location.origin = _aureliaPal.PLATFORM.location.protocol + '//' + _aureliaPal.PLATFORM.location.hostname + (_aureliaPal.PLATFORM.location.port ? ':' + _aureliaPal.PLATFORM.location.port : ''); } - Popup.prototype.open = function open(url, windowName, options) { - this.url = url; - var optionsString = buildPopupWindowOptions(options || {}); + var baseConfig = aurelia.container.get(BaseConfig); - this.popupWindow = _aureliaPal.PLATFORM.global.open(url, windowName, optionsString); + if (typeof config === 'function') { + config(baseConfig); + } else if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') { + baseConfig.configure(config); + } - if (this.popupWindow && this.popupWindow.focus) { - this.popupWindow.focus(); + for (var _iterator = baseConfig.globalValueConverters, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; + + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; } - return this; - }; + var converter = _ref; - Popup.prototype.eventListener = function eventListener(redirectUri) { - var _this = this; + aurelia.globalResources('./' + converter); + LogManager.getLogger('authentication').info('Add globalResources value-converter: ' + converter); + } + var fetchConfig = aurelia.container.get(FetchConfig); + var clientConfig = aurelia.container.get(_aureliaApi.Config); - return new Promise(function (resolve, reject) { - _this.popupWindow.addEventListener('loadstart', function (event) { - if (event.url.indexOf(redirectUri) !== 0) { - return; - } + if (Array.isArray(baseConfig.configureEndpoints)) { + baseConfig.configureEndpoints.forEach(function (endpointToPatch) { + fetchConfig.configure(endpointToPatch); + }); + } - var parser = _aureliaPal.DOM.createElement('a'); - parser.href = event.url; + var client = void 0; - if (parser.search || parser.hash) { - var qs = parseUrl(parser); + if (baseConfig.endpoint !== null) { + if (typeof baseConfig.endpoint === 'string') { + var endpoint = clientConfig.getEndpoint(baseConfig.endpoint); + if (!endpoint) { + throw new Error('There is no \'' + (baseConfig.endpoint || 'default') + '\' endpoint registered.'); + } + client = endpoint; + } else if (baseConfig.endpoint instanceof _aureliaFetchClient.HttpClient) { + client = new _aureliaApi.Rest(baseConfig.endpoint); + } + } - if (qs.error) { - reject({ error: qs.error }); - } else { - resolve(qs); - } + if (!(client instanceof _aureliaApi.Rest)) { + client = new _aureliaApi.Rest(aurelia.container.get(_aureliaFetchClient.HttpClient)); + } - _this.popupWindow.close(); - } - }); + baseConfig.client = client; +} - _this.popupWindow.addEventListener('exit', function () { - reject({ data: 'Provider Popup was closed' }); - }); +var Auth0Lock = exports.Auth0Lock = (_dec = (0, _aureliaDependencyInjection.inject)(Storage, BaseConfig), _dec(_class = function () { + function Auth0Lock(storage, config) { + _classCallCheck(this, Auth0Lock); - _this.popupWindow.addEventListener('loaderror', function () { - reject({ data: 'Authorization Failed' }); - }); - }); - }; + 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' + }; + } - Popup.prototype.pollPopup = function pollPopup() { - var _this2 = this; + Auth0Lock.prototype.open = function open(options, userData) { + var _this = this; - return new Promise(function (resolve, reject) { - _this2.polling = _aureliaPal.PLATFORM.global.setInterval(function () { - var errorData = void 0; + if (typeof _aureliaPal.PLATFORM.global.Auth0Lock !== 'function') { + throw new Error('Auth0Lock was not found in global scope. Please load it before using this provider.'); + } + var provider = (0, _extend2.default)(true, {}, this.defaults, options); + var stateName = provider.name + '_state'; - try { - if (_this2.popupWindow.location.host === _aureliaPal.PLATFORM.global.document.location.host && (_this2.popupWindow.location.search || _this2.popupWindow.location.hash)) { - var qs = parseUrl(_this2.popupWindow.location); + if (typeof provider.state === 'function') { + this.storage.set(stateName, provider.state()); + } else if (typeof provider.state === 'string') { + this.storage.set(stateName, provider.state); + } - if (qs.error) { - reject({ error: qs.error }); - } else { - resolve(qs); - } + this.lock = this.lock || new _aureliaPal.PLATFORM.global.Auth0Lock(provider.clientId, provider.clientDomain); - _this2.popupWindow.close(); - _aureliaPal.PLATFORM.global.clearInterval(_this2.polling); - } - } catch (error) { - errorData = error; - } + var openPopup = new Promise(function (resolve, reject) { + var 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'); - if (!_this2.popupWindow) { - _aureliaPal.PLATFORM.global.clearInterval(_this2.polling); - reject({ - error: errorData, - data: 'Provider Popup Blocked' - }); - } else if (_this2.popupWindow.closed) { - _aureliaPal.PLATFORM.global.clearInterval(_this2.polling); - reject({ - error: errorData, - data: 'Problem poll popup' + _this.lock.show(provider.lockOptions, function (err, profile, tokenOrCode) { + if (err) { + reject(err); + } else { + resolve({ + access_token: tokenOrCode }); } - }, 35); + }); + }); + + return openPopup.then(function (lockResponse) { + if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { + return lockResponse; + } + + throw new Error('Only `token` responseType is supported'); }); }; - return Popup; -}(); + return Auth0Lock; +}()) || _class); +var AuthenticateStep = exports.AuthenticateStep = (_dec2 = (0, _aureliaDependencyInjection.inject)(AuthService), _dec2(_class2 = function () { + function AuthenticateStep(authService) { + -var buildPopupWindowOptions = function buildPopupWindowOptions(options) { - var width = options.width || 500; - var height = options.height || 500; + this.authService = authService; + } - var extended = (0, _extend2.default)({ - width: width, - height: height, - left: _aureliaPal.PLATFORM.global.screenX + (_aureliaPal.PLATFORM.global.outerWidth - width) / 2, - top: _aureliaPal.PLATFORM.global.screenY + (_aureliaPal.PLATFORM.global.outerHeight - height) / 2.5 - }, options); + AuthenticateStep.prototype.run = function run(routingContext, next) { + var isLoggedIn = this.authService.authenticated; + var loginRoute = this.authService.config.loginRoute; - var parts = []; - Object.keys(extended).map(function (key) { - return parts.push(key + '=' + extended[key]); - }); + if (routingContext.getAllInstructions().some(function (route) { + return route.config.auth === true; + })) { + if (!isLoggedIn) { + return next.cancel(new _aureliaRouter.Redirect(loginRoute)); + } + } else if (isLoggedIn && routingContext.getAllInstructions().some(function (route) { + return route.fragment === loginRoute; + })) { + return next.cancel(new _aureliaRouter.Redirect(this.authService.config.loginRedirect)); + } - return parts.join(','); -}; + return next(); + }; -var parseUrl = function parseUrl(url) { - return (0, _extend2.default)(true, {}, (0, _aureliaPath.parseQueryString)(url.search), (0, _aureliaPath.parseQueryString)(url.hash)); -}; + return AuthenticateStep; +}()) || _class2); +var Authentication = exports.Authentication = (_dec3 = (0, _aureliaDependencyInjection.inject)(Storage, BaseConfig, OAuth1, OAuth2, Auth0Lock), _dec4 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.loginRoute instead.' }), _dec5 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.loginRedirect instead.' }), _dec6 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.joinBase(baseConfig.loginUrl) instead.' }), _dec7 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.joinBase(baseConfig.signupUrl) instead.' }), _dec8 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.joinBase(baseConfig.profileUrl) instead.' }), _dec9 = (0, _aureliaMetadata.deprecated)({ message: 'Use .getAccessToken() instead.' }), _dec3(_class3 = (_class4 = function () { + function Authentication(storage, config, oAuth1, oAuth2, auth0Lock) { + -var BaseConfig = exports.BaseConfig = function () { - function BaseConfig() { - _classCallCheck(this, BaseConfig); + this.storage = storage; + this.config = config; + this.oAuth1 = oAuth1; + this.oAuth2 = oAuth2; + this.auth0Lock = auth0Lock; + this.updateTokenCallstack = []; + this.accessToken = null; + this.refreshToken = null; + this.payload = null; + this.exp = null; + this.hasDataStored = false; + } - this.client = null; - this.endpoint = null; - this.configureEndpoints = null; - this.loginRedirect = '#/'; - this.logoutRedirect = '#/'; - this.loginRoute = '/login'; - this.loginOnSignup = true; - this.signupRedirect = '#/login'; - this.expiredRedirect = 0; - this.baseUrl = ''; - this.loginUrl = '/auth/login'; - this.logoutUrl = null; - this.logoutMethod = 'get'; - this.signupUrl = '/auth/signup'; - this.profileUrl = '/auth/me'; - this.profileMethod = 'put'; - this.unlinkUrl = '/auth/unlink/'; - this.unlinkMethod = 'get'; - this.refreshTokenUrl = null; - this.authHeader = 'Authorization'; - this.authTokenType = 'Bearer'; - this.accessTokenProp = 'access_token'; - this.accessTokenName = 'token'; - this.accessTokenRoot = false; - this.useRefreshToken = false; - this.autoUpdateToken = true; - this.clientId = false; - this.refreshTokenProp = 'refresh_token'; - this.refreshTokenName = 'token'; - this.refreshTokenRoot = false; - this.httpInterceptor = true; - this.withCredentials = true; - this.platform = 'browser'; - this.storage = 'localStorage'; - this.storageKey = 'aurelia_authentication'; - this.globalValueConverters = ['authFilterValueConverter']; - this.providers = { - facebook: { - name: 'facebook', - url: '/auth/facebook', - authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', - redirectUri: _aureliaPal.PLATFORM.location.origin + '/', - requiredUrlParams: ['display', 'scope'], - scope: ['email'], - scopeDelimiter: ',', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 580, height: 400 } - }, - google: { - name: 'google', - url: '/auth/google', - authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth', - redirectUri: _aureliaPal.PLATFORM.location.origin, - requiredUrlParams: ['scope'], - optionalUrlParams: ['display', 'state'], - scope: ['profile', 'email'], - scopePrefix: 'openid', - scopeDelimiter: ' ', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 452, height: 633 }, - state: randomState - }, - github: { - name: 'github', - url: '/auth/github', - authorizationEndpoint: 'https://github.com/login/oauth/authorize', - redirectUri: _aureliaPal.PLATFORM.location.origin, - optionalUrlParams: ['scope'], - scope: ['user:email'], - scopeDelimiter: ' ', - oauthType: '2.0', - popupOptions: { width: 1020, height: 618 } - }, - instagram: { - name: 'instagram', - url: '/auth/instagram', - authorizationEndpoint: 'https://api.instagram.com/oauth/authorize', - redirectUri: _aureliaPal.PLATFORM.location.origin, - requiredUrlParams: ['scope'], - scope: ['basic'], - scopeDelimiter: '+', - oauthType: '2.0' - }, - linkedin: { - name: 'linkedin', - url: '/auth/linkedin', - authorizationEndpoint: 'https://www.linkedin.com/uas/oauth2/authorization', - redirectUri: _aureliaPal.PLATFORM.location.origin, - requiredUrlParams: ['state'], - scope: ['r_emailaddress'], - scopeDelimiter: ' ', - state: 'STATE', - oauthType: '2.0', - popupOptions: { width: 527, height: 582 } - }, - twitter: { - name: 'twitter', - url: '/auth/twitter', - authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', - redirectUri: _aureliaPal.PLATFORM.location.origin, - oauthType: '1.0', - popupOptions: { width: 495, height: 645 } - }, - twitch: { - name: 'twitch', - url: '/auth/twitch', - authorizationEndpoint: 'https://api.twitch.tv/kraken/oauth2/authorize', - redirectUri: _aureliaPal.PLATFORM.location.origin, - requiredUrlParams: ['scope'], - scope: ['user_read'], - scopeDelimiter: ' ', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 500, height: 560 } - }, - live: { - name: 'live', - url: '/auth/live', - authorizationEndpoint: 'https://login.live.com/oauth20_authorize.srf', - redirectUri: _aureliaPal.PLATFORM.location.origin, - requiredUrlParams: ['display', 'scope'], - scope: ['wl.emails'], - scopeDelimiter: ' ', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 500, height: 560 } - }, - yahoo: { - name: 'yahoo', - url: '/auth/yahoo', - authorizationEndpoint: 'https://api.login.yahoo.com/oauth2/request_auth', - redirectUri: _aureliaPal.PLATFORM.location.origin, - scope: [], - scopeDelimiter: ',', - oauthType: '2.0', - popupOptions: { width: 559, height: 519 } - }, - bitbucket: { - name: 'bitbucket', - url: '/auth/bitbucket', - authorizationEndpoint: 'https://bitbucket.org/site/oauth2/authorize', - redirectUri: _aureliaPal.PLATFORM.location.origin + '/', - requiredUrlParams: ['scope'], - scope: ['email'], - 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: randomState - } - }; - this._authToken = 'Bearer'; - this._responseTokenProp = 'access_token'; - this._tokenName = 'token'; - this._tokenRoot = false; - this._tokenPrefix = 'aurelia'; - } + Authentication.prototype.getLoginRoute = function getLoginRoute() { + return this.config.loginRoute; + }; - BaseConfig.prototype.joinBase = function joinBase(url) { - return (0, _aureliaPath.join)(this.baseUrl, url); + Authentication.prototype.getLoginRedirect = function getLoginRedirect() { + return this.config.loginRedirect; }; - BaseConfig.prototype.configure = function configure(incomming) { - for (var key in incomming) { - var value = incomming[key]; - if (value !== undefined) { - if (Array.isArray(value) || (typeof value === 'undefined' ? 'undefined' : _typeof(value)) !== 'object' || value === null) { - this[key] = value; - } else { - (0, _extend2.default)(true, this[key], value); - } - } - } + Authentication.prototype.getLoginUrl = function getLoginUrl() { + return this.Config.joinBase(this.config.loginUrl); }; - _createClass(BaseConfig, [{ - key: 'authToken', - set: function set(authToken) { - LogManager.getLogger('authentication').warn('BaseConfig.authToken is deprecated. Use BaseConfig.authTokenType instead.'); - this._authTokenType = authToken; - this.authTokenType = authToken; - return authToken; - }, - get: function get() { - return this._authTokenType; - } - }, { - key: 'responseTokenProp', - set: function set(responseTokenProp) { - LogManager.getLogger('authentication').warn('BaseConfig.responseTokenProp is deprecated. Use BaseConfig.accessTokenProp instead.'); - this._responseTokenProp = responseTokenProp; - this.accessTokenProp = responseTokenProp; - return responseTokenProp; - }, - get: function get() { - return this._responseTokenProp; - } - }, { - key: 'tokenRoot', - set: function set(tokenRoot) { - LogManager.getLogger('authentication').warn('BaseConfig.tokenRoot is deprecated. Use BaseConfig.accessTokenRoot instead.'); - this._tokenRoot = tokenRoot; - this.accessTokenRoot = tokenRoot; - return tokenRoot; - }, - get: function get() { - return this._tokenRoot; - } - }, { - key: 'tokenName', - set: function set(tokenName) { - LogManager.getLogger('authentication').warn('BaseConfig.tokenName is deprecated. Use BaseConfig.accessTokenName instead.'); - this._tokenName = tokenName; - this.accessTokenName = tokenName; - return tokenName; - }, - get: function get() { - return this._tokenName; + Authentication.prototype.getSignupUrl = function getSignupUrl() { + return this.Config.joinBase(this.config.signupUrl); + }; + + Authentication.prototype.getProfileUrl = function getProfileUrl() { + return this.Config.joinBase(this.config.profileUrl); + }; + + Authentication.prototype.getToken = function getToken() { + return this.getAccessToken(); + }; + + Authentication.prototype.getResponseObject = function getResponseObject() { + return JSON.parse(this.storage.get(this.config.storageKey)); + }; + + Authentication.prototype.setResponseObject = function setResponseObject(response) { + if (response) { + this.getDataFromResponse(response); + this.storage.set(this.config.storageKey, JSON.stringify(response)); + return; } - }, { - key: 'tokenPrefix', - set: function set(tokenPrefix) { - LogManager.getLogger('authentication').warn('BaseConfig.tokenPrefix is obsolete. Use BaseConfig.storageKey instead.'); - this._tokenPrefix = tokenPrefix; - return tokenPrefix; - }, - get: function get() { - return this._tokenPrefix || 'aurelia'; + this.accessToken = null; + this.refreshToken = null; + this.payload = null; + this.exp = null; + + this.hasDataStored = false; + + this.storage.remove(this.config.storageKey); + }; + + Authentication.prototype.getAccessToken = function getAccessToken() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.accessToken; + }; + + Authentication.prototype.getRefreshToken = function getRefreshToken() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.refreshToken; + }; + + Authentication.prototype.getPayload = function getPayload() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.payload; + }; + + Authentication.prototype.getExp = function getExp() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.exp; + }; + + Authentication.prototype.getTtl = function getTtl() { + var exp = this.getExp(); + return Number.isNaN(exp) ? NaN : exp - Math.round(new Date().getTime() / 1000); + }; + + Authentication.prototype.isTokenExpired = function isTokenExpired() { + var timeLeft = this.getTtl(); + return Number.isNaN(timeLeft) ? undefined : timeLeft < 0; + }; + + Authentication.prototype.isAuthenticated = function isAuthenticated() { + var isTokenExpired = this.isTokenExpired(); + if (isTokenExpired === undefined) return this.accessToken ? true : false; + return !isTokenExpired; + }; + + Authentication.prototype.getDataFromResponse = function getDataFromResponse(response) { + var config = this.config; + + this.accessToken = this.getTokenFromResponse(response, config.accessTokenProp, config.accessTokenName, config.accessTokenRoot); + + this.refreshToken = null; + if (config.useRefreshToken) { + try { + this.refreshToken = this.getTokenFromResponse(response, config.refreshTokenProp, config.refreshTokenName, config.refreshTokenRoot); + } catch (e) { + this.refreshToken = null; + } } - }, { - key: 'current', - get: function get() { - LogManager.getLogger('authentication').warn('Getter BaseConfig.current is deprecated. Use BaseConfig directly instead.'); - return this; - }, - set: function set(_) { - throw new Error('Setter BaseConfig.current is obsolete. Use BaseConfig directly instead.'); + + this.payload = null; + + try { + this.payload = this.accessToken ? (0, _jwtDecode2.default)(this.accessToken) : null; + } catch (_) { + _; } - }, { - key: '_current', - get: function get() { - LogManager.getLogger('authentication').warn('Getter BaseConfig._current is deprecated. Use BaseConfig directly instead.'); - return this; - }, - set: function set(_) { - throw new Error('Setter BaseConfig._current is obsolete. Use BaseConfig directly instead.'); + + this.exp = this.payload ? parseInt(this.payload.exp, 10) : NaN; + + this.hasDataStored = true; + + return { + accessToken: this.accessToken, + refreshToken: this.refreshToken, + payload: this.payload, + exp: this.exp + }; + }; + + Authentication.prototype.getTokenFromResponse = function getTokenFromResponse(response, tokenProp, tokenName, tokenRoot) { + if (!response) return undefined; + + var responseTokenProp = tokenProp.split('.').reduce(function (o, x) { + return o[x]; + }, response); + + if (typeof responseTokenProp === 'string') { + return responseTokenProp; } - }]); - return BaseConfig; -}(); + if ((typeof responseTokenProp === 'undefined' ? 'undefined' : _typeof(responseTokenProp)) === 'object') { + var tokenRootData = tokenRoot && tokenRoot.split('.').reduce(function (o, x) { + return o[x]; + }, responseTokenProp); + var _token = tokenRootData ? tokenRootData[tokenName] : responseTokenProp[tokenName]; -function randomState() { - var rand = Math.random().toString(36).substr(2); - return encodeURIComponent(rand); -} + if (!_token) throw new Error('Token not found in response'); -var Storage = exports.Storage = (_dec = (0, _aureliaDependencyInjection.inject)(BaseConfig), _dec(_class2 = function () { - function Storage(config) { - _classCallCheck(this, Storage); + return _token; + } - this.config = config; - } + var token = response[tokenName] === undefined ? null : response[tokenName]; - Storage.prototype.get = function get(key) { - return _aureliaPal.PLATFORM.global[this.config.storage].getItem(key); + if (!token) throw new Error('Token not found in response'); + + return token; }; - Storage.prototype.set = function set(key, value) { - _aureliaPal.PLATFORM.global[this.config.storage].setItem(key, value); + Authentication.prototype.toUpdateTokenCallstack = function toUpdateTokenCallstack() { + var _this2 = this; + + return new Promise(function (resolve) { + return _this2.updateTokenCallstack.push(resolve); + }); }; - Storage.prototype.remove = function remove(key) { - _aureliaPal.PLATFORM.global[this.config.storage].removeItem(key); + Authentication.prototype.resolveUpdateTokenCallstack = function resolveUpdateTokenCallstack(response) { + this.updateTokenCallstack.map(function (resolve) { + return resolve(response); + }); + this.updateTokenCallstack = []; }; - return Storage; -}()) || _class2); -var Auth0Lock = exports.Auth0Lock = (_dec2 = (0, _aureliaDependencyInjection.inject)(Storage, BaseConfig), _dec2(_class3 = function () { - function Auth0Lock(storage, config) { - _classCallCheck(this, Auth0Lock); + Authentication.prototype.authenticate = function authenticate(name) { + var userData = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - 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' - }; - } + var oauthType = this.config.providers[name].type; - Auth0Lock.prototype.open = function open(options, userData) { - var _this3 = this; + if (oauthType) { + LogManager.getLogger('authentication').warn('DEPRECATED: Setting provider.type is deprecated and replaced by provider.oauthType'); + } else { + oauthType = this.config.providers[name].oauthType; + } - if (typeof _aureliaPal.PLATFORM.global.Auth0Lock !== 'function') { - throw new Error('Auth0Lock was not found in global scope. Please load it before using this provider.'); + var providerLogin = void 0; + if (oauthType === 'auth0-lock') { + providerLogin = this.auth0Lock; + } else { + providerLogin = oauthType === '1.0' ? this.oAuth1 : this.oAuth2; } - var provider = (0, _extend2.default)(true, {}, this.defaults, options); - var 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); + return providerLogin.open(this.config.providers[name], userData); + }; + + Authentication.prototype.redirect = function redirect(redirectUrl, defaultRedirectUrl) { + if (redirectUrl === true) { + LogManager.getLogger('authentication').warn('DEPRECATED: Setting redirectUrl === true to actually *not redirect* is deprecated. Set redirectUrl === 0 instead.'); + return; } - this.lock = this.lock || new _aureliaPal.PLATFORM.global.Auth0Lock(provider.clientId, provider.clientDomain); + if (redirectUrl === false) { + LogManager.getLogger('authentication').warn('BREAKING CHANGE: Setting redirectUrl === false to actually *do redirect* is deprecated. Set redirectUrl to undefined or null to use the defaultRedirectUrl if so desired.'); + } - var openPopup = new Promise(function (resolve, reject) { - var 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 = _this3.storage.get(provider.name + '_state'); + if (redirectUrl === 0) { + return; + } + if (typeof redirectUrl === 'string') { + _aureliaPal.PLATFORM.location.href = encodeURI(redirectUrl); + } else if (defaultRedirectUrl) { + _aureliaPal.PLATFORM.location.href = defaultRedirectUrl; + } + }; - _this3.lock.show(provider.lockOptions, function (err, profile, tokenOrCode) { - if (err) { - reject(err); - } else { - resolve({ - access_token: tokenOrCode - }); - } - }); - }); + _createClass(Authentication, [{ + key: 'responseObject', + get: function get() { + LogManager.getLogger('authentication').warn('Getter Authentication.responseObject is deprecated. Use Authentication.getResponseObject() instead.'); + return this.getResponseObject(); + }, + set: function set(response) { + LogManager.getLogger('authentication').warn('Setter Authentication.responseObject is deprecated. Use AuthServive.setResponseObject(response) instead.'); + this.setResponseObject(response); + } + }]); - return openPopup.then(function (lockResponse) { - if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { - return lockResponse; + return Authentication; +}(), (_applyDecoratedDescriptor(_class4.prototype, 'getLoginRoute', [_dec4], Object.getOwnPropertyDescriptor(_class4.prototype, 'getLoginRoute'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getLoginRedirect', [_dec5], Object.getOwnPropertyDescriptor(_class4.prototype, 'getLoginRedirect'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getLoginUrl', [_dec6], Object.getOwnPropertyDescriptor(_class4.prototype, 'getLoginUrl'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getSignupUrl', [_dec7], Object.getOwnPropertyDescriptor(_class4.prototype, 'getSignupUrl'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getProfileUrl', [_dec8], Object.getOwnPropertyDescriptor(_class4.prototype, 'getProfileUrl'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getToken', [_dec9], Object.getOwnPropertyDescriptor(_class4.prototype, 'getToken'), _class4.prototype)), _class4)) || _class3); +var AuthorizeStep = exports.AuthorizeStep = (_dec10 = (0, _aureliaDependencyInjection.inject)(AuthService), _dec10(_class5 = function () { + function AuthorizeStep(authService) { + + + LogManager.getLogger('authentication').warn('AuthorizeStep is deprecated. Use AuthenticationStep instead.'); + + this.authService = authService; + } + + AuthorizeStep.prototype.run = function run(routingContext, next) { + var isLoggedIn = this.authService.isAuthenticated(); + var loginRoute = this.authService.config.loginRoute; + + if (routingContext.getAllInstructions().some(function (route) { + return route.config.auth; + })) { + if (!isLoggedIn) { + return next.cancel(new _aureliaRouter.Redirect(loginRoute)); } + } else if (isLoggedIn && routingContext.getAllInstructions().some(function (route) { + return route.fragment === loginRoute; + })) { + return next.cancel(new _aureliaRouter.Redirect(this.authService.config.loginRedirect)); + } - throw new Error('Only `token` responseType is supported'); - }); + return next(); }; - return Auth0Lock; -}()) || _class3); -var OAuth1 = exports.OAuth1 = (_dec3 = (0, _aureliaDependencyInjection.inject)(Storage, Popup, BaseConfig), _dec3(_class4 = function () { - function OAuth1(storage, popup, config) { - _classCallCheck(this, OAuth1); + return AuthorizeStep; +}()) || _class5); +var AuthService = exports.AuthService = (_dec11 = (0, _aureliaDependencyInjection.inject)(Authentication, BaseConfig), _dec12 = (0, _aureliaMetadata.deprecated)({ message: 'Use .getAccessToken() instead.' }), _dec11(_class6 = (_class7 = function () { + function AuthService(authentication, config) { + - this.storage = storage; - this.config = config; - this.popup = popup; - this.defaults = { - url: null, - name: null, - popupOptions: null, - redirectUri: null, - authorizationEndpoint: null - }; - } + this.authenticated = false; + this.timeoutID = 0; - OAuth1.prototype.open = function open(options, userData) { - var _this4 = this; + this.authentication = authentication; + this.config = config; - var provider = (0, _extend2.default)(true, {}, this.defaults, options); - var serverUrl = this.config.joinBase(provider.url); + var oldStorageKey = config.tokenPrefix ? config.tokenPrefix + '_' + config.tokenName : config.tokenName; + var oldToken = authentication.storage.get(oldStorageKey); - if (this.config.platform !== 'mobile') { - this.popup = this.popup.open('', provider.name, provider.popupOptions); + if (oldToken) { + LogManager.getLogger('authentication').info('Found token with deprecated format in storage. Converting it to new format. No further action required.'); + var fakeOldResponse = {}; + fakeOldResponse[config.accessTokenProp] = oldToken; + this.setResponseObject(fakeOldResponse); + authentication.storage.remove(oldStorageKey); } - return this.config.client.post(serverUrl).then(function (response) { - var url = provider.authorizationEndpoint + '?' + (0, _aureliaPath.buildQueryString)(response); + this.setResponseObject(this.authentication.getResponseObject()); + } - if (_this4.config.platform === 'mobile') { - _this4.popup = _this4.popup.open(url, provider.name, provider.popupOptions); - } else { - _this4.popup.popupWindow.location = url; - } + AuthService.prototype.setTimeout = function setTimeout(ttl) { + var _this3 = this; - var popupListener = _this4.config.platform === 'mobile' ? _this4.popup.eventListener(provider.redirectUri) : _this4.popup.pollPopup(); + this.clearTimeout(); - return popupListener.then(function (result) { - return _this4.exchangeForToken(result, userData, provider); - }); - }); + this.timeoutID = _aureliaPal.PLATFORM.global.setTimeout(function () { + if (_this3.config.autoUpdateToken && _this3.authentication.getAccessToken() && _this3.authentication.getRefreshToken()) { + _this3.updateToken(); + } else { + _this3.logout(_this3.config.expiredRedirect); + } + }, ttl); }; - OAuth1.prototype.exchangeForToken = function exchangeForToken(oauthData, userData, provider) { - var data = (0, _extend2.default)(true, {}, userData, oauthData); - var serverUrl = this.config.joinBase(provider.url); - var credentials = this.config.withCredentials ? 'include' : 'same-origin'; - - return this.config.client.post(serverUrl, data, { credentials: credentials }); + AuthService.prototype.clearTimeout = function clearTimeout() { + if (this.timeoutID) { + _aureliaPal.PLATFORM.global.clearTimeout(this.timeoutID); + } + this.timeoutID = 0; }; - return OAuth1; -}()) || _class4); -var OAuth2 = exports.OAuth2 = (_dec4 = (0, _aureliaDependencyInjection.inject)(Storage, Popup, BaseConfig), _dec4(_class5 = function () { - function OAuth2(storage, popup, config) { - _classCallCheck(this, OAuth2); - - this.storage = storage; - this.config = config; - this.popup = popup; - this.defaults = { - url: null, - name: null, - state: null, - scope: null, - scopeDelimiter: null, - redirectUri: null, - popupOptions: null, - authorizationEndpoint: null, - responseParams: null, - requiredUrlParams: null, - optionalUrlParams: null, - defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], - responseType: 'code' - }; - } - - OAuth2.prototype.open = function open(options, userData) { - var _this5 = this; + AuthService.prototype.setResponseObject = function setResponseObject(response) { + this.clearTimeout(); - var provider = (0, _extend2.default)(true, {}, this.defaults, options); - var stateName = provider.name + '_state'; + this.authentication.setResponseObject(response); - if (typeof provider.state === 'function') { - this.storage.set(stateName, provider.state()); - } else if (typeof provider.state === 'string') { - this.storage.set(stateName, provider.state); + this.authenticated = this.authentication.isAuthenticated(); + if (this.authenticated && !Number.isNaN(this.authentication.exp)) { + this.setTimeout(this.getTtl() * 1000); } + }; - var url = provider.authorizationEndpoint + '?' + (0, _aureliaPath.buildQueryString)(this.buildQuery(provider)); - var popup = this.popup.open(url, provider.name, provider.popupOptions); - var openPopup = this.config.platform === 'mobile' ? popup.eventListener(provider.redirectUri) : popup.pollPopup(); - - return openPopup.then(function (oauthData) { - if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { - return oauthData; - } - if (oauthData.state && oauthData.state !== _this5.storage.get(stateName)) { - return Promise.reject('OAuth 2.0 state parameter mismatch.'); - } - return _this5.exchangeForToken(oauthData, userData, provider); - }); + AuthService.prototype.getMe = function getMe(criteriaOrId) { + if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { + criteriaOrId = { id: criteriaOrId }; + } + return this.client.find(this.config.joinBase(this.config.profileUrl), criteriaOrId); }; - OAuth2.prototype.exchangeForToken = function exchangeForToken(oauthData, userData, provider) { - var data = (0, _extend2.default)(true, {}, userData, { - clientId: provider.clientId, - redirectUri: provider.redirectUri - }, oauthData); + AuthService.prototype.updateMe = function updateMe(body, criteriaOrId) { + if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { + criteriaOrId = { id: criteriaOrId }; + } + if (this.config.profileMethod === 'put') { + return this.client.update(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); + } + return this.client.patch(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); + }; - var serverUrl = this.config.joinBase(provider.url); - var credentials = this.config.withCredentials ? 'include' : 'same-origin'; + AuthService.prototype.getAccessToken = function getAccessToken() { + return this.authentication.getAccessToken(); + }; - return this.config.client.post(serverUrl, data, { credentials: credentials }); + AuthService.prototype.getCurrentToken = function getCurrentToken() { + return this.getAccessToken(); }; - OAuth2.prototype.buildQuery = function buildQuery(provider) { - var _this6 = this; + AuthService.prototype.getRefreshToken = function getRefreshToken() { + return this.authentication.getRefreshToken(); + }; - var query = {}; - var urlParams = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams']; + AuthService.prototype.isAuthenticated = function isAuthenticated() { + var authenticated = this.authentication.isAuthenticated(); - urlParams.forEach(function (params) { - (provider[params] || []).forEach(function (paramName) { - var camelizedName = camelCase(paramName); - var paramValue = typeof provider[paramName] === 'function' ? provider[paramName]() : provider[camelizedName]; + if (!authenticated && this.config.autoUpdateToken && this.authentication.getAccessToken() && this.authentication.getRefreshToken()) { + this.updateToken(); + authenticated = true; + } - if (paramName === 'state') { - paramValue = encodeURIComponent(_this6.storage.get(provider.name + '_state')); - } + return authenticated; + }; - if (paramName === 'scope' && Array.isArray(paramValue)) { - paramValue = paramValue.join(provider.scopeDelimiter); + AuthService.prototype.getExp = function getExp() { + return this.authentication.getExp(); + }; - if (provider.scopePrefix) { - paramValue = provider.scopePrefix + provider.scopeDelimiter + paramValue; - } - } + AuthService.prototype.getTtl = function getTtl() { + return this.authentication.getTtl(); + }; - query[paramName] = paramValue; - }); - }); - return query; + AuthService.prototype.isTokenExpired = function isTokenExpired() { + return this.authentication.isTokenExpired(); }; - return OAuth2; -}()) || _class5); + AuthService.prototype.getTokenPayload = function getTokenPayload() { + return this.authentication.getPayload(); + }; + AuthService.prototype.updateToken = function updateToken() { + var _this4 = this; -var camelCase = function camelCase(name) { - return name.replace(/([\:\-\_]+(.))/g, function (_, separator, letter, offset) { - return offset ? letter.toUpperCase() : letter; - }); -}; + if (!this.authentication.getRefreshToken()) { + return Promise.reject(new Error('refreshToken not set')); + } -var Authentication = exports.Authentication = (_dec5 = (0, _aureliaDependencyInjection.inject)(Storage, BaseConfig, OAuth1, OAuth2, Auth0Lock), _dec6 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.loginRoute instead.' }), _dec7 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.loginRedirect instead.' }), _dec8 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.joinBase(baseConfig.loginUrl) instead.' }), _dec9 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.joinBase(baseConfig.signupUrl) instead.' }), _dec10 = (0, _aureliaMetadata.deprecated)({ message: 'Use baseConfig.joinBase(baseConfig.profileUrl) instead.' }), _dec11 = (0, _aureliaMetadata.deprecated)({ message: 'Use .getAccessToken() instead.' }), _dec5(_class6 = (_class7 = function () { - function Authentication(storage, config, oAuth1, oAuth2, auth0Lock) { - _classCallCheck(this, Authentication); + if (this.authentication.updateTokenCallstack.length === 0) { + var content = { + grant_type: 'refresh_token', + refresh_token: this.authentication.getRefreshToken(), + client_id: this.config.clientId ? this.config.clientId : undefined + }; - this.storage = storage; - this.config = config; - this.oAuth1 = oAuth1; - this.oAuth2 = oAuth2; - this.auth0Lock = auth0Lock; - this.updateTokenCallstack = []; - this.accessToken = null; - this.refreshToken = null; - this.payload = null; - this.exp = null; - this.hasDataStored = false; - } + this.client.post(this.config.joinBase(this.config.refreshTokenUrl ? this.config.refreshTokenUrl : this.config.loginUrl), content).then(function (response) { + _this4.setResponseObject(response); + _this4.authentication.resolveUpdateTokenCallstack(_this4.isAuthenticated()); + }).catch(function (err) { + _this4.setResponseObject(null); + _this4.authentication.resolveUpdateTokenCallstack(Promise.reject(err)); + }); + } - Authentication.prototype.getLoginRoute = function getLoginRoute() { - return this.config.loginRoute; + return this.authentication.toUpdateTokenCallstack(); }; - Authentication.prototype.getLoginRedirect = function getLoginRedirect() { - return this.config.loginRedirect; - }; + AuthService.prototype.signup = function signup(displayNameOrCredentials, emailOrOptions, passwordOrRedirectUri, options, redirectUri) { + var _this5 = this; - Authentication.prototype.getLoginUrl = function getLoginUrl() { - return this.Config.joinBase(this.config.loginUrl); - }; + var content = void 0; - Authentication.prototype.getSignupUrl = function getSignupUrl() { - return this.Config.joinBase(this.config.signupUrl); - }; + if (_typeof(arguments[0]) === 'object') { + content = arguments[0]; + options = arguments[1]; + redirectUri = arguments[2]; + } else { + content = { + 'displayName': displayNameOrCredentials, + 'email': emailOrOptions, + 'password': passwordOrRedirectUri + }; + } + return this.client.post(this.config.joinBase(this.config.signupUrl), content, options).then(function (response) { + if (_this5.config.loginOnSignup) { + _this5.setResponseObject(response); + } + _this5.authentication.redirect(redirectUri, _this5.config.signupRedirect); - Authentication.prototype.getProfileUrl = function getProfileUrl() { - return this.Config.joinBase(this.config.profileUrl); + return response; + }); }; - Authentication.prototype.getToken = function getToken() { - return this.getAccessToken(); - }; + AuthService.prototype.login = function login(emailOrCredentials, passwordOrOptions, optionsOrRedirectUri, redirectUri) { + var _this6 = this; - Authentication.prototype.getResponseObject = function getResponseObject() { - return JSON.parse(this.storage.get(this.config.storageKey)); - }; + var content = void 0; - Authentication.prototype.setResponseObject = function setResponseObject(response) { - if (response) { - this.getDataFromResponse(response); - this.storage.set(this.config.storageKey, JSON.stringify(response)); - return; + if (_typeof(arguments[0]) === 'object') { + content = arguments[0]; + optionsOrRedirectUri = arguments[1]; + redirectUri = arguments[2]; + } else { + content = { + 'email': emailOrCredentials, + 'password': passwordOrOptions + }; + optionsOrRedirectUri = optionsOrRedirectUri; + } + + if (this.config.clientId) { + content.client_id = this.config.clientId; } - this.accessToken = null; - this.refreshToken = null; - this.payload = null; - this.exp = null; - this.hasDataStored = false; + return this.client.post(this.config.joinBase(this.config.loginUrl), content, optionsOrRedirectUri).then(function (response) { + _this6.setResponseObject(response); - this.storage.remove(this.config.storageKey); - }; + _this6.authentication.redirect(redirectUri, _this6.config.loginRedirect); - Authentication.prototype.getAccessToken = function getAccessToken() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.accessToken; + return response; + }); }; - Authentication.prototype.getRefreshToken = function getRefreshToken() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.refreshToken; - }; + AuthService.prototype.logout = function logout(redirectUri) { + var _this7 = this; - Authentication.prototype.getPayload = function getPayload() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.payload; - }; + var localLogout = function localLogout(response) { + return new Promise(function (resolve) { + _this7.setResponseObject(null); - Authentication.prototype.getExp = function getExp() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.exp; - }; + _this7.authentication.redirect(redirectUri, _this7.config.logoutRedirect); - Authentication.prototype.getTtl = function getTtl() { - var exp = this.getExp(); - return Number.isNaN(exp) ? NaN : exp - Math.round(new Date().getTime() / 1000); - }; + if (typeof _this7.onLogout === 'function') { + _this7.onLogout(response); + } - Authentication.prototype.isTokenExpired = function isTokenExpired() { - var timeLeft = this.getTtl(); - return Number.isNaN(timeLeft) ? undefined : timeLeft < 0; - }; + resolve(response); + }); + }; - Authentication.prototype.isAuthenticated = function isAuthenticated() { - var isTokenExpired = this.isTokenExpired(); - if (isTokenExpired === undefined) return this.accessToken ? true : false; - return !isTokenExpired; + return this.config.logoutUrl ? this.client.request(this.config.logoutMethod, this.config.joinBase(this.config.logoutUrl)).then(localLogout) : localLogout(); }; - Authentication.prototype.getDataFromResponse = function getDataFromResponse(response) { - var config = this.config; + AuthService.prototype.authenticate = function authenticate(name, redirectUri) { + var _this8 = this; - this.accessToken = this.getTokenFromResponse(response, config.accessTokenProp, config.accessTokenName, config.accessTokenRoot); + var userData = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - this.refreshToken = null; - if (config.useRefreshToken) { - try { - this.refreshToken = this.getTokenFromResponse(response, config.refreshTokenProp, config.refreshTokenName, config.refreshTokenRoot); - } catch (e) { - this.refreshToken = null; - } - } + return this.authentication.authenticate(name, userData).then(function (response) { + _this8.setResponseObject(response); - this.payload = null; + _this8.authentication.redirect(redirectUri, _this8.config.loginRedirect); - try { - this.payload = this.accessToken ? (0, _jwtDecode2.default)(this.accessToken) : null; - } catch (_) { - _; + return response; + }); + }; + + AuthService.prototype.unlink = function unlink(name, redirectUri) { + var _this9 = this; + + var unlinkUrl = this.config.joinBase(this.config.unlinkUrl) + name; + return this.client.request(this.config.unlinkMethod, unlinkUrl).then(function (response) { + _this9.authentication.redirect(redirectUri); + + return response; + }); + }; + + _createClass(AuthService, [{ + key: 'client', + get: function get() { + return this.config.client; + } + }, { + key: 'auth', + get: function get() { + LogManager.getLogger('authentication').warn('AuthService.auth is deprecated. Use .authentication instead.'); + return this.authentication; } + }]); - this.exp = this.payload ? parseInt(this.payload.exp, 10) : NaN; + return AuthService; +}(), (_applyDecoratedDescriptor(_class7.prototype, 'getCurrentToken', [_dec12], Object.getOwnPropertyDescriptor(_class7.prototype, 'getCurrentToken'), _class7.prototype)), _class7)) || _class6); - this.hasDataStored = true; +var BaseConfig = exports.BaseConfig = function () { + function BaseConfig() { + - return { - accessToken: this.accessToken, - refreshToken: this.refreshToken, - payload: this.payload, - exp: this.exp + this.client = null; + this.endpoint = null; + this.configureEndpoints = null; + this.loginRedirect = '#/'; + this.logoutRedirect = '#/'; + this.loginRoute = '/login'; + this.loginOnSignup = true; + this.signupRedirect = '#/login'; + this.expiredRedirect = 0; + this.baseUrl = ''; + this.loginUrl = '/auth/login'; + this.logoutUrl = null; + this.logoutMethod = 'get'; + this.signupUrl = '/auth/signup'; + this.profileUrl = '/auth/me'; + this.profileMethod = 'put'; + this.unlinkUrl = '/auth/unlink/'; + this.unlinkMethod = 'get'; + this.refreshTokenUrl = null; + this.authHeader = 'Authorization'; + this.authTokenType = 'Bearer'; + this.accessTokenProp = 'access_token'; + this.accessTokenName = 'token'; + this.accessTokenRoot = false; + this.useRefreshToken = false; + this.autoUpdateToken = true; + this.clientId = false; + this.refreshTokenProp = 'refresh_token'; + this.refreshTokenName = 'token'; + this.refreshTokenRoot = false; + this.httpInterceptor = true; + this.withCredentials = true; + this.platform = 'browser'; + this.storage = 'localStorage'; + this.storageKey = 'aurelia_authentication'; + this.globalValueConverters = ['authFilterValueConverter']; + this.providers = { + facebook: { + name: 'facebook', + url: '/auth/facebook', + authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', + redirectUri: _aureliaPal.PLATFORM.location.origin + '/', + requiredUrlParams: ['display', 'scope'], + scope: ['email'], + scopeDelimiter: ',', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 580, height: 400 } + }, + google: { + name: 'google', + url: '/auth/google', + authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth', + redirectUri: _aureliaPal.PLATFORM.location.origin, + requiredUrlParams: ['scope'], + optionalUrlParams: ['display', 'state'], + scope: ['profile', 'email'], + scopePrefix: 'openid', + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 452, height: 633 }, + state: randomState + }, + github: { + name: 'github', + url: '/auth/github', + authorizationEndpoint: 'https://github.com/login/oauth/authorize', + redirectUri: _aureliaPal.PLATFORM.location.origin, + optionalUrlParams: ['scope'], + scope: ['user:email'], + scopeDelimiter: ' ', + oauthType: '2.0', + popupOptions: { width: 1020, height: 618 } + }, + instagram: { + name: 'instagram', + url: '/auth/instagram', + authorizationEndpoint: 'https://api.instagram.com/oauth/authorize', + redirectUri: _aureliaPal.PLATFORM.location.origin, + requiredUrlParams: ['scope'], + scope: ['basic'], + scopeDelimiter: '+', + oauthType: '2.0' + }, + linkedin: { + name: 'linkedin', + url: '/auth/linkedin', + authorizationEndpoint: 'https://www.linkedin.com/uas/oauth2/authorization', + redirectUri: _aureliaPal.PLATFORM.location.origin, + requiredUrlParams: ['state'], + scope: ['r_emailaddress'], + scopeDelimiter: ' ', + state: 'STATE', + oauthType: '2.0', + popupOptions: { width: 527, height: 582 } + }, + twitter: { + name: 'twitter', + url: '/auth/twitter', + authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', + redirectUri: _aureliaPal.PLATFORM.location.origin, + oauthType: '1.0', + popupOptions: { width: 495, height: 645 } + }, + twitch: { + name: 'twitch', + url: '/auth/twitch', + authorizationEndpoint: 'https://api.twitch.tv/kraken/oauth2/authorize', + redirectUri: _aureliaPal.PLATFORM.location.origin, + requiredUrlParams: ['scope'], + scope: ['user_read'], + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 500, height: 560 } + }, + live: { + name: 'live', + url: '/auth/live', + authorizationEndpoint: 'https://login.live.com/oauth20_authorize.srf', + redirectUri: _aureliaPal.PLATFORM.location.origin, + requiredUrlParams: ['display', 'scope'], + scope: ['wl.emails'], + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 500, height: 560 } + }, + yahoo: { + name: 'yahoo', + url: '/auth/yahoo', + authorizationEndpoint: 'https://api.login.yahoo.com/oauth2/request_auth', + redirectUri: _aureliaPal.PLATFORM.location.origin, + scope: [], + scopeDelimiter: ',', + oauthType: '2.0', + popupOptions: { width: 559, height: 519 } + }, + bitbucket: { + name: 'bitbucket', + url: '/auth/bitbucket', + authorizationEndpoint: 'https://bitbucket.org/site/oauth2/authorize', + redirectUri: _aureliaPal.PLATFORM.location.origin + '/', + requiredUrlParams: ['scope'], + scope: ['email'], + 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: randomState + } }; - }; - - Authentication.prototype.getTokenFromResponse = function getTokenFromResponse(response, tokenProp, tokenName, tokenRoot) { - if (!response) return undefined; - - var responseTokenProp = tokenProp.split('.').reduce(function (o, x) { - return o[x]; - }, response); - - if (typeof responseTokenProp === 'string') { - return responseTokenProp; - } - - if ((typeof responseTokenProp === 'undefined' ? 'undefined' : _typeof(responseTokenProp)) === 'object') { - var tokenRootData = tokenRoot && tokenRoot.split('.').reduce(function (o, x) { - return o[x]; - }, responseTokenProp); - var _token = tokenRootData ? tokenRootData[tokenName] : responseTokenProp[tokenName]; - - if (!_token) throw new Error('Token not found in response'); - - return _token; - } - - var token = response[tokenName] === undefined ? null : response[tokenName]; - - if (!token) throw new Error('Token not found in response'); - - return token; - }; - - Authentication.prototype.toUpdateTokenCallstack = function toUpdateTokenCallstack() { - var _this7 = this; + this._authToken = 'Bearer'; + this._responseTokenProp = 'access_token'; + this._tokenName = 'token'; + this._tokenRoot = false; + this._tokenPrefix = 'aurelia'; + } - return new Promise(function (resolve) { - return _this7.updateTokenCallstack.push(resolve); - }); + BaseConfig.prototype.joinBase = function joinBase(url) { + return (0, _aureliaPath.join)(this.baseUrl, url); }; - Authentication.prototype.resolveUpdateTokenCallstack = function resolveUpdateTokenCallstack(response) { - this.updateTokenCallstack.map(function (resolve) { - return resolve(response); - }); - this.updateTokenCallstack = []; + BaseConfig.prototype.configure = function configure(incomming) { + for (var key in incomming) { + var value = incomming[key]; + if (value !== undefined) { + if (Array.isArray(value) || (typeof value === 'undefined' ? 'undefined' : _typeof(value)) !== 'object' || value === null) { + this[key] = value; + } else { + (0, _extend2.default)(true, this[key], value); + } + } + } }; - Authentication.prototype.authenticate = function authenticate(name) { - var userData = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - - var oauthType = this.config.providers[name].type; - - if (oauthType) { - LogManager.getLogger('authentication').warn('DEPRECATED: Setting provider.type is deprecated and replaced by provider.oauthType'); - } else { - oauthType = this.config.providers[name].oauthType; + _createClass(BaseConfig, [{ + key: 'authToken', + set: function set(authToken) { + LogManager.getLogger('authentication').warn('BaseConfig.authToken is deprecated. Use BaseConfig.authTokenType instead.'); + this._authTokenType = authToken; + this.authTokenType = authToken; + return authToken; + }, + get: function get() { + return this._authTokenType; } - - var providerLogin = void 0; - if (oauthType === 'auth0-lock') { - providerLogin = this.auth0Lock; - } else { - providerLogin = oauthType === '1.0' ? this.oAuth1 : this.oAuth2; + }, { + key: 'responseTokenProp', + set: function set(responseTokenProp) { + LogManager.getLogger('authentication').warn('BaseConfig.responseTokenProp is deprecated. Use BaseConfig.accessTokenProp instead.'); + this._responseTokenProp = responseTokenProp; + this.accessTokenProp = responseTokenProp; + return responseTokenProp; + }, + get: function get() { + return this._responseTokenProp; } - - return providerLogin.open(this.config.providers[name], userData); - }; - - Authentication.prototype.redirect = function redirect(redirectUrl, defaultRedirectUrl) { - if (redirectUrl === true) { - LogManager.getLogger('authentication').warn('DEPRECATED: Setting redirectUrl === true to actually *not redirect* is deprecated. Set redirectUrl === 0 instead.'); - return; + }, { + key: 'tokenRoot', + set: function set(tokenRoot) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenRoot is deprecated. Use BaseConfig.accessTokenRoot instead.'); + this._tokenRoot = tokenRoot; + this.accessTokenRoot = tokenRoot; + return tokenRoot; + }, + get: function get() { + return this._tokenRoot; } - - if (redirectUrl === false) { - LogManager.getLogger('authentication').warn('BREAKING CHANGE: Setting redirectUrl === false to actually *do redirect* is deprecated. Set redirectUrl to undefined or null to use the defaultRedirectUrl if so desired.'); + }, { + key: 'tokenName', + set: function set(tokenName) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenName is deprecated. Use BaseConfig.accessTokenName instead.'); + this._tokenName = tokenName; + this.accessTokenName = tokenName; + return tokenName; + }, + get: function get() { + return this._tokenName; } - - if (redirectUrl === 0) { - return; + }, { + key: 'tokenPrefix', + set: function set(tokenPrefix) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenPrefix is obsolete. Use BaseConfig.storageKey instead.'); + this._tokenPrefix = tokenPrefix; + return tokenPrefix; + }, + get: function get() { + return this._tokenPrefix || 'aurelia'; } - if (typeof redirectUrl === 'string') { - _aureliaPal.PLATFORM.location.href = encodeURI(redirectUrl); - } else if (defaultRedirectUrl) { - _aureliaPal.PLATFORM.location.href = defaultRedirectUrl; + }, { + key: 'current', + get: function get() { + LogManager.getLogger('authentication').warn('Getter BaseConfig.current is deprecated. Use BaseConfig directly instead.'); + return this; + }, + set: function set(_) { + throw new Error('Setter BaseConfig.current is obsolete. Use BaseConfig directly instead.'); } - }; - - _createClass(Authentication, [{ - key: 'responseObject', + }, { + key: '_current', get: function get() { - LogManager.getLogger('authentication').warn('Getter Authentication.responseObject is deprecated. Use Authentication.getResponseObject() instead.'); - return this.getResponseObject(); + LogManager.getLogger('authentication').warn('Getter BaseConfig._current is deprecated. Use BaseConfig directly instead.'); + return this; }, - set: function set(response) { - LogManager.getLogger('authentication').warn('Setter Authentication.responseObject is deprecated. Use AuthServive.setResponseObject(response) instead.'); - this.setResponseObject(response); + set: function set(_) { + throw new Error('Setter BaseConfig._current is obsolete. Use BaseConfig directly instead.'); } }]); - return Authentication; -}(), (_applyDecoratedDescriptor(_class7.prototype, 'getLoginRoute', [_dec6], Object.getOwnPropertyDescriptor(_class7.prototype, 'getLoginRoute'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getLoginRedirect', [_dec7], Object.getOwnPropertyDescriptor(_class7.prototype, 'getLoginRedirect'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getLoginUrl', [_dec8], Object.getOwnPropertyDescriptor(_class7.prototype, 'getLoginUrl'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getSignupUrl', [_dec9], Object.getOwnPropertyDescriptor(_class7.prototype, 'getSignupUrl'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getProfileUrl', [_dec10], Object.getOwnPropertyDescriptor(_class7.prototype, 'getProfileUrl'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getToken', [_dec11], Object.getOwnPropertyDescriptor(_class7.prototype, 'getToken'), _class7.prototype)), _class7)) || _class6); -var AuthService = exports.AuthService = (_dec12 = (0, _aureliaDependencyInjection.inject)(Authentication, BaseConfig), _dec13 = (0, _aureliaMetadata.deprecated)({ message: 'Use .getAccessToken() instead.' }), _dec12(_class8 = (_class9 = function () { - function AuthService(authentication, config) { - _classCallCheck(this, AuthService); - - this.authenticated = false; - this.timeoutID = 0; - - this.authentication = authentication; - this.config = config; + return BaseConfig; +}(); - var oldStorageKey = config.tokenPrefix ? config.tokenPrefix + '_' + config.tokenName : config.tokenName; - var oldToken = authentication.storage.get(oldStorageKey); +function randomState() { + var rand = Math.random().toString(36).substr(2); + return encodeURIComponent(rand); +} - if (oldToken) { - LogManager.getLogger('authentication').info('Found token with deprecated format in storage. Converting it to new format. No further action required.'); - var fakeOldResponse = {}; - fakeOldResponse[config.accessTokenProp] = oldToken; - this.setResponseObject(fakeOldResponse); - authentication.storage.remove(oldStorageKey); - } +var FetchConfig = exports.FetchConfig = (_dec13 = (0, _aureliaDependencyInjection.inject)(_aureliaFetchClient.HttpClient, _aureliaApi.Config, AuthService, BaseConfig), _dec13(_class10 = function () { + function FetchConfig(httpClient, clientConfig, authService, config) { + - this.setResponseObject(this.authentication.getResponseObject()); + this.httpClient = httpClient; + this.clientConfig = clientConfig; + this.authService = authService; + this.config = config; } - AuthService.prototype.setTimeout = function setTimeout(ttl) { - var _this8 = this; - - this.clearTimeout(); - - this.timeoutID = _aureliaPal.PLATFORM.global.setTimeout(function () { - if (_this8.config.autoUpdateToken && _this8.authentication.getAccessToken() && _this8.authentication.getRefreshToken()) { - _this8.updateToken(); - } else { - _this8.logout(_this8.config.expiredRedirect); - } - }, ttl); - }; - - AuthService.prototype.clearTimeout = function clearTimeout() { - if (this.timeoutID) { - _aureliaPal.PLATFORM.global.clearTimeout(this.timeoutID); - } - this.timeoutID = 0; - }; - - AuthService.prototype.setResponseObject = function setResponseObject(response) { - this.clearTimeout(); - - this.authentication.setResponseObject(response); - - this.authenticated = this.authentication.isAuthenticated(); - if (this.authenticated && !Number.isNaN(this.authentication.exp)) { - this.setTimeout(this.getTtl() * 1000); - } - }; - - AuthService.prototype.getMe = function getMe(criteriaOrId) { - if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { - criteriaOrId = { id: criteriaOrId }; - } - return this.client.find(this.config.joinBase(this.config.profileUrl), criteriaOrId); - }; - - AuthService.prototype.updateMe = function updateMe(body, criteriaOrId) { - if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { - criteriaOrId = { id: criteriaOrId }; - } - if (this.config.profileMethod === 'put') { - return this.client.update(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); - } - return this.client.patch(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); - }; - - AuthService.prototype.getAccessToken = function getAccessToken() { - return this.authentication.getAccessToken(); - }; - - AuthService.prototype.getCurrentToken = function getCurrentToken() { - return this.getAccessToken(); - }; - - AuthService.prototype.getRefreshToken = function getRefreshToken() { - return this.authentication.getRefreshToken(); - }; + FetchConfig.prototype.configure = function configure(client) { + var _this10 = this; - AuthService.prototype.isAuthenticated = function isAuthenticated() { - var authenticated = this.authentication.isAuthenticated(); + if (Array.isArray(client)) { + var _ret = function () { + var configuredClients = []; + client.forEach(function (toConfigure) { + configuredClients.push(_this10.configure(toConfigure)); + }); - if (!authenticated && this.config.autoUpdateToken && this.authentication.getAccessToken() && this.authentication.getRefreshToken()) { - this.updateToken(); - authenticated = true; - } + return { + v: configuredClients + }; + }(); - return authenticated; - }; + if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; + } - AuthService.prototype.getExp = function getExp() { - return this.authentication.getExp(); - }; + if (typeof client === 'string') { + var endpoint = this.clientConfig.getEndpoint(client); + if (!endpoint) { + throw new Error('There is no \'' + (client || 'default') + '\' endpoint registered.'); + } + client = endpoint.client; + } else if (client instanceof _aureliaApi.Rest) { + client = client.client; + } else if (!(client instanceof _aureliaFetchClient.HttpClient)) { + client = this.httpClient; + } - AuthService.prototype.getTtl = function getTtl() { - return this.authentication.getTtl(); - }; + client.interceptors.push(this.interceptor); - AuthService.prototype.isTokenExpired = function isTokenExpired() { - return this.authentication.isTokenExpired(); + return client; }; - AuthService.prototype.getTokenPayload = function getTokenPayload() { - return this.authentication.getPayload(); - }; + _createClass(FetchConfig, [{ + key: 'interceptor', + get: function get() { + var _this11 = this; - AuthService.prototype.updateToken = function updateToken() { - var _this9 = this; + return { + request: function request(_request) { + if (!_this11.config.httpInterceptor || !_this11.authService.isAuthenticated()) { + return _request; + } + var token = _this11.authService.getAccessToken(); - if (!this.authentication.getRefreshToken()) { - return Promise.reject(new Error('refreshToken not set')); - } + if (_this11.config.authTokenType) { + token = _this11.config.authTokenType + ' ' + token; + } - if (this.authentication.updateTokenCallstack.length === 0) { - var content = { - grant_type: 'refresh_token', - refresh_token: this.authentication.getRefreshToken(), - client_id: this.config.clientId ? this.config.clientId : undefined - }; + _request.headers.set(_this11.config.authHeader, token); - this.client.post(this.config.joinBase(this.config.refreshTokenUrl ? this.config.refreshTokenUrl : this.config.loginUrl), content).then(function (response) { - _this9.setResponseObject(response); - _this9.authentication.resolveUpdateTokenCallstack(_this9.isAuthenticated()); - }).catch(function (err) { - _this9.setResponseObject(null); - _this9.authentication.resolveUpdateTokenCallstack(Promise.reject(err)); - }); - } + return _request; + }, + response: function response(_response, request) { + return new Promise(function (resolve, reject) { + if (_response.ok) { + return resolve(_response); + } + if (_response.status !== 401) { + return resolve(_response); + } + if (!_this11.config.httpInterceptor || !_this11.authService.isTokenExpired()) { + return resolve(_response); + } + if (!_this11.config.useRefreshToken || !_this11.authService.getRefreshToken()) { + return resolve(_response); + } - return this.authentication.toUpdateTokenCallstack(); - }; + return _this11.authService.updateToken().then(function () { + var token = _this11.authService.getAccessToken(); - AuthService.prototype.signup = function signup(displayNameOrCredentials, emailOrOptions, passwordOrRedirectUri, options, redirectUri) { - var _this10 = this; + if (_this11.config.authTokenType) { + token = _this11.config.authTokenType + ' ' + token; + } - var content = void 0; + request.headers.set(_this11.config.authHeader, token); - if (_typeof(arguments[0]) === 'object') { - content = arguments[0]; - options = arguments[1]; - redirectUri = arguments[2]; - } else { - content = { - 'displayName': displayNameOrCredentials, - 'email': emailOrOptions, - 'password': passwordOrRedirectUri + return _this11.client.fetch(request).then(resolve); + }); + }); + } }; } - return this.client.post(this.config.joinBase(this.config.signupUrl), content, options).then(function (response) { - if (_this10.config.loginOnSignup) { - _this10.setResponseObject(response); - } - _this10.authentication.redirect(redirectUri, _this10.config.signupRedirect); + }]); - return response; - }); - }; + return FetchConfig; +}()) || _class10); +var OAuth1 = exports.OAuth1 = (_dec14 = (0, _aureliaDependencyInjection.inject)(Storage, Popup, BaseConfig), _dec14(_class11 = function () { + function OAuth1(storage, popup, config) { + - AuthService.prototype.login = function login(emailOrCredentials, passwordOrOptions, optionsOrRedirectUri, redirectUri) { - var _this11 = this; + this.storage = storage; + this.config = config; + this.popup = popup; + this.defaults = { + url: null, + name: null, + popupOptions: null, + redirectUri: null, + authorizationEndpoint: null + }; + } - var content = void 0; + OAuth1.prototype.open = function open(options, userData) { + var _this12 = this; - if (_typeof(arguments[0]) === 'object') { - content = arguments[0]; - optionsOrRedirectUri = arguments[1]; - redirectUri = arguments[2]; - } else { - content = { - 'email': emailOrCredentials, - 'password': passwordOrOptions - }; - optionsOrRedirectUri = optionsOrRedirectUri; - } + var provider = (0, _extend2.default)(true, {}, this.defaults, options); + var serverUrl = this.config.joinBase(provider.url); - if (this.config.clientId) { - content.client_id = this.config.clientId; + if (this.config.platform !== 'mobile') { + this.popup = this.popup.open('', provider.name, provider.popupOptions); } - return this.client.post(this.config.joinBase(this.config.loginUrl), content, optionsOrRedirectUri).then(function (response) { - _this11.setResponseObject(response); + return this.config.client.post(serverUrl).then(function (response) { + var url = provider.authorizationEndpoint + '?' + (0, _aureliaPath.buildQueryString)(response); - _this11.authentication.redirect(redirectUri, _this11.config.loginRedirect); + if (_this12.config.platform === 'mobile') { + _this12.popup = _this12.popup.open(url, provider.name, provider.popupOptions); + } else { + _this12.popup.popupWindow.location = url; + } - return response; + var popupListener = _this12.config.platform === 'mobile' ? _this12.popup.eventListener(provider.redirectUri) : _this12.popup.pollPopup(); + + return popupListener.then(function (result) { + return _this12.exchangeForToken(result, userData, provider); + }); }); }; - AuthService.prototype.logout = function logout(redirectUri) { - var _this12 = this; - - var localLogout = function localLogout(response) { - return new Promise(function (resolve) { - _this12.setResponseObject(null); + OAuth1.prototype.exchangeForToken = function exchangeForToken(oauthData, userData, provider) { + var data = (0, _extend2.default)(true, {}, userData, oauthData); + var serverUrl = this.config.joinBase(provider.url); + var credentials = this.config.withCredentials ? 'include' : 'same-origin'; - _this12.authentication.redirect(redirectUri, _this12.config.logoutRedirect); + return this.config.client.post(serverUrl, data, { credentials: credentials }); + }; - if (typeof _this12.onLogout === 'function') { - _this12.onLogout(response); - } + return OAuth1; +}()) || _class11); +var OAuth2 = exports.OAuth2 = (_dec15 = (0, _aureliaDependencyInjection.inject)(Storage, Popup, BaseConfig), _dec15(_class12 = function () { + function OAuth2(storage, popup, config) { + - resolve(response); - }); + this.storage = storage; + this.config = config; + this.popup = popup; + this.defaults = { + url: null, + name: null, + state: null, + scope: null, + scopeDelimiter: null, + redirectUri: null, + popupOptions: null, + authorizationEndpoint: null, + responseParams: null, + requiredUrlParams: null, + optionalUrlParams: null, + defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], + responseType: 'code' }; + } - return this.config.logoutUrl ? this.client.request(this.config.logoutMethod, this.config.joinBase(this.config.logoutUrl)).then(localLogout) : localLogout(); - }; - - AuthService.prototype.authenticate = function authenticate(name, redirectUri) { + OAuth2.prototype.open = function open(options, userData) { var _this13 = this; - var userData = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - - return this.authentication.authenticate(name, userData).then(function (response) { - _this13.setResponseObject(response); - - _this13.authentication.redirect(redirectUri, _this13.config.loginRedirect); - - return response; - }); - }; + var provider = (0, _extend2.default)(true, {}, this.defaults, options); + var stateName = provider.name + '_state'; - AuthService.prototype.unlink = function unlink(name, redirectUri) { - var _this14 = this; + if (typeof provider.state === 'function') { + this.storage.set(stateName, provider.state()); + } else if (typeof provider.state === 'string') { + this.storage.set(stateName, provider.state); + } - var unlinkUrl = this.config.joinBase(this.config.unlinkUrl) + name; - return this.client.request(this.config.unlinkMethod, unlinkUrl).then(function (response) { - _this14.authentication.redirect(redirectUri); + var url = provider.authorizationEndpoint + '?' + (0, _aureliaPath.buildQueryString)(this.buildQuery(provider)); + var popup = this.popup.open(url, provider.name, provider.popupOptions); + var openPopup = this.config.platform === 'mobile' ? popup.eventListener(provider.redirectUri) : popup.pollPopup(); - return response; + return openPopup.then(function (oauthData) { + if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { + return oauthData; + } + if (oauthData.state && oauthData.state !== _this13.storage.get(stateName)) { + return Promise.reject('OAuth 2.0 state parameter mismatch.'); + } + return _this13.exchangeForToken(oauthData, userData, provider); }); }; - _createClass(AuthService, [{ - key: 'client', - get: function get() { - return this.config.client; - } - }, { - key: 'auth', - get: function get() { - LogManager.getLogger('authentication').warn('AuthService.auth is deprecated. Use .authentication instead.'); - return this.authentication; - } - }]); - - return AuthService; -}(), (_applyDecoratedDescriptor(_class9.prototype, 'getCurrentToken', [_dec13], Object.getOwnPropertyDescriptor(_class9.prototype, 'getCurrentToken'), _class9.prototype)), _class9)) || _class8); -var AuthenticateStep = exports.AuthenticateStep = (_dec14 = (0, _aureliaDependencyInjection.inject)(AuthService), _dec14(_class11 = function () { - function AuthenticateStep(authService) { - _classCallCheck(this, AuthenticateStep); - - this.authService = authService; - } - - AuthenticateStep.prototype.run = function run(routingContext, next) { - var isLoggedIn = this.authService.authenticated; - var loginRoute = this.authService.config.loginRoute; + OAuth2.prototype.exchangeForToken = function exchangeForToken(oauthData, userData, provider) { + var data = (0, _extend2.default)(true, {}, userData, { + clientId: provider.clientId, + redirectUri: provider.redirectUri + }, oauthData); - if (routingContext.getAllInstructions().some(function (route) { - return route.config.auth === true; - })) { - if (!isLoggedIn) { - return next.cancel(new _aureliaRouter.Redirect(loginRoute)); - } - } else if (isLoggedIn && routingContext.getAllInstructions().some(function (route) { - return route.fragment === loginRoute; - })) { - return next.cancel(new _aureliaRouter.Redirect(this.authService.config.loginRedirect)); - } + var serverUrl = this.config.joinBase(provider.url); + var credentials = this.config.withCredentials ? 'include' : 'same-origin'; - return next(); + return this.config.client.post(serverUrl, data, { credentials: credentials }); }; - return AuthenticateStep; -}()) || _class11); -var AuthorizeStep = exports.AuthorizeStep = (_dec15 = (0, _aureliaDependencyInjection.inject)(AuthService), _dec15(_class12 = function () { - function AuthorizeStep(authService) { - _classCallCheck(this, AuthorizeStep); + OAuth2.prototype.buildQuery = function buildQuery(provider) { + var _this14 = this; - LogManager.getLogger('authentication').warn('AuthorizeStep is deprecated. Use AuthenticationStep instead.'); + var query = {}; + var urlParams = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams']; - this.authService = authService; - } + urlParams.forEach(function (params) { + (provider[params] || []).forEach(function (paramName) { + var camelizedName = camelCase(paramName); + var paramValue = typeof provider[paramName] === 'function' ? provider[paramName]() : provider[camelizedName]; - AuthorizeStep.prototype.run = function run(routingContext, next) { - var isLoggedIn = this.authService.isAuthenticated(); - var loginRoute = this.authService.config.loginRoute; + if (paramName === 'state') { + paramValue = encodeURIComponent(_this14.storage.get(provider.name + '_state')); + } - if (routingContext.getAllInstructions().some(function (route) { - return route.config.auth; - })) { - if (!isLoggedIn) { - return next.cancel(new _aureliaRouter.Redirect(loginRoute)); - } - } else if (isLoggedIn && routingContext.getAllInstructions().some(function (route) { - return route.fragment === loginRoute; - })) { - return next.cancel(new _aureliaRouter.Redirect(this.authService.config.loginRedirect)); - } + if (paramName === 'scope' && Array.isArray(paramValue)) { + paramValue = paramValue.join(provider.scopeDelimiter); - return next(); + if (provider.scopePrefix) { + paramValue = provider.scopePrefix + provider.scopeDelimiter + paramValue; + } + } + + query[paramName] = paramValue; + }); + }); + return query; }; - return AuthorizeStep; + return OAuth2; }()) || _class12); -var FetchConfig = exports.FetchConfig = (_dec16 = (0, _aureliaDependencyInjection.inject)(_aureliaFetchClient.HttpClient, _aureliaApi.Config, AuthService, BaseConfig), _dec16(_class13 = function () { - function FetchConfig(httpClient, clientConfig, authService, config) { - _classCallCheck(this, FetchConfig); - this.httpClient = httpClient; - this.clientConfig = clientConfig; - this.authService = authService; - this.config = config; - } - FetchConfig.prototype.configure = function configure(client) { - var _this15 = this; +var camelCase = function camelCase(name) { + return name.replace(/([\:\-\_]+(.))/g, function (_, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }); +}; - if (Array.isArray(client)) { - var _ret = function () { - var configuredClients = []; - client.forEach(function (toConfigure) { - configuredClients.push(_this15.configure(toConfigure)); - }); +var Popup = exports.Popup = function () { + function Popup() { + - return { - v: configuredClients - }; - }(); + this.popupWindow = null; + this.polling = null; + this.url = ''; + } - if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; - } + Popup.prototype.open = function open(url, windowName, options) { + this.url = url; + var optionsString = buildPopupWindowOptions(options || {}); - if (typeof client === 'string') { - var endpoint = this.clientConfig.getEndpoint(client); - if (!endpoint) { - throw new Error('There is no \'' + (client || 'default') + '\' endpoint registered.'); - } - client = endpoint.client; - } else if (client instanceof _aureliaApi.Rest) { - client = client.client; - } else if (!(client instanceof _aureliaFetchClient.HttpClient)) { - client = this.httpClient; - } + this.popupWindow = _aureliaPal.PLATFORM.global.open(url, windowName, optionsString); - client.interceptors.push(this.interceptor); + if (this.popupWindow && this.popupWindow.focus) { + this.popupWindow.focus(); + } - return client; + return this; }; - _createClass(FetchConfig, [{ - key: 'interceptor', - get: function get() { - var _this16 = this; + Popup.prototype.eventListener = function eventListener(redirectUri) { + var _this15 = this; - return { - request: function request(_request) { - if (!_this16.config.httpInterceptor || !_this16.authService.isAuthenticated()) { - return _request; - } - var token = _this16.authService.getAccessToken(); + return new Promise(function (resolve, reject) { + _this15.popupWindow.addEventListener('loadstart', function (event) { + if (event.url.indexOf(redirectUri) !== 0) { + return; + } + + var parser = _aureliaPal.DOM.createElement('a'); + parser.href = event.url; + + if (parser.search || parser.hash) { + var qs = parseUrl(parser); - if (_this16.config.authTokenType) { - token = _this16.config.authTokenType + ' ' + token; + if (qs.error) { + reject({ error: qs.error }); + } else { + resolve(qs); } - _request.headers.set(_this16.config.authHeader, token); + _this15.popupWindow.close(); + } + }); - return _request; - }, - response: function response(_response, request) { - return new Promise(function (resolve, reject) { - if (_response.ok) { - return resolve(_response); - } - if (_response.status !== 401) { - return resolve(_response); - } - if (!_this16.config.httpInterceptor || !_this16.authService.isTokenExpired()) { - return resolve(_response); - } - if (!_this16.config.useRefreshToken || !_this16.authService.getRefreshToken()) { - return resolve(_response); - } + _this15.popupWindow.addEventListener('exit', function () { + reject({ data: 'Provider Popup was closed' }); + }); - _this16.authService.updateToken().then(function () { - var token = _this16.authService.getAccessToken(); + _this15.popupWindow.addEventListener('loaderror', function () { + reject({ data: 'Authorization Failed' }); + }); + }); + }; - if (_this16.config.authTokenType) { - token = _this16.config.authTokenType + ' ' + token; - } + Popup.prototype.pollPopup = function pollPopup() { + var _this16 = this; - request.headers.set(_this16.config.authHeader, token); + return new Promise(function (resolve, reject) { + _this16.polling = _aureliaPal.PLATFORM.global.setInterval(function () { + var errorData = void 0; - return _this16.client.fetch(request).then(resolve); - }); - }); - } - }; - } - }]); + try { + if (_this16.popupWindow.location.host === _aureliaPal.PLATFORM.global.document.location.host && (_this16.popupWindow.location.search || _this16.popupWindow.location.hash)) { + var qs = parseUrl(_this16.popupWindow.location); - return FetchConfig; -}()) || _class13); + if (qs.error) { + reject({ error: qs.error }); + } else { + resolve(qs); + } -function configure(aurelia, config) { - if (!_aureliaPal.PLATFORM.location.origin) { - _aureliaPal.PLATFORM.location.origin = _aureliaPal.PLATFORM.location.protocol + '//' + _aureliaPal.PLATFORM.location.hostname + (_aureliaPal.PLATFORM.location.port ? ':' + _aureliaPal.PLATFORM.location.port : ''); - } + _this16.popupWindow.close(); + _aureliaPal.PLATFORM.global.clearInterval(_this16.polling); + } + } catch (error) { + errorData = error; + } - var baseConfig = aurelia.container.get(BaseConfig); + if (!_this16.popupWindow) { + _aureliaPal.PLATFORM.global.clearInterval(_this16.polling); + reject({ + error: errorData, + data: 'Provider Popup Blocked' + }); + } else if (_this16.popupWindow.closed) { + _aureliaPal.PLATFORM.global.clearInterval(_this16.polling); + reject({ + error: errorData, + data: 'Problem poll popup' + }); + } + }, 35); + }); + }; - if (typeof config === 'function') { - config(baseConfig); - } else if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') { - baseConfig.configure(config); - } + return Popup; +}(); - for (var _iterator = baseConfig.globalValueConverters, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; +var buildPopupWindowOptions = function buildPopupWindowOptions(options) { + var width = options.width || 500; + var height = options.height || 500; - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } + var extended = (0, _extend2.default)({ + width: width, + height: height, + left: _aureliaPal.PLATFORM.global.screenX + (_aureliaPal.PLATFORM.global.outerWidth - width) / 2, + top: _aureliaPal.PLATFORM.global.screenY + (_aureliaPal.PLATFORM.global.outerHeight - height) / 2.5 + }, options); - var converter = _ref; + var parts = []; + Object.keys(extended).map(function (key) { + return parts.push(key + '=' + extended[key]); + }); - aurelia.globalResources('./' + converter); - LogManager.getLogger('authentication').info('Add globalResources value-converter: ' + converter); - } - var fetchConfig = aurelia.container.get(FetchConfig); - var clientConfig = aurelia.container.get(_aureliaApi.Config); + return parts.join(','); +}; - if (Array.isArray(baseConfig.configureEndpoints)) { - baseConfig.configureEndpoints.forEach(function (endpointToPatch) { - fetchConfig.configure(endpointToPatch); - }); - } +var parseUrl = function parseUrl(url) { + return (0, _extend2.default)(true, {}, (0, _aureliaPath.parseQueryString)(url.search), (0, _aureliaPath.parseQueryString)(url.hash)); +}; - var client = void 0; +var Storage = exports.Storage = (_dec16 = (0, _aureliaDependencyInjection.inject)(BaseConfig), _dec16(_class13 = function () { + function Storage(config) { + - if (baseConfig.endpoint !== null) { - if (typeof baseConfig.endpoint === 'string') { - var endpoint = clientConfig.getEndpoint(baseConfig.endpoint); - if (!endpoint) { - throw new Error('There is no \'' + (baseConfig.endpoint || 'default') + '\' endpoint registered.'); - } - client = endpoint; - } else if (baseConfig.endpoint instanceof _aureliaFetchClient.HttpClient) { - client = new _aureliaApi.Rest(baseConfig.endpoint); - } + this.config = config; } - if (!(client instanceof _aureliaApi.Rest)) { - client = new _aureliaApi.Rest(aurelia.container.get(_aureliaFetchClient.HttpClient)); - } + Storage.prototype.get = function get(key) { + return _aureliaPal.PLATFORM.global[this.config.storage].getItem(key); + }; - baseConfig.client = client; -} + Storage.prototype.set = function set(key, value) { + _aureliaPal.PLATFORM.global[this.config.storage].setItem(key, value); + }; -exports.configure = configure; -exports.FetchConfig = FetchConfig; -exports.AuthService = AuthService; -exports.AuthorizeStep = AuthorizeStep; -exports.AuthenticateStep = AuthenticateStep; \ No newline at end of file + Storage.prototype.remove = function remove(key) { + _aureliaPal.PLATFORM.global[this.config.storage].removeItem(key); + }; + + return Storage; +}()) || _class13); \ No newline at end of file diff --git a/dist/commonjs/authFilterValueConverter.js b/dist/commonjs/authFilterValueConverter.js index ca2b813..7894c4d 100644 --- a/dist/commonjs/authFilterValueConverter.js +++ b/dist/commonjs/authFilterValueConverter.js @@ -4,11 +4,11 @@ Object.defineProperty(exports, "__esModule", { value: true }); -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + var AuthFilterValueConverter = exports.AuthFilterValueConverter = function () { function AuthFilterValueConverter() { - _classCallCheck(this, AuthFilterValueConverter); + } AuthFilterValueConverter.prototype.toView = function toView(routes, isAuthenticated) { diff --git a/dist/commonjs/authenticatedFilterValueConverter.js b/dist/commonjs/authenticatedFilterValueConverter.js index 8801529..8268bb2 100644 --- a/dist/commonjs/authenticatedFilterValueConverter.js +++ b/dist/commonjs/authenticatedFilterValueConverter.js @@ -11,11 +11,11 @@ var _aureliaDependencyInjection = require('aurelia-dependency-injection'); var _aureliaAuthentication = require('./aurelia-authentication'); -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + var AuthenticatedFilterValueConverter = exports.AuthenticatedFilterValueConverter = (_dec = (0, _aureliaDependencyInjection.inject)(_aureliaAuthentication.AuthService), _dec(_class = function () { function AuthenticatedFilterValueConverter(authService) { - _classCallCheck(this, AuthenticatedFilterValueConverter); + this.authService = authService; } diff --git a/dist/commonjs/authenticatedValueConverter.js b/dist/commonjs/authenticatedValueConverter.js index bec1138..2a2bc53 100644 --- a/dist/commonjs/authenticatedValueConverter.js +++ b/dist/commonjs/authenticatedValueConverter.js @@ -11,11 +11,11 @@ var _aureliaDependencyInjection = require('aurelia-dependency-injection'); var _aureliaAuthentication = require('./aurelia-authentication'); -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + var AuthenticatedValueConverter = exports.AuthenticatedValueConverter = (_dec = (0, _aureliaDependencyInjection.inject)(_aureliaAuthentication.AuthService), _dec(_class = function () { function AuthenticatedValueConverter(authService) { - _classCallCheck(this, AuthenticatedValueConverter); + this.authService = authService; } diff --git a/dist/commonjs/index.js b/dist/commonjs/index.js new file mode 100644 index 0000000..51518e7 --- /dev/null +++ b/dist/commonjs/index.js @@ -0,0 +1,17 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _aureliaAuthentication = require('./aurelia-authentication'); + +Object.keys(_aureliaAuthentication).forEach(function (key) { + if (key === "default") return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function get() { + return _aureliaAuthentication[key]; + } + }); +}); \ No newline at end of file diff --git a/dist/es2015/aurelia-authentication.d.ts b/dist/es2015/aurelia-authentication.d.ts deleted file mode 100644 index ba2a7d6..0000000 --- a/dist/es2015/aurelia-authentication.d.ts +++ /dev/null @@ -1,495 +0,0 @@ -declare module 'aurelia-authentication' { - export class Auth0Lock { - constructor(storage?: any, config?: any); - open(options?: any, userData?: any): any; - } - export class AuthenticatedFilterValueConverter { - constructor(authService?: any); - - /** - * route toView predictator on route.config.auth === (parameter || authService.isAuthenticated()) - * @param {RouteConfig} routes the routes array to convert - * @param {[Boolean]} [isAuthenticated] optional isAuthenticated value. default: this.authService.authenticated - * @return {Boolean} show/hide element - */ - toView(routes?: any, isAuthenticated?: any): any; - } - export class AuthenticatedValueConverter { - constructor(authService?: any); - - /** - * element toView predictator on authService.isAuthenticated() - * @return {Boolean} show/hide element - */ - toView(): any; - } - export class AuthenticateStep { - constructor(authService?: any); - run(routingContext?: any, next?: any): any; - } - export class Authentication { - constructor(storage?: any, config?: any, oAuth1?: any, oAuth2?: any, auth0Lock?: any); - - /* deprecated methods */ - getLoginRoute(): any; - getLoginRedirect(): any; - getLoginUrl(): any; - getSignupUrl(): any; - getProfileUrl(): any; - getToken(): any; - responseObject: any; - - /* get/set responseObject */ - getResponseObject(): any; - setResponseObject(response?: any): any; - - /* get data, update if needed first */ - getAccessToken(): any; - getRefreshToken(): any; - getPayload(): any; - getExp(): any; - - /* get status from data */ - getTtl(): any; - isTokenExpired(): any; - isAuthenticated(): any; - - /* get and set from response */ - getDataFromResponse(response?: any): any; - getTokenFromResponse(response?: any, tokenProp?: any, tokenName?: any, tokenRoot?: any): any; - toUpdateTokenCallstack(): any; - resolveUpdateTokenCallstack(response?: any): any; - - /** - * Authenticate with third-party - * - * @param {String} name of the provider - * @param {[{}]} [userData] - * - * @return {Promise} - */ - authenticate(name?: any, userData?: any): any; - redirect(redirectUrl?: any, defaultRedirectUrl?: any): any; - } - export class AuthFilterValueConverter { - - /** - * route toView predictator on route.config.auth === isAuthenticated - * @param {RouteConfig} routes the routes array to convert - * @param {Boolean} isAuthenticated authentication status - * @return {Boolean} show/hide element - */ - toView(routes?: any, isAuthenticated?: any): any; - } - export class AuthorizeStep { - constructor(authService?: any); - run(routingContext?: any, next?: any): any; - } - export class AuthService { - - /** - * The Authentication instance that handles the token - * - * @param {Authentication} - */ - authentication: any; - - /** - * The Config instance that contains the current configuration setting - * - * @param {Config} - */ - config: any; - - /** - * The current login status - * - * @param {Boolean} - */ - authenticated: any; - - /** - * The currently set timeoutID - * - * @param {Number} - */ - timeoutID: any; - - /** - * Create an AuthService instance - * - * @param {Authentication} authentication The Authentication instance to be used - * @param {Config} config The Config instance to be used - */ - constructor(authentication?: any, config?: any); - - /** - * Getter: The configured client for all aurelia-authentication requests - * - * @return {HttpClient} - */ - client: any; - auth: any; - - /** - * Sets the login timeout - * - * @param {Number} ttl Timeout time in ms - */ - setTimeout(ttl?: any): any; - - /** - * Clears the login timeout - */ - clearTimeout(): any; - - /** - * Stores and analyses the servers responseObject. Sets login status and timeout - * - * @param {Object} response The servers response as GOJO - */ - setResponseObject(response?: any): any; - - /** - * Get current user profile from server - * - * @param {[{}|number|string]} [criteriaOrId object or a Number|String converted to {id: criteriaOrId}] - * - * @return {Promise} - */ - getMe(criteriaOrId?: any): any; - - /** - * Send current user profile update to server - - * @param {any} Request body with data. - * @param {[{}|Number|String]} [criteriaOrId object or a Number|String converted to {id: criteriaOrId}] - * - * @return {Promise} - */ - updateMe(body?: any, criteriaOrId?: any): any; - - /** - * Get accessToken from storage - * - * @returns {String} Current accessToken - */ - getAccessToken(): any; - getCurrentToken(): any; - - /** - * Get refreshToken from storage - * - * @returns {String} Current refreshToken - */ - getRefreshToken(): any; - - /** - * Gets authentication status - * - * @returns {Boolean} For Non-JWT and unexpired JWT: true, else: false - */ - isAuthenticated(): any; - - /** - * Gets exp in milliseconds - * - * @returns {Number} Exp for JWT tokens, NaN for all other tokens - */ - getExp(): any; - - /** - * Gets ttl in seconds - * - * @returns {Number} Ttl for JWT tokens, NaN for all other tokens - */ - getTtl(): any; - - /** - * Gets exp from token payload and compares to current time - * - * @returns {Boolean} Returns (ttl > 0)? for JWT, undefined other tokens - */ - isTokenExpired(): any; - - /** - * Get payload from tokens - * - * @returns {Object} Payload for JWT, else null - */ - getTokenPayload(): any; - - /** - * Request new accesss token - * - * @returns {Promise} Requests new token. can be called multiple times - */ - updateToken(): any; - - /** - * Signup locally. Login and redirect depending on config - * - * @param {String|{}} displayNameOrCredentials displayName | object with signup data. - * @param {[String]|{}} emailOrOptions [email | options for post request] - * @param {[String]} passwordOrRedirectUri [password | optional redirectUri overwrite] - * @param {[{}]} options [options] - * @param {[String]} redirectUri [optional redirectUri overwrite] - * - * @return {Promise|Promise} Server response as Object - */ - signup(displayNameOrCredentials?: any, emailOrOptions?: any, passwordOrRedirectUri?: any, options?: any, redirectUri?: any): any; - - /** - * login locally. Redirect depending on config - * - * @param {[String]|{}} emailOrCredentials email | object with signup data. - * @param {[String]} [passwordOrOptions] [password | options for post request] - * @param {[{}]} [optionsOrRedirectUri] [options | redirectUri overwrite]] - * @param {[String]} [redirectUri] [optional redirectUri overwrite] - * - * @return {Promise|Promise} Server response as Object - */ - login(emailOrCredentials?: any, passwordOrOptions?: any, optionsOrRedirectUri?: any, redirectUri?: any): any; - - /** - * logout locally and redirect to redirectUri (if set) or redirectUri of config. Sends logout request first, if set in config - * - * @param {[String]} [redirectUri] [optional redirectUri overwrite] - * - * @return {Promise<>|Promise|Promise} Server response as Object - */ - logout(redirectUri?: any): any; - - /** - * Authenticate with third-party and redirect to redirectUri (if set) or redirectUri of config - * - * @param {String} name Name of the provider - * @param {[String]} [redirectUri] [optional redirectUri overwrite] - * @param {[{}]} [userData] [optional userData for the local authentication server] - * - * @return {Promise|Promise} Server response as Object - */ - authenticate(name?: any, redirectUri?: any, userData?: any): any; - - /** - * Unlink third-party - * - * @param {String} name Name of the provider - * - * @return {Promise|Promise} Server response as Object - */ - unlink(name?: any, redirectUri?: any): any; - } - export class BaseConfig { - - /** - * Prepends baseUrl to a given url - * @param {String} url The relative url to append - * @return {String} joined baseUrl and url - */ - joinBase(url?: any): any; - - /** - * Merge current settings with incomming settings - * @param {Object} incomming Settings object to be merged into the current configuration - * @return {Config} this - */ - configure(incomming?: any): any; - - /* ----------- default config ----------- */ - // Used internally. The used Rest instance; set during configuration (see index.js) - client: any; - - // If using aurelia-api: - // ===================== - // This is the name of the endpoint used for any requests made in relation to authentication (login, logout, etc.). An empty string selects the default endpoint of aurelia-api. - endpoint: any; - - // When authenticated, these endpoints will have the token added to the header of any requests (for authorization). Accepts an array of endpoint names. An empty string selects the default endpoint of aurelia-api. - configureEndpoints: any; - - // SPA related options - // =================== - // The SPA url to which the user is redirected after a successful login - loginRedirect: any; - - // The SPA url to which the user is redirected after a successful logout - logoutRedirect: any; - - // The SPA route used when an unauthenticated user tries to access an SPA page that requires authentication - loginRoute: any; - - // Whether or not an authentication token is provided in the response to a successful signup - loginOnSignup: any; - - // If loginOnSignup == false: The SPA url to which the user is redirected after a successful signup (else loginRedirect is used) - signupRedirect: any; - - // redirect when token expires. 0 = don't redirect (default), 1 = use logoutRedirect, string = redirect there - expiredRedirect: any; - - // API related options - // =================== - // The base url used for all authentication related requests, including provider.url below. - // This appends to the httpClient/endpoint base url, it does not override it. - baseUrl: any; - - // The API endpoint to which login requests are sent - loginUrl: any; - - // The API endpoint to which logout requests are sent (not needed for jwt) - logoutUrl: any; - - // The HTTP method used for 'unlink' requests (Options: 'get' or 'post') - logoutMethod: any; - - // The API endpoint to which signup requests are sent - signupUrl: any; - - // The API endpoint used in profile requests (inc. `find/get` and `update`) - profileUrl: any; - - // The method used to update the profile ('put' or 'patch') - profileMethod: any; - - // The API endpoint used with oAuth to unlink authentication - unlinkUrl: any; - - // The HTTP method used for 'unlink' requests (Options: 'get' or 'post') - unlinkMethod: any; - - // The API endpoint to which refreshToken requests are sent. null = loginUrl - refreshTokenUrl: any; - - // Token Options - // ============= - // The header property used to contain the authToken in the header of API requests that require authentication - authHeader: any; - - // The token name used in the header of API requests that require authentication - authTokenType: any; - - // The the property from which to get the access token after a successful login or signup. Can also be dotted eg "accessTokenProp.accessTokenName" - accessTokenProp: any; - - // If the property defined by `accessTokenProp` is an object: - // ------------------------------------------------------------ - //This is the property from which to get the token `{ "accessTokenProp": { "accessTokenName" : '...' } }` - accessTokenName: any; - - // This allows the token to be a further object deeper `{ "accessTokenProp": { "accessTokenRoot" : { "accessTokenName" : '...' } } }` - accessTokenRoot: any; - - // Refresh Token Options - // ===================== - // Option to turn refresh tokens On/Off - useRefreshToken: any; - - // The option to enable/disable the automatic refresh of Auth tokens using Refresh Tokens - autoUpdateToken: any; - - // Oauth Client Id - clientId: any; - - // The the property from which to get the refresh token after a successful token refresh. Can also be dotted eg "refreshTokenProp.refreshTokenProp" - refreshTokenProp: any; - - // If the property defined by `refreshTokenProp` is an object: - // ----------------------------------------------------------- - // This is the property from which to get the token `{ "refreshTokenProp": { "refreshTokenName" : '...' } }` - refreshTokenName: any; - - // This allows the refresh token to be a further object deeper `{ "refreshTokenProp": { "refreshTokenRoot" : { "refreshTokenName" : '...' } } }` - refreshTokenRoot: any; - - // Miscellaneous Options - // ===================== - // Whether to enable the fetch interceptor which automatically adds the authentication headers - // (or not... e.g. if using a session based API or you want to override the default behaviour) - httpInterceptor: any; - - // For OAuth only: Tell the API whether or not to include token cookies in the response (for session based APIs) - withCredentials: any; - - // Controls how the popup is shown for different devices (Options: 'browser' or 'mobile') - platform: any; - - // Determines the `PLATFORM` property name upon which aurelia-authentication data is stored (Default: `PLATFORM.localStorage`) - storage: any; - - // The key used for storing the authentication response locally - storageKey: any; - - // List of value-converters to make global - globalValueConverters: any; - - //OAuth provider specific related configuration - // ============================================ - providers: any; - - /* deprecated defaults */ - _authToken: any; - _responseTokenProp: any; - _tokenName: any; - _tokenRoot: any; - _tokenPrefix: any; - authToken: any; - responseTokenProp: any; - tokenRoot: any; - tokenName: any; - tokenPrefix: any; - current: any; - _current: any; - } - export class FetchConfig { - - /** - * Construct the FetchConfig - * - * @param {HttpClient} httpClient - * @param {Config} clientConfig - * @param {Authentication} authService - * @param {BaseConfig} config - */ - constructor(httpClient?: any, clientConfig?: any, authService?: any, config?: any); - - /** - * Interceptor for HttpClient - * - * @return {{request: Function, response: Function}} - */ - interceptor: any; - - /** - * Configure client(s) with authorization interceptor - * - * @param {HttpClient|Rest|string[]} client HttpClient, rest client or api endpoint name, or an array thereof - * - * @return {HttpClient[]} - */ - configure(client?: any): any; - } - export class OAuth1 { - constructor(storage?: any, popup?: any, config?: any); - open(options?: any, userData?: any): any; - exchangeForToken(oauthData?: any, userData?: any, provider?: any): any; - } - export class OAuth2 { - constructor(storage?: any, popup?: any, config?: any); - open(options?: any, userData?: any): any; - exchangeForToken(oauthData?: any, userData?: any, provider?: any): any; - buildQuery(provider?: any): any; - } - export class Popup { - constructor(); - open(url?: any, windowName?: any, options?: any): any; - eventListener(redirectUri?: any): any; - pollPopup(): any; - } - export class Storage { - constructor(config?: any); - get(key?: any): any; - set(key?: any, value?: any): any; - remove(key?: any): any; - } -} \ No newline at end of file diff --git a/dist/es2015/aurelia-authentication.js b/dist/es2015/aurelia-authentication.js index 55c90c1..811f34c 100644 --- a/dist/es2015/aurelia-authentication.js +++ b/dist/es2015/aurelia-authentication.js @@ -1,4 +1,4 @@ -var _dec, _class2, _dec2, _class3, _dec3, _class4, _dec4, _class5, _dec5, _dec6, _dec7, _dec8, _dec9, _dec10, _dec11, _class6, _desc, _value, _class7, _dec12, _dec13, _class8, _desc2, _value2, _class9, _dec14, _class11, _dec15, _class12, _dec16, _class13; +var _dec, _class, _dec2, _class2, _dec3, _dec4, _dec5, _dec6, _dec7, _dec8, _dec9, _class3, _desc, _value, _class4, _dec10, _class5, _dec11, _dec12, _class6, _desc2, _value2, _class7, _dec13, _class10, _dec14, _class11, _dec15, _class12, _dec16, _class13; function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; @@ -29,1230 +29,1228 @@ function _applyDecoratedDescriptor(target, property, decorators, descriptor, con return desc; } -import extend from 'extend'; import * as LogManager from 'aurelia-logging'; +import extend from 'extend'; import jwtDecode from 'jwt-decode'; import { PLATFORM, DOM } from 'aurelia-pal'; -import { parseQueryString, join, buildQueryString } from 'aurelia-path'; -import { inject } from 'aurelia-dependency-injection'; -import { deprecated } from 'aurelia-metadata'; -import { Redirect } from 'aurelia-router'; import { HttpClient } from 'aurelia-fetch-client'; import { Config, Rest } from 'aurelia-api'; +import { inject } from 'aurelia-dependency-injection'; +import { Redirect } from 'aurelia-router'; +import { deprecated } from 'aurelia-metadata'; +import { join, buildQueryString, parseQueryString } from 'aurelia-path'; -export let Popup = class Popup { - constructor() { - this.popupWindow = null; - this.polling = null; - this.url = ''; +import './authFilterValueConverter'; + +export function configure(aurelia, config) { + if (!PLATFORM.location.origin) { + PLATFORM.location.origin = PLATFORM.location.protocol + '//' + PLATFORM.location.hostname + (PLATFORM.location.port ? ':' + PLATFORM.location.port : ''); } - open(url, windowName, options) { - this.url = url; - const optionsString = buildPopupWindowOptions(options || {}); + const baseConfig = aurelia.container.get(BaseConfig); - this.popupWindow = PLATFORM.global.open(url, windowName, optionsString); + if (typeof config === 'function') { + config(baseConfig); + } else if (typeof config === 'object') { + baseConfig.configure(config); + } - if (this.popupWindow && this.popupWindow.focus) { - this.popupWindow.focus(); + for (let converter of baseConfig.globalValueConverters) { + aurelia.globalResources(`./${ converter }`); + LogManager.getLogger('authentication').info(`Add globalResources value-converter: ${ converter }`); + } + const fetchConfig = aurelia.container.get(FetchConfig); + const clientConfig = aurelia.container.get(Config); + + if (Array.isArray(baseConfig.configureEndpoints)) { + baseConfig.configureEndpoints.forEach(endpointToPatch => { + fetchConfig.configure(endpointToPatch); + }); + } + + let client; + + if (baseConfig.endpoint !== null) { + if (typeof baseConfig.endpoint === 'string') { + const endpoint = clientConfig.getEndpoint(baseConfig.endpoint); + if (!endpoint) { + throw new Error(`There is no '${ baseConfig.endpoint || 'default' }' endpoint registered.`); + } + client = endpoint; + } else if (baseConfig.endpoint instanceof HttpClient) { + client = new Rest(baseConfig.endpoint); } + } - return this; + if (!(client instanceof Rest)) { + client = new Rest(aurelia.container.get(HttpClient)); } - eventListener(redirectUri) { - return new Promise((resolve, reject) => { - this.popupWindow.addEventListener('loadstart', event => { - if (event.url.indexOf(redirectUri) !== 0) { - return; - } + baseConfig.client = client; +} - const parser = DOM.createElement('a'); - parser.href = event.url; +export let Auth0Lock = (_dec = inject(Storage, BaseConfig), _dec(_class = 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' + }; + } - if (parser.search || parser.hash) { - const qs = parseUrl(parser); + open(options, userData) { + 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 (qs.error) { - reject({ error: qs.error }); - } else { - resolve(qs); - } + if (typeof provider.state === 'function') { + this.storage.set(stateName, provider.state()); + } else if (typeof provider.state === 'string') { + this.storage.set(stateName, provider.state); + } - this.popupWindow.close(); + this.lock = this.lock || new PLATFORM.global.Auth0Lock(provider.clientId, provider.clientDomain); + + const openPopup = new Promise((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({ + access_token: tokenOrCode + }); } }); + }); - this.popupWindow.addEventListener('exit', () => { - reject({ data: 'Provider Popup was closed' }); - }); + return openPopup.then(lockResponse => { + if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { + return lockResponse; + } - this.popupWindow.addEventListener('loaderror', () => { - reject({ data: 'Authorization Failed' }); - }); + throw new Error('Only `token` responseType is supported'); }); } +}) || _class); - pollPopup() { - return new Promise((resolve, reject) => { - this.polling = PLATFORM.global.setInterval(() => { - let errorData; +export let AuthenticateStep = (_dec2 = inject(AuthService), _dec2(_class2 = class AuthenticateStep { + constructor(authService) { + this.authService = authService; + } - try { - if (this.popupWindow.location.host === PLATFORM.global.document.location.host && (this.popupWindow.location.search || this.popupWindow.location.hash)) { - const qs = parseUrl(this.popupWindow.location); + run(routingContext, next) { + const isLoggedIn = this.authService.authenticated; + const loginRoute = this.authService.config.loginRoute; - if (qs.error) { - reject({ error: qs.error }); - } else { - resolve(qs); - } + if (routingContext.getAllInstructions().some(route => route.config.auth === true)) { + if (!isLoggedIn) { + return next.cancel(new Redirect(loginRoute)); + } + } else if (isLoggedIn && routingContext.getAllInstructions().some(route => route.fragment === loginRoute)) { + return next.cancel(new Redirect(this.authService.config.loginRedirect)); + } - this.popupWindow.close(); - PLATFORM.global.clearInterval(this.polling); - } - } catch (error) { - errorData = error; - } + return next(); + } +}) || _class2); - if (!this.popupWindow) { - PLATFORM.global.clearInterval(this.polling); - reject({ - error: errorData, - data: 'Provider Popup Blocked' - }); - } else if (this.popupWindow.closed) { - PLATFORM.global.clearInterval(this.polling); - reject({ - error: errorData, - data: 'Problem poll popup' - }); - } - }, 35); - }); +export let Authentication = (_dec3 = inject(Storage, BaseConfig, OAuth1, OAuth2, Auth0Lock), _dec4 = deprecated({ message: 'Use baseConfig.loginRoute instead.' }), _dec5 = deprecated({ message: 'Use baseConfig.loginRedirect instead.' }), _dec6 = deprecated({ message: 'Use baseConfig.joinBase(baseConfig.loginUrl) instead.' }), _dec7 = deprecated({ message: 'Use baseConfig.joinBase(baseConfig.signupUrl) instead.' }), _dec8 = deprecated({ message: 'Use baseConfig.joinBase(baseConfig.profileUrl) instead.' }), _dec9 = deprecated({ message: 'Use .getAccessToken() instead.' }), _dec3(_class3 = (_class4 = class Authentication { + 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; + this.payload = null; + this.exp = null; + this.hasDataStored = false; } -}; -const buildPopupWindowOptions = options => { - const width = options.width || 500; - const height = options.height || 500; + getLoginRoute() { + return this.config.loginRoute; + } - const extended = extend({ - width: width, - height: height, - left: PLATFORM.global.screenX + (PLATFORM.global.outerWidth - width) / 2, - top: PLATFORM.global.screenY + (PLATFORM.global.outerHeight - height) / 2.5 - }, options); + getLoginRedirect() { + return this.config.loginRedirect; + } - let parts = []; - Object.keys(extended).map(key => parts.push(key + '=' + extended[key])); + getLoginUrl() { + return this.Config.joinBase(this.config.loginUrl); + } - return parts.join(','); -}; + getSignupUrl() { + return this.Config.joinBase(this.config.signupUrl); + } -const parseUrl = url => { - return extend(true, {}, parseQueryString(url.search), parseQueryString(url.hash)); -}; + getProfileUrl() { + return this.Config.joinBase(this.config.profileUrl); + } -export let BaseConfig = class BaseConfig { - constructor() { - this.client = null; - this.endpoint = null; - this.configureEndpoints = null; - this.loginRedirect = '#/'; - this.logoutRedirect = '#/'; - this.loginRoute = '/login'; - this.loginOnSignup = true; - this.signupRedirect = '#/login'; - this.expiredRedirect = 0; - this.baseUrl = ''; - this.loginUrl = '/auth/login'; - this.logoutUrl = null; - this.logoutMethod = 'get'; - this.signupUrl = '/auth/signup'; - this.profileUrl = '/auth/me'; - this.profileMethod = 'put'; - this.unlinkUrl = '/auth/unlink/'; - this.unlinkMethod = 'get'; - this.refreshTokenUrl = null; - this.authHeader = 'Authorization'; - this.authTokenType = 'Bearer'; - this.accessTokenProp = 'access_token'; - this.accessTokenName = 'token'; - this.accessTokenRoot = false; - this.useRefreshToken = false; - this.autoUpdateToken = true; - this.clientId = false; - this.refreshTokenProp = 'refresh_token'; - this.refreshTokenName = 'token'; - this.refreshTokenRoot = false; - this.httpInterceptor = true; - this.withCredentials = true; - this.platform = 'browser'; - this.storage = 'localStorage'; - this.storageKey = 'aurelia_authentication'; - this.globalValueConverters = ['authFilterValueConverter']; - this.providers = { - facebook: { - name: 'facebook', - url: '/auth/facebook', - authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', - redirectUri: PLATFORM.location.origin + '/', - requiredUrlParams: ['display', 'scope'], - scope: ['email'], - scopeDelimiter: ',', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 580, height: 400 } - }, - google: { - name: 'google', - url: '/auth/google', - authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth', - redirectUri: PLATFORM.location.origin, - requiredUrlParams: ['scope'], - optionalUrlParams: ['display', 'state'], - scope: ['profile', 'email'], - scopePrefix: 'openid', - scopeDelimiter: ' ', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 452, height: 633 }, - state: randomState - }, - github: { - name: 'github', - url: '/auth/github', - authorizationEndpoint: 'https://github.com/login/oauth/authorize', - redirectUri: PLATFORM.location.origin, - optionalUrlParams: ['scope'], - scope: ['user:email'], - scopeDelimiter: ' ', - oauthType: '2.0', - popupOptions: { width: 1020, height: 618 } - }, - instagram: { - name: 'instagram', - url: '/auth/instagram', - authorizationEndpoint: 'https://api.instagram.com/oauth/authorize', - redirectUri: PLATFORM.location.origin, - requiredUrlParams: ['scope'], - scope: ['basic'], - scopeDelimiter: '+', - oauthType: '2.0' - }, - linkedin: { - name: 'linkedin', - url: '/auth/linkedin', - authorizationEndpoint: 'https://www.linkedin.com/uas/oauth2/authorization', - redirectUri: PLATFORM.location.origin, - requiredUrlParams: ['state'], - scope: ['r_emailaddress'], - scopeDelimiter: ' ', - state: 'STATE', - oauthType: '2.0', - popupOptions: { width: 527, height: 582 } - }, - twitter: { - name: 'twitter', - url: '/auth/twitter', - authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', - redirectUri: PLATFORM.location.origin, - oauthType: '1.0', - popupOptions: { width: 495, height: 645 } - }, - twitch: { - name: 'twitch', - url: '/auth/twitch', - authorizationEndpoint: 'https://api.twitch.tv/kraken/oauth2/authorize', - redirectUri: PLATFORM.location.origin, - requiredUrlParams: ['scope'], - scope: ['user_read'], - scopeDelimiter: ' ', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 500, height: 560 } - }, - live: { - name: 'live', - url: '/auth/live', - authorizationEndpoint: 'https://login.live.com/oauth20_authorize.srf', - redirectUri: PLATFORM.location.origin, - requiredUrlParams: ['display', 'scope'], - scope: ['wl.emails'], - scopeDelimiter: ' ', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 500, height: 560 } - }, - yahoo: { - name: 'yahoo', - url: '/auth/yahoo', - authorizationEndpoint: 'https://api.login.yahoo.com/oauth2/request_auth', - redirectUri: PLATFORM.location.origin, - scope: [], - scopeDelimiter: ',', - oauthType: '2.0', - popupOptions: { width: 559, height: 519 } - }, - bitbucket: { - name: 'bitbucket', - url: '/auth/bitbucket', - authorizationEndpoint: 'https://bitbucket.org/site/oauth2/authorize', - redirectUri: PLATFORM.location.origin + '/', - requiredUrlParams: ['scope'], - scope: ['email'], - 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: randomState - } - }; - this._authToken = 'Bearer'; - this._responseTokenProp = 'access_token'; - this._tokenName = 'token'; - this._tokenRoot = false; - this._tokenPrefix = 'aurelia'; + getToken() { + return this.getAccessToken(); } - joinBase(url) { - return join(this.baseUrl, url); + get responseObject() { + LogManager.getLogger('authentication').warn('Getter Authentication.responseObject is deprecated. Use Authentication.getResponseObject() instead.'); + return this.getResponseObject(); } - configure(incomming) { - for (let key in incomming) { - const value = incomming[key]; - if (value !== undefined) { - if (Array.isArray(value) || typeof value !== 'object' || value === null) { - this[key] = value; - } else { - extend(true, this[key], value); - } - } - } + set responseObject(response) { + LogManager.getLogger('authentication').warn('Setter Authentication.responseObject is deprecated. Use AuthServive.setResponseObject(response) instead.'); + this.setResponseObject(response); } - set authToken(authToken) { - LogManager.getLogger('authentication').warn('BaseConfig.authToken is deprecated. Use BaseConfig.authTokenType instead.'); - this._authTokenType = authToken; - this.authTokenType = authToken; - return authToken; - } - get authToken() { - return this._authTokenType; + getResponseObject() { + return JSON.parse(this.storage.get(this.config.storageKey)); } - set responseTokenProp(responseTokenProp) { - LogManager.getLogger('authentication').warn('BaseConfig.responseTokenProp is deprecated. Use BaseConfig.accessTokenProp instead.'); - this._responseTokenProp = responseTokenProp; - this.accessTokenProp = responseTokenProp; - return responseTokenProp; - } - get responseTokenProp() { - return this._responseTokenProp; - } + setResponseObject(response) { + if (response) { + this.getDataFromResponse(response); + this.storage.set(this.config.storageKey, JSON.stringify(response)); + return; + } + this.accessToken = null; + this.refreshToken = null; + this.payload = null; + this.exp = null; - set tokenRoot(tokenRoot) { - LogManager.getLogger('authentication').warn('BaseConfig.tokenRoot is deprecated. Use BaseConfig.accessTokenRoot instead.'); - this._tokenRoot = tokenRoot; - this.accessTokenRoot = tokenRoot; - return tokenRoot; - } - get tokenRoot() { - return this._tokenRoot; - } + this.hasDataStored = false; - set tokenName(tokenName) { - LogManager.getLogger('authentication').warn('BaseConfig.tokenName is deprecated. Use BaseConfig.accessTokenName instead.'); - this._tokenName = tokenName; - this.accessTokenName = tokenName; - return tokenName; + this.storage.remove(this.config.storageKey); } - get tokenName() { - return this._tokenName; + + getAccessToken() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.accessToken; } - set tokenPrefix(tokenPrefix) { - LogManager.getLogger('authentication').warn('BaseConfig.tokenPrefix is obsolete. Use BaseConfig.storageKey instead.'); - this._tokenPrefix = tokenPrefix; - return tokenPrefix; + getRefreshToken() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.refreshToken; } - get tokenPrefix() { - return this._tokenPrefix || 'aurelia'; + + getPayload() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.payload; } - get current() { - LogManager.getLogger('authentication').warn('Getter BaseConfig.current is deprecated. Use BaseConfig directly instead.'); - return this; + getExp() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.exp; } - set current(_) { - throw new Error('Setter BaseConfig.current is obsolete. Use BaseConfig directly instead.'); + + getTtl() { + const exp = this.getExp(); + return Number.isNaN(exp) ? NaN : exp - Math.round(new Date().getTime() / 1000); } - get _current() { - LogManager.getLogger('authentication').warn('Getter BaseConfig._current is deprecated. Use BaseConfig directly instead.'); - return this; + isTokenExpired() { + const timeLeft = this.getTtl(); + return Number.isNaN(timeLeft) ? undefined : timeLeft < 0; } - set _current(_) { - throw new Error('Setter BaseConfig._current is obsolete. Use BaseConfig directly instead.'); + + isAuthenticated() { + const isTokenExpired = this.isTokenExpired(); + if (isTokenExpired === undefined) return this.accessToken ? true : false; + return !isTokenExpired; } -}; -function randomState() { - let rand = Math.random().toString(36).substr(2); - return encodeURIComponent(rand); -} + getDataFromResponse(response) { + const config = this.config; -export let Storage = (_dec = inject(BaseConfig), _dec(_class2 = class Storage { - constructor(config) { - this.config = config; - } + this.accessToken = this.getTokenFromResponse(response, config.accessTokenProp, config.accessTokenName, config.accessTokenRoot); - get(key) { - return PLATFORM.global[this.config.storage].getItem(key); + this.refreshToken = null; + if (config.useRefreshToken) { + try { + this.refreshToken = this.getTokenFromResponse(response, config.refreshTokenProp, config.refreshTokenName, config.refreshTokenRoot); + } catch (e) { + this.refreshToken = null; + } + } + + this.payload = null; + + try { + this.payload = this.accessToken ? jwtDecode(this.accessToken) : null; + } catch (_) { + _; + } + + this.exp = this.payload ? parseInt(this.payload.exp, 10) : NaN; + + this.hasDataStored = true; + + return { + accessToken: this.accessToken, + refreshToken: this.refreshToken, + payload: this.payload, + exp: this.exp + }; } - set(key, value) { - PLATFORM.global[this.config.storage].setItem(key, value); + getTokenFromResponse(response, tokenProp, tokenName, tokenRoot) { + if (!response) return undefined; + + const responseTokenProp = tokenProp.split('.').reduce((o, x) => o[x], response); + + if (typeof responseTokenProp === 'string') { + return responseTokenProp; + } + + if (typeof responseTokenProp === 'object') { + const tokenRootData = tokenRoot && tokenRoot.split('.').reduce((o, x) => o[x], responseTokenProp); + const token = tokenRootData ? tokenRootData[tokenName] : responseTokenProp[tokenName]; + + if (!token) throw new Error('Token not found in response'); + + return token; + } + + const token = response[tokenName] === undefined ? null : response[tokenName]; + + if (!token) throw new Error('Token not found in response'); + + return token; } - remove(key) { - PLATFORM.global[this.config.storage].removeItem(key); + toUpdateTokenCallstack() { + return new Promise(resolve => this.updateTokenCallstack.push(resolve)); } -}) || _class2); -export let Auth0Lock = (_dec2 = inject(Storage, BaseConfig), _dec2(_class3 = 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' - }; + resolveUpdateTokenCallstack(response) { + this.updateTokenCallstack.map(resolve => resolve(response)); + this.updateTokenCallstack = []; } - open(options, userData) { - if (typeof PLATFORM.global.Auth0Lock !== 'function') { - throw new Error('Auth0Lock was not found in global scope. Please load it before using this provider.'); + authenticate(name, userData = {}) { + let oauthType = this.config.providers[name].type; + + if (oauthType) { + LogManager.getLogger('authentication').warn('DEPRECATED: Setting provider.type is deprecated and replaced by provider.oauthType'); + } else { + oauthType = this.config.providers[name].oauthType; } - 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); + let providerLogin; + if (oauthType === 'auth0-lock') { + providerLogin = this.auth0Lock; + } else { + providerLogin = oauthType === '1.0' ? this.oAuth1 : this.oAuth2; } - this.lock = this.lock || new PLATFORM.global.Auth0Lock(provider.clientId, provider.clientDomain); + return providerLogin.open(this.config.providers[name], userData); + } - const openPopup = new Promise((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'); + redirect(redirectUrl, defaultRedirectUrl) { + if (redirectUrl === true) { + LogManager.getLogger('authentication').warn('DEPRECATED: Setting redirectUrl === true to actually *not redirect* is deprecated. Set redirectUrl === 0 instead.'); + return; + } + + if (redirectUrl === false) { + LogManager.getLogger('authentication').warn('BREAKING CHANGE: Setting redirectUrl === false to actually *do redirect* is deprecated. Set redirectUrl to undefined or null to use the defaultRedirectUrl if so desired.'); + } + + if (redirectUrl === 0) { + return; + } + if (typeof redirectUrl === 'string') { + PLATFORM.location.href = encodeURI(redirectUrl); + } else if (defaultRedirectUrl) { + PLATFORM.location.href = defaultRedirectUrl; + } + } +}, (_applyDecoratedDescriptor(_class4.prototype, 'getLoginRoute', [_dec4], Object.getOwnPropertyDescriptor(_class4.prototype, 'getLoginRoute'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getLoginRedirect', [_dec5], Object.getOwnPropertyDescriptor(_class4.prototype, 'getLoginRedirect'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getLoginUrl', [_dec6], Object.getOwnPropertyDescriptor(_class4.prototype, 'getLoginUrl'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getSignupUrl', [_dec7], Object.getOwnPropertyDescriptor(_class4.prototype, 'getSignupUrl'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getProfileUrl', [_dec8], Object.getOwnPropertyDescriptor(_class4.prototype, 'getProfileUrl'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getToken', [_dec9], Object.getOwnPropertyDescriptor(_class4.prototype, 'getToken'), _class4.prototype)), _class4)) || _class3); - this.lock.show(provider.lockOptions, (err, profile, tokenOrCode) => { - if (err) { - reject(err); - } else { - resolve({ - access_token: tokenOrCode - }); - } - }); - }); +export let AuthorizeStep = (_dec10 = inject(AuthService), _dec10(_class5 = class AuthorizeStep { + constructor(authService) { + LogManager.getLogger('authentication').warn('AuthorizeStep is deprecated. Use AuthenticationStep instead.'); - return openPopup.then(lockResponse => { - if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { - return lockResponse; + this.authService = authService; + } + + run(routingContext, next) { + const isLoggedIn = this.authService.isAuthenticated(); + const loginRoute = this.authService.config.loginRoute; + + if (routingContext.getAllInstructions().some(route => route.config.auth)) { + if (!isLoggedIn) { + return next.cancel(new Redirect(loginRoute)); } + } else if (isLoggedIn && routingContext.getAllInstructions().some(route => route.fragment === loginRoute)) { + return next.cancel(new Redirect(this.authService.config.loginRedirect)); + } - throw new Error('Only `token` responseType is supported'); - }); + return next(); } -}) || _class3); +}) || _class5); -export let OAuth1 = (_dec3 = inject(Storage, Popup, BaseConfig), _dec3(_class4 = class OAuth1 { - constructor(storage, popup, config) { - this.storage = storage; +export let AuthService = (_dec11 = inject(Authentication, BaseConfig), _dec12 = deprecated({ message: 'Use .getAccessToken() instead.' }), _dec11(_class6 = (_class7 = class AuthService { + constructor(authentication, config) { + this.authenticated = false; + this.timeoutID = 0; + + this.authentication = authentication; this.config = config; - this.popup = popup; - this.defaults = { - url: null, - name: null, - popupOptions: null, - redirectUri: null, - authorizationEndpoint: null - }; - } - open(options, userData) { - const provider = extend(true, {}, this.defaults, options); - const serverUrl = this.config.joinBase(provider.url); + const oldStorageKey = config.tokenPrefix ? config.tokenPrefix + '_' + config.tokenName : config.tokenName; + const oldToken = authentication.storage.get(oldStorageKey); - if (this.config.platform !== 'mobile') { - this.popup = this.popup.open('', provider.name, provider.popupOptions); + if (oldToken) { + LogManager.getLogger('authentication').info('Found token with deprecated format in storage. Converting it to new format. No further action required.'); + let fakeOldResponse = {}; + fakeOldResponse[config.accessTokenProp] = oldToken; + this.setResponseObject(fakeOldResponse); + authentication.storage.remove(oldStorageKey); } - return this.config.client.post(serverUrl).then(response => { - const url = provider.authorizationEndpoint + '?' + buildQueryString(response); - - if (this.config.platform === 'mobile') { - this.popup = this.popup.open(url, provider.name, provider.popupOptions); - } else { - this.popup.popupWindow.location = url; - } + this.setResponseObject(this.authentication.getResponseObject()); + } - const popupListener = this.config.platform === 'mobile' ? this.popup.eventListener(provider.redirectUri) : this.popup.pollPopup(); + get client() { + return this.config.client; + } - return popupListener.then(result => this.exchangeForToken(result, userData, provider)); - }); + get auth() { + LogManager.getLogger('authentication').warn('AuthService.auth is deprecated. Use .authentication instead.'); + return this.authentication; } - exchangeForToken(oauthData, userData, provider) { - const data = extend(true, {}, userData, oauthData); - const serverUrl = this.config.joinBase(provider.url); - const credentials = this.config.withCredentials ? 'include' : 'same-origin'; + setTimeout(ttl) { + this.clearTimeout(); - return this.config.client.post(serverUrl, data, { credentials: credentials }); + this.timeoutID = PLATFORM.global.setTimeout(() => { + if (this.config.autoUpdateToken && this.authentication.getAccessToken() && this.authentication.getRefreshToken()) { + this.updateToken(); + } else { + this.logout(this.config.expiredRedirect); + } + }, ttl); } -}) || _class4); -export let OAuth2 = (_dec4 = inject(Storage, Popup, BaseConfig), _dec4(_class5 = class OAuth2 { - constructor(storage, popup, config) { - this.storage = storage; - this.config = config; - this.popup = popup; - this.defaults = { - url: null, - name: null, - state: null, - scope: null, - scopeDelimiter: null, - redirectUri: null, - popupOptions: null, - authorizationEndpoint: null, - responseParams: null, - requiredUrlParams: null, - optionalUrlParams: null, - defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], - responseType: 'code' - }; + clearTimeout() { + if (this.timeoutID) { + PLATFORM.global.clearTimeout(this.timeoutID); + } + this.timeoutID = 0; } - open(options, userData) { - const provider = extend(true, {}, this.defaults, options); - const stateName = provider.name + '_state'; + setResponseObject(response) { + this.clearTimeout(); - if (typeof provider.state === 'function') { - this.storage.set(stateName, provider.state()); - } else if (typeof provider.state === 'string') { - this.storage.set(stateName, provider.state); - } + this.authentication.setResponseObject(response); - const url = provider.authorizationEndpoint + '?' + buildQueryString(this.buildQuery(provider)); - const popup = this.popup.open(url, provider.name, provider.popupOptions); - const openPopup = this.config.platform === 'mobile' ? popup.eventListener(provider.redirectUri) : popup.pollPopup(); + this.authenticated = this.authentication.isAuthenticated(); + if (this.authenticated && !Number.isNaN(this.authentication.exp)) { + this.setTimeout(this.getTtl() * 1000); + } + } - return openPopup.then(oauthData => { - if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { - return oauthData; - } - if (oauthData.state && oauthData.state !== this.storage.get(stateName)) { - return Promise.reject('OAuth 2.0 state parameter mismatch.'); - } - return this.exchangeForToken(oauthData, userData, provider); - }); + getMe(criteriaOrId) { + if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { + criteriaOrId = { id: criteriaOrId }; + } + return this.client.find(this.config.joinBase(this.config.profileUrl), criteriaOrId); } - exchangeForToken(oauthData, userData, provider) { - const data = extend(true, {}, userData, { - clientId: provider.clientId, - redirectUri: provider.redirectUri - }, oauthData); + updateMe(body, criteriaOrId) { + if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { + criteriaOrId = { id: criteriaOrId }; + } + if (this.config.profileMethod === 'put') { + return this.client.update(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); + } + return this.client.patch(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); + } - const serverUrl = this.config.joinBase(provider.url); - const credentials = this.config.withCredentials ? 'include' : 'same-origin'; + getAccessToken() { + return this.authentication.getAccessToken(); + } - return this.config.client.post(serverUrl, data, { credentials: credentials }); + getCurrentToken() { + return this.getAccessToken(); } - buildQuery(provider) { - let query = {}; - const urlParams = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams']; + getRefreshToken() { + return this.authentication.getRefreshToken(); + } - urlParams.forEach(params => { - (provider[params] || []).forEach(paramName => { - const camelizedName = camelCase(paramName); - let paramValue = typeof provider[paramName] === 'function' ? provider[paramName]() : provider[camelizedName]; + isAuthenticated() { + let authenticated = this.authentication.isAuthenticated(); - if (paramName === 'state') { - paramValue = encodeURIComponent(this.storage.get(provider.name + '_state')); - } + if (!authenticated && this.config.autoUpdateToken && this.authentication.getAccessToken() && this.authentication.getRefreshToken()) { + this.updateToken(); + authenticated = true; + } - if (paramName === 'scope' && Array.isArray(paramValue)) { - paramValue = paramValue.join(provider.scopeDelimiter); + return authenticated; + } - if (provider.scopePrefix) { - paramValue = provider.scopePrefix + provider.scopeDelimiter + paramValue; - } - } + getExp() { + return this.authentication.getExp(); + } - query[paramName] = paramValue; - }); - }); - return query; + getTtl() { + return this.authentication.getTtl(); } -}) || _class5); -const camelCase = function (name) { - return name.replace(/([\:\-\_]+(.))/g, function (_, separator, letter, offset) { - return offset ? letter.toUpperCase() : letter; - }); -}; + isTokenExpired() { + return this.authentication.isTokenExpired(); + } -export let Authentication = (_dec5 = inject(Storage, BaseConfig, OAuth1, OAuth2, Auth0Lock), _dec6 = deprecated({ message: 'Use baseConfig.loginRoute instead.' }), _dec7 = deprecated({ message: 'Use baseConfig.loginRedirect instead.' }), _dec8 = deprecated({ message: 'Use baseConfig.joinBase(baseConfig.loginUrl) instead.' }), _dec9 = deprecated({ message: 'Use baseConfig.joinBase(baseConfig.signupUrl) instead.' }), _dec10 = deprecated({ message: 'Use baseConfig.joinBase(baseConfig.profileUrl) instead.' }), _dec11 = deprecated({ message: 'Use .getAccessToken() instead.' }), _dec5(_class6 = (_class7 = class Authentication { - 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; - this.payload = null; - this.exp = null; - this.hasDataStored = false; + getTokenPayload() { + return this.authentication.getPayload(); } - getLoginRoute() { - return this.config.loginRoute; + updateToken() { + if (!this.authentication.getRefreshToken()) { + return Promise.reject(new Error('refreshToken not set')); + } + + if (this.authentication.updateTokenCallstack.length === 0) { + const content = { + grant_type: 'refresh_token', + refresh_token: this.authentication.getRefreshToken(), + client_id: this.config.clientId ? this.config.clientId : undefined + }; + + this.client.post(this.config.joinBase(this.config.refreshTokenUrl ? this.config.refreshTokenUrl : this.config.loginUrl), content).then(response => { + this.setResponseObject(response); + this.authentication.resolveUpdateTokenCallstack(this.isAuthenticated()); + }).catch(err => { + this.setResponseObject(null); + this.authentication.resolveUpdateTokenCallstack(Promise.reject(err)); + }); + } + + return this.authentication.toUpdateTokenCallstack(); } - getLoginRedirect() { - return this.config.loginRedirect; - } + signup(displayNameOrCredentials, emailOrOptions, passwordOrRedirectUri, options, redirectUri) { + let content; - getLoginUrl() { - return this.Config.joinBase(this.config.loginUrl); - } + if (typeof arguments[0] === 'object') { + content = arguments[0]; + options = arguments[1]; + redirectUri = arguments[2]; + } else { + content = { + 'displayName': displayNameOrCredentials, + 'email': emailOrOptions, + 'password': passwordOrRedirectUri + }; + } + return this.client.post(this.config.joinBase(this.config.signupUrl), content, options).then(response => { + if (this.config.loginOnSignup) { + this.setResponseObject(response); + } + this.authentication.redirect(redirectUri, this.config.signupRedirect); - getSignupUrl() { - return this.Config.joinBase(this.config.signupUrl); + return response; + }); } - getProfileUrl() { - return this.Config.joinBase(this.config.profileUrl); - } + login(emailOrCredentials, passwordOrOptions, optionsOrRedirectUri, redirectUri) { + let content; - getToken() { - return this.getAccessToken(); - } + if (typeof arguments[0] === 'object') { + content = arguments[0]; + optionsOrRedirectUri = arguments[1]; + redirectUri = arguments[2]; + } else { + content = { + 'email': emailOrCredentials, + 'password': passwordOrOptions + }; + optionsOrRedirectUri = optionsOrRedirectUri; + } - get responseObject() { - LogManager.getLogger('authentication').warn('Getter Authentication.responseObject is deprecated. Use Authentication.getResponseObject() instead.'); - return this.getResponseObject(); - } + if (this.config.clientId) { + content.client_id = this.config.clientId; + } - set responseObject(response) { - LogManager.getLogger('authentication').warn('Setter Authentication.responseObject is deprecated. Use AuthServive.setResponseObject(response) instead.'); - this.setResponseObject(response); - } + return this.client.post(this.config.joinBase(this.config.loginUrl), content, optionsOrRedirectUri).then(response => { + this.setResponseObject(response); - getResponseObject() { - return JSON.parse(this.storage.get(this.config.storageKey)); + this.authentication.redirect(redirectUri, this.config.loginRedirect); + + return response; + }); } - setResponseObject(response) { - if (response) { - this.getDataFromResponse(response); - this.storage.set(this.config.storageKey, JSON.stringify(response)); - return; - } - this.accessToken = null; - this.refreshToken = null; - this.payload = null; - this.exp = null; + logout(redirectUri) { + let localLogout = response => new Promise(resolve => { + this.setResponseObject(null); - this.hasDataStored = false; + this.authentication.redirect(redirectUri, this.config.logoutRedirect); - this.storage.remove(this.config.storageKey); - } + if (typeof this.onLogout === 'function') { + this.onLogout(response); + } - getAccessToken() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.accessToken; - } + resolve(response); + }); - getRefreshToken() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.refreshToken; + return this.config.logoutUrl ? this.client.request(this.config.logoutMethod, this.config.joinBase(this.config.logoutUrl)).then(localLogout) : localLogout(); } - getPayload() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.payload; - } + authenticate(name, redirectUri, userData = {}) { + return this.authentication.authenticate(name, userData).then(response => { + this.setResponseObject(response); - getExp() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.exp; - } + this.authentication.redirect(redirectUri, this.config.loginRedirect); - getTtl() { - const exp = this.getExp(); - return Number.isNaN(exp) ? NaN : exp - Math.round(new Date().getTime() / 1000); + return response; + }); } - isTokenExpired() { - const timeLeft = this.getTtl(); - return Number.isNaN(timeLeft) ? undefined : timeLeft < 0; - } + unlink(name, redirectUri) { + const unlinkUrl = this.config.joinBase(this.config.unlinkUrl) + name; + return this.client.request(this.config.unlinkMethod, unlinkUrl).then(response => { + this.authentication.redirect(redirectUri); - isAuthenticated() { - const isTokenExpired = this.isTokenExpired(); - if (isTokenExpired === undefined) return this.accessToken ? true : false; - return !isTokenExpired; + return response; + }); } +}, (_applyDecoratedDescriptor(_class7.prototype, 'getCurrentToken', [_dec12], Object.getOwnPropertyDescriptor(_class7.prototype, 'getCurrentToken'), _class7.prototype)), _class7)) || _class6); - getDataFromResponse(response) { - const config = this.config; - - this.accessToken = this.getTokenFromResponse(response, config.accessTokenProp, config.accessTokenName, config.accessTokenRoot); - - this.refreshToken = null; - if (config.useRefreshToken) { - try { - this.refreshToken = this.getTokenFromResponse(response, config.refreshTokenProp, config.refreshTokenName, config.refreshTokenRoot); - } catch (e) { - this.refreshToken = null; +export let BaseConfig = class BaseConfig { + constructor() { + this.client = null; + this.endpoint = null; + this.configureEndpoints = null; + this.loginRedirect = '#/'; + this.logoutRedirect = '#/'; + this.loginRoute = '/login'; + this.loginOnSignup = true; + this.signupRedirect = '#/login'; + this.expiredRedirect = 0; + this.baseUrl = ''; + this.loginUrl = '/auth/login'; + this.logoutUrl = null; + this.logoutMethod = 'get'; + this.signupUrl = '/auth/signup'; + this.profileUrl = '/auth/me'; + this.profileMethod = 'put'; + this.unlinkUrl = '/auth/unlink/'; + this.unlinkMethod = 'get'; + this.refreshTokenUrl = null; + this.authHeader = 'Authorization'; + this.authTokenType = 'Bearer'; + this.accessTokenProp = 'access_token'; + this.accessTokenName = 'token'; + this.accessTokenRoot = false; + this.useRefreshToken = false; + this.autoUpdateToken = true; + this.clientId = false; + this.refreshTokenProp = 'refresh_token'; + this.refreshTokenName = 'token'; + this.refreshTokenRoot = false; + this.httpInterceptor = true; + this.withCredentials = true; + this.platform = 'browser'; + this.storage = 'localStorage'; + this.storageKey = 'aurelia_authentication'; + this.globalValueConverters = ['authFilterValueConverter']; + this.providers = { + facebook: { + name: 'facebook', + url: '/auth/facebook', + authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', + redirectUri: PLATFORM.location.origin + '/', + requiredUrlParams: ['display', 'scope'], + scope: ['email'], + scopeDelimiter: ',', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 580, height: 400 } + }, + google: { + name: 'google', + url: '/auth/google', + authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['scope'], + optionalUrlParams: ['display', 'state'], + scope: ['profile', 'email'], + scopePrefix: 'openid', + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 452, height: 633 }, + state: randomState + }, + github: { + name: 'github', + url: '/auth/github', + authorizationEndpoint: 'https://github.com/login/oauth/authorize', + redirectUri: PLATFORM.location.origin, + optionalUrlParams: ['scope'], + scope: ['user:email'], + scopeDelimiter: ' ', + oauthType: '2.0', + popupOptions: { width: 1020, height: 618 } + }, + instagram: { + name: 'instagram', + url: '/auth/instagram', + authorizationEndpoint: 'https://api.instagram.com/oauth/authorize', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['scope'], + scope: ['basic'], + scopeDelimiter: '+', + oauthType: '2.0' + }, + linkedin: { + name: 'linkedin', + url: '/auth/linkedin', + authorizationEndpoint: 'https://www.linkedin.com/uas/oauth2/authorization', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['state'], + scope: ['r_emailaddress'], + scopeDelimiter: ' ', + state: 'STATE', + oauthType: '2.0', + popupOptions: { width: 527, height: 582 } + }, + twitter: { + name: 'twitter', + url: '/auth/twitter', + authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', + redirectUri: PLATFORM.location.origin, + oauthType: '1.0', + popupOptions: { width: 495, height: 645 } + }, + twitch: { + name: 'twitch', + url: '/auth/twitch', + authorizationEndpoint: 'https://api.twitch.tv/kraken/oauth2/authorize', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['scope'], + scope: ['user_read'], + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 500, height: 560 } + }, + live: { + name: 'live', + url: '/auth/live', + authorizationEndpoint: 'https://login.live.com/oauth20_authorize.srf', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['display', 'scope'], + scope: ['wl.emails'], + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 500, height: 560 } + }, + yahoo: { + name: 'yahoo', + url: '/auth/yahoo', + authorizationEndpoint: 'https://api.login.yahoo.com/oauth2/request_auth', + redirectUri: PLATFORM.location.origin, + scope: [], + scopeDelimiter: ',', + oauthType: '2.0', + popupOptions: { width: 559, height: 519 } + }, + bitbucket: { + name: 'bitbucket', + url: '/auth/bitbucket', + authorizationEndpoint: 'https://bitbucket.org/site/oauth2/authorize', + redirectUri: PLATFORM.location.origin + '/', + requiredUrlParams: ['scope'], + scope: ['email'], + 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: randomState } - } - - this.payload = null; - - try { - this.payload = this.accessToken ? jwtDecode(this.accessToken) : null; - } catch (_) { - _; - } - - this.exp = this.payload ? parseInt(this.payload.exp, 10) : NaN; - - this.hasDataStored = true; - - return { - accessToken: this.accessToken, - refreshToken: this.refreshToken, - payload: this.payload, - exp: this.exp }; + this._authToken = 'Bearer'; + this._responseTokenProp = 'access_token'; + this._tokenName = 'token'; + this._tokenRoot = false; + this._tokenPrefix = 'aurelia'; } - getTokenFromResponse(response, tokenProp, tokenName, tokenRoot) { - if (!response) return undefined; - - const responseTokenProp = tokenProp.split('.').reduce((o, x) => o[x], response); - - if (typeof responseTokenProp === 'string') { - return responseTokenProp; - } - - if (typeof responseTokenProp === 'object') { - const tokenRootData = tokenRoot && tokenRoot.split('.').reduce((o, x) => o[x], responseTokenProp); - const token = tokenRootData ? tokenRootData[tokenName] : responseTokenProp[tokenName]; - - if (!token) throw new Error('Token not found in response'); - - return token; - } - - const token = response[tokenName] === undefined ? null : response[tokenName]; - - if (!token) throw new Error('Token not found in response'); - - return token; + joinBase(url) { + return join(this.baseUrl, url); } - toUpdateTokenCallstack() { - return new Promise(resolve => this.updateTokenCallstack.push(resolve)); + configure(incomming) { + for (let key in incomming) { + const value = incomming[key]; + if (value !== undefined) { + if (Array.isArray(value) || typeof value !== 'object' || value === null) { + this[key] = value; + } else { + extend(true, this[key], value); + } + } + } } - resolveUpdateTokenCallstack(response) { - this.updateTokenCallstack.map(resolve => resolve(response)); - this.updateTokenCallstack = []; + set authToken(authToken) { + LogManager.getLogger('authentication').warn('BaseConfig.authToken is deprecated. Use BaseConfig.authTokenType instead.'); + this._authTokenType = authToken; + this.authTokenType = authToken; + return authToken; } - - authenticate(name, userData = {}) { - let oauthType = this.config.providers[name].type; - - if (oauthType) { - LogManager.getLogger('authentication').warn('DEPRECATED: Setting provider.type is deprecated and replaced by provider.oauthType'); - } else { - oauthType = this.config.providers[name].oauthType; - } - - 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); + get authToken() { + return this._authTokenType; } - redirect(redirectUrl, defaultRedirectUrl) { - if (redirectUrl === true) { - LogManager.getLogger('authentication').warn('DEPRECATED: Setting redirectUrl === true to actually *not redirect* is deprecated. Set redirectUrl === 0 instead.'); - return; - } - - if (redirectUrl === false) { - LogManager.getLogger('authentication').warn('BREAKING CHANGE: Setting redirectUrl === false to actually *do redirect* is deprecated. Set redirectUrl to undefined or null to use the defaultRedirectUrl if so desired.'); - } - - if (redirectUrl === 0) { - return; - } - if (typeof redirectUrl === 'string') { - PLATFORM.location.href = encodeURI(redirectUrl); - } else if (defaultRedirectUrl) { - PLATFORM.location.href = defaultRedirectUrl; - } + set responseTokenProp(responseTokenProp) { + LogManager.getLogger('authentication').warn('BaseConfig.responseTokenProp is deprecated. Use BaseConfig.accessTokenProp instead.'); + this._responseTokenProp = responseTokenProp; + this.accessTokenProp = responseTokenProp; + return responseTokenProp; } -}, (_applyDecoratedDescriptor(_class7.prototype, 'getLoginRoute', [_dec6], Object.getOwnPropertyDescriptor(_class7.prototype, 'getLoginRoute'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getLoginRedirect', [_dec7], Object.getOwnPropertyDescriptor(_class7.prototype, 'getLoginRedirect'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getLoginUrl', [_dec8], Object.getOwnPropertyDescriptor(_class7.prototype, 'getLoginUrl'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getSignupUrl', [_dec9], Object.getOwnPropertyDescriptor(_class7.prototype, 'getSignupUrl'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getProfileUrl', [_dec10], Object.getOwnPropertyDescriptor(_class7.prototype, 'getProfileUrl'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getToken', [_dec11], Object.getOwnPropertyDescriptor(_class7.prototype, 'getToken'), _class7.prototype)), _class7)) || _class6); - -export let AuthService = (_dec12 = inject(Authentication, BaseConfig), _dec13 = deprecated({ message: 'Use .getAccessToken() instead.' }), _dec12(_class8 = (_class9 = class AuthService { - constructor(authentication, config) { - this.authenticated = false; - this.timeoutID = 0; - - this.authentication = authentication; - this.config = config; - - const oldStorageKey = config.tokenPrefix ? config.tokenPrefix + '_' + config.tokenName : config.tokenName; - const oldToken = authentication.storage.get(oldStorageKey); - - if (oldToken) { - LogManager.getLogger('authentication').info('Found token with deprecated format in storage. Converting it to new format. No further action required.'); - let fakeOldResponse = {}; - fakeOldResponse[config.accessTokenProp] = oldToken; - this.setResponseObject(fakeOldResponse); - authentication.storage.remove(oldStorageKey); - } - - this.setResponseObject(this.authentication.getResponseObject()); + get responseTokenProp() { + return this._responseTokenProp; } - get client() { - return this.config.client; + set tokenRoot(tokenRoot) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenRoot is deprecated. Use BaseConfig.accessTokenRoot instead.'); + this._tokenRoot = tokenRoot; + this.accessTokenRoot = tokenRoot; + return tokenRoot; } - - get auth() { - LogManager.getLogger('authentication').warn('AuthService.auth is deprecated. Use .authentication instead.'); - return this.authentication; + get tokenRoot() { + return this._tokenRoot; } - setTimeout(ttl) { - this.clearTimeout(); - - this.timeoutID = PLATFORM.global.setTimeout(() => { - if (this.config.autoUpdateToken && this.authentication.getAccessToken() && this.authentication.getRefreshToken()) { - this.updateToken(); - } else { - this.logout(this.config.expiredRedirect); - } - }, ttl); + set tokenName(tokenName) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenName is deprecated. Use BaseConfig.accessTokenName instead.'); + this._tokenName = tokenName; + this.accessTokenName = tokenName; + return tokenName; } - - clearTimeout() { - if (this.timeoutID) { - PLATFORM.global.clearTimeout(this.timeoutID); - } - this.timeoutID = 0; + get tokenName() { + return this._tokenName; } - setResponseObject(response) { - this.clearTimeout(); - - this.authentication.setResponseObject(response); - - this.authenticated = this.authentication.isAuthenticated(); - if (this.authenticated && !Number.isNaN(this.authentication.exp)) { - this.setTimeout(this.getTtl() * 1000); - } + set tokenPrefix(tokenPrefix) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenPrefix is obsolete. Use BaseConfig.storageKey instead.'); + this._tokenPrefix = tokenPrefix; + return tokenPrefix; } - - getMe(criteriaOrId) { - if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { - criteriaOrId = { id: criteriaOrId }; - } - return this.client.find(this.config.joinBase(this.config.profileUrl), criteriaOrId); + get tokenPrefix() { + return this._tokenPrefix || 'aurelia'; } - updateMe(body, criteriaOrId) { - if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { - criteriaOrId = { id: criteriaOrId }; - } - if (this.config.profileMethod === 'put') { - return this.client.update(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); - } - return this.client.patch(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); + get current() { + LogManager.getLogger('authentication').warn('Getter BaseConfig.current is deprecated. Use BaseConfig directly instead.'); + return this; } - - getAccessToken() { - return this.authentication.getAccessToken(); + set current(_) { + throw new Error('Setter BaseConfig.current is obsolete. Use BaseConfig directly instead.'); } - getCurrentToken() { - return this.getAccessToken(); + get _current() { + LogManager.getLogger('authentication').warn('Getter BaseConfig._current is deprecated. Use BaseConfig directly instead.'); + return this; } - - getRefreshToken() { - return this.authentication.getRefreshToken(); + set _current(_) { + throw new Error('Setter BaseConfig._current is obsolete. Use BaseConfig directly instead.'); } +}; - isAuthenticated() { - let authenticated = this.authentication.isAuthenticated(); - - if (!authenticated && this.config.autoUpdateToken && this.authentication.getAccessToken() && this.authentication.getRefreshToken()) { - this.updateToken(); - authenticated = true; - } +function randomState() { + let rand = Math.random().toString(36).substr(2); + return encodeURIComponent(rand); +} - return authenticated; +export let FetchConfig = (_dec13 = inject(HttpClient, Config, AuthService, BaseConfig), _dec13(_class10 = class FetchConfig { + constructor(httpClient, clientConfig, authService, config) { + this.httpClient = httpClient; + this.clientConfig = clientConfig; + this.authService = authService; + this.config = config; } - getExp() { - return this.authentication.getExp(); - } + get interceptor() { + return { + request: request => { + if (!this.config.httpInterceptor || !this.authService.isAuthenticated()) { + return request; + } + let token = this.authService.getAccessToken(); - getTtl() { - return this.authentication.getTtl(); - } + if (this.config.authTokenType) { + token = `${ this.config.authTokenType } ${ token }`; + } - isTokenExpired() { - return this.authentication.isTokenExpired(); - } + request.headers.set(this.config.authHeader, token); - getTokenPayload() { - return this.authentication.getPayload(); - } + return request; + }, + response: (response, request) => { + return new Promise((resolve, reject) => { + if (response.ok) { + return resolve(response); + } + if (response.status !== 401) { + return resolve(response); + } + if (!this.config.httpInterceptor || !this.authService.isTokenExpired()) { + return resolve(response); + } + if (!this.config.useRefreshToken || !this.authService.getRefreshToken()) { + return resolve(response); + } - updateToken() { - if (!this.authentication.getRefreshToken()) { - return Promise.reject(new Error('refreshToken not set')); - } + return this.authService.updateToken().then(() => { + let token = this.authService.getAccessToken(); - if (this.authentication.updateTokenCallstack.length === 0) { - const content = { - grant_type: 'refresh_token', - refresh_token: this.authentication.getRefreshToken(), - client_id: this.config.clientId ? this.config.clientId : undefined - }; + if (this.config.authTokenType) { + token = `${ this.config.authTokenType } ${ token }`; + } - this.client.post(this.config.joinBase(this.config.refreshTokenUrl ? this.config.refreshTokenUrl : this.config.loginUrl), content).then(response => { - this.setResponseObject(response); - this.authentication.resolveUpdateTokenCallstack(this.isAuthenticated()); - }).catch(err => { - this.setResponseObject(null); - this.authentication.resolveUpdateTokenCallstack(Promise.reject(err)); - }); - } + request.headers.set(this.config.authHeader, token); - return this.authentication.toUpdateTokenCallstack(); + return this.client.fetch(request).then(resolve); + }); + }); + } + }; } - signup(displayNameOrCredentials, emailOrOptions, passwordOrRedirectUri, options, redirectUri) { - let content; + configure(client) { + if (Array.isArray(client)) { + let configuredClients = []; + client.forEach(toConfigure => { + configuredClients.push(this.configure(toConfigure)); + }); - if (typeof arguments[0] === 'object') { - content = arguments[0]; - options = arguments[1]; - redirectUri = arguments[2]; - } else { - content = { - 'displayName': displayNameOrCredentials, - 'email': emailOrOptions, - 'password': passwordOrRedirectUri - }; + return configuredClients; } - return this.client.post(this.config.joinBase(this.config.signupUrl), content, options).then(response => { - if (this.config.loginOnSignup) { - this.setResponseObject(response); + + if (typeof client === 'string') { + const endpoint = this.clientConfig.getEndpoint(client); + if (!endpoint) { + throw new Error(`There is no '${ client || 'default' }' endpoint registered.`); } - this.authentication.redirect(redirectUri, this.config.signupRedirect); + client = endpoint.client; + } else if (client instanceof Rest) { + client = client.client; + } else if (!(client instanceof HttpClient)) { + client = this.httpClient; + } - return response; - }); + client.interceptors.push(this.interceptor); + + return client; } +}) || _class10); - login(emailOrCredentials, passwordOrOptions, optionsOrRedirectUri, redirectUri) { - let content; +export let OAuth1 = (_dec14 = inject(Storage, Popup, BaseConfig), _dec14(_class11 = class OAuth1 { + constructor(storage, popup, config) { + this.storage = storage; + this.config = config; + this.popup = popup; + this.defaults = { + url: null, + name: null, + popupOptions: null, + redirectUri: null, + authorizationEndpoint: null + }; + } - if (typeof arguments[0] === 'object') { - content = arguments[0]; - optionsOrRedirectUri = arguments[1]; - redirectUri = arguments[2]; - } else { - content = { - 'email': emailOrCredentials, - 'password': passwordOrOptions - }; - optionsOrRedirectUri = optionsOrRedirectUri; - } + open(options, userData) { + const provider = extend(true, {}, this.defaults, options); + const serverUrl = this.config.joinBase(provider.url); - if (this.config.clientId) { - content.client_id = this.config.clientId; + if (this.config.platform !== 'mobile') { + this.popup = this.popup.open('', provider.name, provider.popupOptions); } - return this.client.post(this.config.joinBase(this.config.loginUrl), content, optionsOrRedirectUri).then(response => { - this.setResponseObject(response); + return this.config.client.post(serverUrl).then(response => { + const url = provider.authorizationEndpoint + '?' + buildQueryString(response); - this.authentication.redirect(redirectUri, this.config.loginRedirect); + if (this.config.platform === 'mobile') { + this.popup = this.popup.open(url, provider.name, provider.popupOptions); + } else { + this.popup.popupWindow.location = url; + } - return response; + const popupListener = this.config.platform === 'mobile' ? this.popup.eventListener(provider.redirectUri) : this.popup.pollPopup(); + + return popupListener.then(result => this.exchangeForToken(result, userData, provider)); }); } - logout(redirectUri) { - let localLogout = response => new Promise(resolve => { - this.setResponseObject(null); - - this.authentication.redirect(redirectUri, this.config.logoutRedirect); - - if (typeof this.onLogout === 'function') { - this.onLogout(response); - } + exchangeForToken(oauthData, userData, provider) { + const data = extend(true, {}, userData, oauthData); + const serverUrl = this.config.joinBase(provider.url); + const credentials = this.config.withCredentials ? 'include' : 'same-origin'; - resolve(response); - }); + return this.config.client.post(serverUrl, data, { credentials: credentials }); + } +}) || _class11); - return this.config.logoutUrl ? this.client.request(this.config.logoutMethod, this.config.joinBase(this.config.logoutUrl)).then(localLogout) : localLogout(); +export let OAuth2 = (_dec15 = inject(Storage, Popup, BaseConfig), _dec15(_class12 = class OAuth2 { + constructor(storage, popup, config) { + this.storage = storage; + this.config = config; + this.popup = popup; + this.defaults = { + url: null, + name: null, + state: null, + scope: null, + scopeDelimiter: null, + redirectUri: null, + popupOptions: null, + authorizationEndpoint: null, + responseParams: null, + requiredUrlParams: null, + optionalUrlParams: null, + defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], + responseType: 'code' + }; } - authenticate(name, redirectUri, userData = {}) { - return this.authentication.authenticate(name, userData).then(response => { - this.setResponseObject(response); + open(options, userData) { + const provider = extend(true, {}, this.defaults, options); + const stateName = provider.name + '_state'; - this.authentication.redirect(redirectUri, this.config.loginRedirect); + if (typeof provider.state === 'function') { + this.storage.set(stateName, provider.state()); + } else if (typeof provider.state === 'string') { + this.storage.set(stateName, provider.state); + } - return response; + const url = provider.authorizationEndpoint + '?' + buildQueryString(this.buildQuery(provider)); + const popup = this.popup.open(url, provider.name, provider.popupOptions); + const openPopup = this.config.platform === 'mobile' ? popup.eventListener(provider.redirectUri) : popup.pollPopup(); + + return openPopup.then(oauthData => { + if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { + return oauthData; + } + if (oauthData.state && oauthData.state !== this.storage.get(stateName)) { + return Promise.reject('OAuth 2.0 state parameter mismatch.'); + } + return this.exchangeForToken(oauthData, userData, provider); }); } - unlink(name, redirectUri) { - const unlinkUrl = this.config.joinBase(this.config.unlinkUrl) + name; - return this.client.request(this.config.unlinkMethod, unlinkUrl).then(response => { - this.authentication.redirect(redirectUri); + exchangeForToken(oauthData, userData, provider) { + const data = extend(true, {}, userData, { + clientId: provider.clientId, + redirectUri: provider.redirectUri + }, oauthData); + + const serverUrl = this.config.joinBase(provider.url); + const credentials = this.config.withCredentials ? 'include' : 'same-origin'; - return response; - }); + return this.config.client.post(serverUrl, data, { credentials: credentials }); } -}, (_applyDecoratedDescriptor(_class9.prototype, 'getCurrentToken', [_dec13], Object.getOwnPropertyDescriptor(_class9.prototype, 'getCurrentToken'), _class9.prototype)), _class9)) || _class8); -export let AuthenticateStep = (_dec14 = inject(AuthService), _dec14(_class11 = class AuthenticateStep { - constructor(authService) { - this.authService = authService; - } + buildQuery(provider) { + let query = {}; + const urlParams = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams']; - run(routingContext, next) { - const isLoggedIn = this.authService.authenticated; - const loginRoute = this.authService.config.loginRoute; + urlParams.forEach(params => { + (provider[params] || []).forEach(paramName => { + const camelizedName = camelCase(paramName); + let paramValue = typeof provider[paramName] === 'function' ? provider[paramName]() : provider[camelizedName]; - if (routingContext.getAllInstructions().some(route => route.config.auth === true)) { - if (!isLoggedIn) { - return next.cancel(new Redirect(loginRoute)); - } - } else if (isLoggedIn && routingContext.getAllInstructions().some(route => route.fragment === loginRoute)) { - return next.cancel(new Redirect(this.authService.config.loginRedirect)); - } + if (paramName === 'state') { + paramValue = encodeURIComponent(this.storage.get(provider.name + '_state')); + } - return next(); + if (paramName === 'scope' && Array.isArray(paramValue)) { + paramValue = paramValue.join(provider.scopeDelimiter); + + if (provider.scopePrefix) { + paramValue = provider.scopePrefix + provider.scopeDelimiter + paramValue; + } + } + + query[paramName] = paramValue; + }); + }); + return query; } -}) || _class11); +}) || _class12); -export let AuthorizeStep = (_dec15 = inject(AuthService), _dec15(_class12 = class AuthorizeStep { - constructor(authService) { - LogManager.getLogger('authentication').warn('AuthorizeStep is deprecated. Use AuthenticationStep instead.'); +const camelCase = function (name) { + return name.replace(/([\:\-\_]+(.))/g, function (_, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }); +}; - this.authService = authService; +export let Popup = class Popup { + constructor() { + this.popupWindow = null; + this.polling = null; + this.url = ''; } - run(routingContext, next) { - const isLoggedIn = this.authService.isAuthenticated(); - const loginRoute = this.authService.config.loginRoute; + open(url, windowName, options) { + this.url = url; + const optionsString = buildPopupWindowOptions(options || {}); - if (routingContext.getAllInstructions().some(route => route.config.auth)) { - if (!isLoggedIn) { - return next.cancel(new Redirect(loginRoute)); - } - } else if (isLoggedIn && routingContext.getAllInstructions().some(route => route.fragment === loginRoute)) { - return next.cancel(new Redirect(this.authService.config.loginRedirect)); - } + this.popupWindow = PLATFORM.global.open(url, windowName, optionsString); - return next(); - } -}) || _class12); + if (this.popupWindow && this.popupWindow.focus) { + this.popupWindow.focus(); + } -export let FetchConfig = (_dec16 = inject(HttpClient, Config, AuthService, BaseConfig), _dec16(_class13 = class FetchConfig { - constructor(httpClient, clientConfig, authService, config) { - this.httpClient = httpClient; - this.clientConfig = clientConfig; - this.authService = authService; - this.config = config; + return this; } - get interceptor() { - return { - request: request => { - if (!this.config.httpInterceptor || !this.authService.isAuthenticated()) { - return request; + eventListener(redirectUri) { + return new Promise((resolve, reject) => { + this.popupWindow.addEventListener('loadstart', event => { + if (event.url.indexOf(redirectUri) !== 0) { + return; } - let token = this.authService.getAccessToken(); - if (this.config.authTokenType) { - token = `${ this.config.authTokenType } ${ token }`; - } + const parser = DOM.createElement('a'); + parser.href = event.url; - request.headers.set(this.config.authHeader, token); + if (parser.search || parser.hash) { + const qs = parseUrl(parser); - return request; - }, - response: (response, request) => { - return new Promise((resolve, reject) => { - if (response.ok) { - return resolve(response); - } - if (response.status !== 401) { - return resolve(response); - } - if (!this.config.httpInterceptor || !this.authService.isTokenExpired()) { - return resolve(response); - } - if (!this.config.useRefreshToken || !this.authService.getRefreshToken()) { - return resolve(response); + if (qs.error) { + reject({ error: qs.error }); + } else { + resolve(qs); } - this.authService.updateToken().then(() => { - let token = this.authService.getAccessToken(); - - if (this.config.authTokenType) { - token = `${ this.config.authTokenType } ${ token }`; - } + this.popupWindow.close(); + } + }); - request.headers.set(this.config.authHeader, token); + this.popupWindow.addEventListener('exit', () => { + reject({ data: 'Provider Popup was closed' }); + }); - return this.client.fetch(request).then(resolve); - }); - }); - } - }; + this.popupWindow.addEventListener('loaderror', () => { + reject({ data: 'Authorization Failed' }); + }); + }); } - configure(client) { - if (Array.isArray(client)) { - let configuredClients = []; - client.forEach(toConfigure => { - configuredClients.push(this.configure(toConfigure)); - }); + pollPopup() { + return new Promise((resolve, reject) => { + this.polling = PLATFORM.global.setInterval(() => { + let errorData; - return configuredClients; - } + try { + if (this.popupWindow.location.host === PLATFORM.global.document.location.host && (this.popupWindow.location.search || this.popupWindow.location.hash)) { + const qs = parseUrl(this.popupWindow.location); - if (typeof client === 'string') { - const endpoint = this.clientConfig.getEndpoint(client); - if (!endpoint) { - throw new Error(`There is no '${ client || 'default' }' endpoint registered.`); - } - client = endpoint.client; - } else if (client instanceof Rest) { - client = client.client; - } else if (!(client instanceof HttpClient)) { - client = this.httpClient; - } + if (qs.error) { + reject({ error: qs.error }); + } else { + resolve(qs); + } - client.interceptors.push(this.interceptor); + this.popupWindow.close(); + PLATFORM.global.clearInterval(this.polling); + } + } catch (error) { + errorData = error; + } - return client; + if (!this.popupWindow) { + PLATFORM.global.clearInterval(this.polling); + reject({ + error: errorData, + data: 'Provider Popup Blocked' + }); + } else if (this.popupWindow.closed) { + PLATFORM.global.clearInterval(this.polling); + reject({ + error: errorData, + data: 'Problem poll popup' + }); + } + }, 35); + }); } -}) || _class13); +}; -import './authFilterValueConverter'; +const buildPopupWindowOptions = options => { + const width = options.width || 500; + const height = options.height || 500; -function configure(aurelia, config) { - if (!PLATFORM.location.origin) { - PLATFORM.location.origin = PLATFORM.location.protocol + '//' + PLATFORM.location.hostname + (PLATFORM.location.port ? ':' + PLATFORM.location.port : ''); - } + const extended = extend({ + width: width, + height: height, + left: PLATFORM.global.screenX + (PLATFORM.global.outerWidth - width) / 2, + top: PLATFORM.global.screenY + (PLATFORM.global.outerHeight - height) / 2.5 + }, options); - const baseConfig = aurelia.container.get(BaseConfig); + let parts = []; + Object.keys(extended).map(key => parts.push(key + '=' + extended[key])); - if (typeof config === 'function') { - config(baseConfig); - } else if (typeof config === 'object') { - baseConfig.configure(config); - } + return parts.join(','); +}; - for (let converter of baseConfig.globalValueConverters) { - aurelia.globalResources(`./${ converter }`); - LogManager.getLogger('authentication').info(`Add globalResources value-converter: ${ converter }`); - } - const fetchConfig = aurelia.container.get(FetchConfig); - const clientConfig = aurelia.container.get(Config); +const parseUrl = url => { + return extend(true, {}, parseQueryString(url.search), parseQueryString(url.hash)); +}; - if (Array.isArray(baseConfig.configureEndpoints)) { - baseConfig.configureEndpoints.forEach(endpointToPatch => { - fetchConfig.configure(endpointToPatch); - }); +export let Storage = (_dec16 = inject(BaseConfig), _dec16(_class13 = class Storage { + constructor(config) { + this.config = config; } - let client; - - if (baseConfig.endpoint !== null) { - if (typeof baseConfig.endpoint === 'string') { - const endpoint = clientConfig.getEndpoint(baseConfig.endpoint); - if (!endpoint) { - throw new Error(`There is no '${ baseConfig.endpoint || 'default' }' endpoint registered.`); - } - client = endpoint; - } else if (baseConfig.endpoint instanceof HttpClient) { - client = new Rest(baseConfig.endpoint); - } + get(key) { + return PLATFORM.global[this.config.storage].getItem(key); } - if (!(client instanceof Rest)) { - client = new Rest(aurelia.container.get(HttpClient)); + set(key, value) { + PLATFORM.global[this.config.storage].setItem(key, value); } - baseConfig.client = client; -} - -export { configure, FetchConfig, AuthService, AuthorizeStep, AuthenticateStep }; \ No newline at end of file + remove(key) { + PLATFORM.global[this.config.storage].removeItem(key); + } +}) || _class13); \ No newline at end of file diff --git a/dist/es2015/index.js b/dist/es2015/index.js new file mode 100644 index 0000000..f4493eb --- /dev/null +++ b/dist/es2015/index.js @@ -0,0 +1 @@ +export * from './aurelia-authentication'; \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..0a857ab --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1 @@ +export * from 'aurelia-authentication/aurelia-authentication'; \ No newline at end of file diff --git a/dist/native-modules/aurelia-authentication.js b/dist/native-modules/aurelia-authentication.js new file mode 100644 index 0000000..0552f8e --- /dev/null +++ b/dist/native-modules/aurelia-authentication.js @@ -0,0 +1,1401 @@ +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _dec, _class, _dec2, _class2, _dec3, _dec4, _dec5, _dec6, _dec7, _dec8, _dec9, _class3, _desc, _value, _class4, _dec10, _class5, _dec11, _dec12, _class6, _desc2, _value2, _class7, _dec13, _class10, _dec14, _class11, _dec15, _class12, _dec16, _class13; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; + +function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { + var desc = {}; + Object['ke' + 'ys'](descriptor).forEach(function (key) { + desc[key] = descriptor[key]; + }); + desc.enumerable = !!desc.enumerable; + desc.configurable = !!desc.configurable; + + if ('value' in desc || desc.initializer) { + desc.writable = true; + } + + desc = decorators.slice().reverse().reduce(function (desc, decorator) { + return decorator(target, property, desc) || desc; + }, desc); + + if (context && desc.initializer !== void 0) { + desc.value = desc.initializer ? desc.initializer.call(context) : void 0; + desc.initializer = undefined; + } + + if (desc.initializer === void 0) { + Object['define' + 'Property'](target, property, desc); + desc = null; + } + + return desc; +} + + + +import * as LogManager from 'aurelia-logging'; +import extend from 'extend'; +import jwtDecode from 'jwt-decode'; +import { PLATFORM, DOM } from 'aurelia-pal'; +import { HttpClient } from 'aurelia-fetch-client'; +import { Config, Rest } from 'aurelia-api'; +import { inject } from 'aurelia-dependency-injection'; +import { Redirect } from 'aurelia-router'; +import { deprecated } from 'aurelia-metadata'; +import { join, buildQueryString, parseQueryString } from 'aurelia-path'; + +import './authFilterValueConverter'; + +export function configure(aurelia, config) { + if (!PLATFORM.location.origin) { + PLATFORM.location.origin = PLATFORM.location.protocol + '//' + PLATFORM.location.hostname + (PLATFORM.location.port ? ':' + PLATFORM.location.port : ''); + } + + var baseConfig = aurelia.container.get(BaseConfig); + + if (typeof config === 'function') { + config(baseConfig); + } else if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') { + baseConfig.configure(config); + } + + for (var _iterator = baseConfig.globalValueConverters, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; + + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; + } + + var converter = _ref; + + aurelia.globalResources('./' + converter); + LogManager.getLogger('authentication').info('Add globalResources value-converter: ' + converter); + } + var fetchConfig = aurelia.container.get(FetchConfig); + var clientConfig = aurelia.container.get(Config); + + if (Array.isArray(baseConfig.configureEndpoints)) { + baseConfig.configureEndpoints.forEach(function (endpointToPatch) { + fetchConfig.configure(endpointToPatch); + }); + } + + var client = void 0; + + if (baseConfig.endpoint !== null) { + if (typeof baseConfig.endpoint === 'string') { + var endpoint = clientConfig.getEndpoint(baseConfig.endpoint); + if (!endpoint) { + throw new Error('There is no \'' + (baseConfig.endpoint || 'default') + '\' endpoint registered.'); + } + client = endpoint; + } else if (baseConfig.endpoint instanceof HttpClient) { + client = new Rest(baseConfig.endpoint); + } + } + + if (!(client instanceof Rest)) { + client = new Rest(aurelia.container.get(HttpClient)); + } + + baseConfig.client = client; +} + +export var Auth0Lock = (_dec = inject(Storage, BaseConfig), _dec(_class = function () { + function Auth0Lock(storage, config) { + _classCallCheck(this, Auth0Lock); + + 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' + }; + } + + Auth0Lock.prototype.open = function open(options, userData) { + var _this = this; + + if (typeof PLATFORM.global.Auth0Lock !== 'function') { + throw new Error('Auth0Lock was not found in global scope. Please load it before using this provider.'); + } + var provider = extend(true, {}, this.defaults, options); + var 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); + + var openPopup = new Promise(function (resolve, reject) { + var 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, function (err, profile, tokenOrCode) { + if (err) { + reject(err); + } else { + resolve({ + access_token: tokenOrCode + }); + } + }); + }); + + return openPopup.then(function (lockResponse) { + if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { + return lockResponse; + } + + throw new Error('Only `token` responseType is supported'); + }); + }; + + return Auth0Lock; +}()) || _class); + +export var AuthenticateStep = (_dec2 = inject(AuthService), _dec2(_class2 = function () { + function AuthenticateStep(authService) { + + + this.authService = authService; + } + + AuthenticateStep.prototype.run = function run(routingContext, next) { + var isLoggedIn = this.authService.authenticated; + var loginRoute = this.authService.config.loginRoute; + + if (routingContext.getAllInstructions().some(function (route) { + return route.config.auth === true; + })) { + if (!isLoggedIn) { + return next.cancel(new Redirect(loginRoute)); + } + } else if (isLoggedIn && routingContext.getAllInstructions().some(function (route) { + return route.fragment === loginRoute; + })) { + return next.cancel(new Redirect(this.authService.config.loginRedirect)); + } + + return next(); + }; + + return AuthenticateStep; +}()) || _class2); + +export var Authentication = (_dec3 = inject(Storage, BaseConfig, OAuth1, OAuth2, Auth0Lock), _dec4 = deprecated({ message: 'Use baseConfig.loginRoute instead.' }), _dec5 = deprecated({ message: 'Use baseConfig.loginRedirect instead.' }), _dec6 = deprecated({ message: 'Use baseConfig.joinBase(baseConfig.loginUrl) instead.' }), _dec7 = deprecated({ message: 'Use baseConfig.joinBase(baseConfig.signupUrl) instead.' }), _dec8 = deprecated({ message: 'Use baseConfig.joinBase(baseConfig.profileUrl) instead.' }), _dec9 = deprecated({ message: 'Use .getAccessToken() instead.' }), _dec3(_class3 = (_class4 = function () { + function Authentication(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; + this.payload = null; + this.exp = null; + this.hasDataStored = false; + } + + Authentication.prototype.getLoginRoute = function getLoginRoute() { + return this.config.loginRoute; + }; + + Authentication.prototype.getLoginRedirect = function getLoginRedirect() { + return this.config.loginRedirect; + }; + + Authentication.prototype.getLoginUrl = function getLoginUrl() { + return this.Config.joinBase(this.config.loginUrl); + }; + + Authentication.prototype.getSignupUrl = function getSignupUrl() { + return this.Config.joinBase(this.config.signupUrl); + }; + + Authentication.prototype.getProfileUrl = function getProfileUrl() { + return this.Config.joinBase(this.config.profileUrl); + }; + + Authentication.prototype.getToken = function getToken() { + return this.getAccessToken(); + }; + + Authentication.prototype.getResponseObject = function getResponseObject() { + return JSON.parse(this.storage.get(this.config.storageKey)); + }; + + Authentication.prototype.setResponseObject = function setResponseObject(response) { + if (response) { + this.getDataFromResponse(response); + this.storage.set(this.config.storageKey, JSON.stringify(response)); + return; + } + this.accessToken = null; + this.refreshToken = null; + this.payload = null; + this.exp = null; + + this.hasDataStored = false; + + this.storage.remove(this.config.storageKey); + }; + + Authentication.prototype.getAccessToken = function getAccessToken() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.accessToken; + }; + + Authentication.prototype.getRefreshToken = function getRefreshToken() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.refreshToken; + }; + + Authentication.prototype.getPayload = function getPayload() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.payload; + }; + + Authentication.prototype.getExp = function getExp() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.exp; + }; + + Authentication.prototype.getTtl = function getTtl() { + var exp = this.getExp(); + return Number.isNaN(exp) ? NaN : exp - Math.round(new Date().getTime() / 1000); + }; + + Authentication.prototype.isTokenExpired = function isTokenExpired() { + var timeLeft = this.getTtl(); + return Number.isNaN(timeLeft) ? undefined : timeLeft < 0; + }; + + Authentication.prototype.isAuthenticated = function isAuthenticated() { + var isTokenExpired = this.isTokenExpired(); + if (isTokenExpired === undefined) return this.accessToken ? true : false; + return !isTokenExpired; + }; + + Authentication.prototype.getDataFromResponse = function getDataFromResponse(response) { + var config = this.config; + + this.accessToken = this.getTokenFromResponse(response, config.accessTokenProp, config.accessTokenName, config.accessTokenRoot); + + this.refreshToken = null; + if (config.useRefreshToken) { + try { + this.refreshToken = this.getTokenFromResponse(response, config.refreshTokenProp, config.refreshTokenName, config.refreshTokenRoot); + } catch (e) { + this.refreshToken = null; + } + } + + this.payload = null; + + try { + this.payload = this.accessToken ? jwtDecode(this.accessToken) : null; + } catch (_) { + _; + } + + this.exp = this.payload ? parseInt(this.payload.exp, 10) : NaN; + + this.hasDataStored = true; + + return { + accessToken: this.accessToken, + refreshToken: this.refreshToken, + payload: this.payload, + exp: this.exp + }; + }; + + Authentication.prototype.getTokenFromResponse = function getTokenFromResponse(response, tokenProp, tokenName, tokenRoot) { + if (!response) return undefined; + + var responseTokenProp = tokenProp.split('.').reduce(function (o, x) { + return o[x]; + }, response); + + if (typeof responseTokenProp === 'string') { + return responseTokenProp; + } + + if ((typeof responseTokenProp === 'undefined' ? 'undefined' : _typeof(responseTokenProp)) === 'object') { + var tokenRootData = tokenRoot && tokenRoot.split('.').reduce(function (o, x) { + return o[x]; + }, responseTokenProp); + var _token = tokenRootData ? tokenRootData[tokenName] : responseTokenProp[tokenName]; + + if (!_token) throw new Error('Token not found in response'); + + return _token; + } + + var token = response[tokenName] === undefined ? null : response[tokenName]; + + if (!token) throw new Error('Token not found in response'); + + return token; + }; + + Authentication.prototype.toUpdateTokenCallstack = function toUpdateTokenCallstack() { + var _this2 = this; + + return new Promise(function (resolve) { + return _this2.updateTokenCallstack.push(resolve); + }); + }; + + Authentication.prototype.resolveUpdateTokenCallstack = function resolveUpdateTokenCallstack(response) { + this.updateTokenCallstack.map(function (resolve) { + return resolve(response); + }); + this.updateTokenCallstack = []; + }; + + Authentication.prototype.authenticate = function authenticate(name) { + var userData = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + var oauthType = this.config.providers[name].type; + + if (oauthType) { + LogManager.getLogger('authentication').warn('DEPRECATED: Setting provider.type is deprecated and replaced by provider.oauthType'); + } else { + oauthType = this.config.providers[name].oauthType; + } + + var providerLogin = void 0; + if (oauthType === 'auth0-lock') { + providerLogin = this.auth0Lock; + } else { + providerLogin = oauthType === '1.0' ? this.oAuth1 : this.oAuth2; + } + + return providerLogin.open(this.config.providers[name], userData); + }; + + Authentication.prototype.redirect = function redirect(redirectUrl, defaultRedirectUrl) { + if (redirectUrl === true) { + LogManager.getLogger('authentication').warn('DEPRECATED: Setting redirectUrl === true to actually *not redirect* is deprecated. Set redirectUrl === 0 instead.'); + return; + } + + if (redirectUrl === false) { + LogManager.getLogger('authentication').warn('BREAKING CHANGE: Setting redirectUrl === false to actually *do redirect* is deprecated. Set redirectUrl to undefined or null to use the defaultRedirectUrl if so desired.'); + } + + if (redirectUrl === 0) { + return; + } + if (typeof redirectUrl === 'string') { + PLATFORM.location.href = encodeURI(redirectUrl); + } else if (defaultRedirectUrl) { + PLATFORM.location.href = defaultRedirectUrl; + } + }; + + _createClass(Authentication, [{ + key: 'responseObject', + get: function get() { + LogManager.getLogger('authentication').warn('Getter Authentication.responseObject is deprecated. Use Authentication.getResponseObject() instead.'); + return this.getResponseObject(); + }, + set: function set(response) { + LogManager.getLogger('authentication').warn('Setter Authentication.responseObject is deprecated. Use AuthServive.setResponseObject(response) instead.'); + this.setResponseObject(response); + } + }]); + + return Authentication; +}(), (_applyDecoratedDescriptor(_class4.prototype, 'getLoginRoute', [_dec4], Object.getOwnPropertyDescriptor(_class4.prototype, 'getLoginRoute'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getLoginRedirect', [_dec5], Object.getOwnPropertyDescriptor(_class4.prototype, 'getLoginRedirect'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getLoginUrl', [_dec6], Object.getOwnPropertyDescriptor(_class4.prototype, 'getLoginUrl'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getSignupUrl', [_dec7], Object.getOwnPropertyDescriptor(_class4.prototype, 'getSignupUrl'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getProfileUrl', [_dec8], Object.getOwnPropertyDescriptor(_class4.prototype, 'getProfileUrl'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getToken', [_dec9], Object.getOwnPropertyDescriptor(_class4.prototype, 'getToken'), _class4.prototype)), _class4)) || _class3); + +export var AuthorizeStep = (_dec10 = inject(AuthService), _dec10(_class5 = function () { + function AuthorizeStep(authService) { + + + LogManager.getLogger('authentication').warn('AuthorizeStep is deprecated. Use AuthenticationStep instead.'); + + this.authService = authService; + } + + AuthorizeStep.prototype.run = function run(routingContext, next) { + var isLoggedIn = this.authService.isAuthenticated(); + var loginRoute = this.authService.config.loginRoute; + + if (routingContext.getAllInstructions().some(function (route) { + return route.config.auth; + })) { + if (!isLoggedIn) { + return next.cancel(new Redirect(loginRoute)); + } + } else if (isLoggedIn && routingContext.getAllInstructions().some(function (route) { + return route.fragment === loginRoute; + })) { + return next.cancel(new Redirect(this.authService.config.loginRedirect)); + } + + return next(); + }; + + return AuthorizeStep; +}()) || _class5); + +export var AuthService = (_dec11 = inject(Authentication, BaseConfig), _dec12 = deprecated({ message: 'Use .getAccessToken() instead.' }), _dec11(_class6 = (_class7 = function () { + function AuthService(authentication, config) { + + + this.authenticated = false; + this.timeoutID = 0; + + this.authentication = authentication; + this.config = config; + + var oldStorageKey = config.tokenPrefix ? config.tokenPrefix + '_' + config.tokenName : config.tokenName; + var oldToken = authentication.storage.get(oldStorageKey); + + if (oldToken) { + LogManager.getLogger('authentication').info('Found token with deprecated format in storage. Converting it to new format. No further action required.'); + var fakeOldResponse = {}; + fakeOldResponse[config.accessTokenProp] = oldToken; + this.setResponseObject(fakeOldResponse); + authentication.storage.remove(oldStorageKey); + } + + this.setResponseObject(this.authentication.getResponseObject()); + } + + AuthService.prototype.setTimeout = function setTimeout(ttl) { + var _this3 = this; + + this.clearTimeout(); + + this.timeoutID = PLATFORM.global.setTimeout(function () { + if (_this3.config.autoUpdateToken && _this3.authentication.getAccessToken() && _this3.authentication.getRefreshToken()) { + _this3.updateToken(); + } else { + _this3.logout(_this3.config.expiredRedirect); + } + }, ttl); + }; + + AuthService.prototype.clearTimeout = function clearTimeout() { + if (this.timeoutID) { + PLATFORM.global.clearTimeout(this.timeoutID); + } + this.timeoutID = 0; + }; + + AuthService.prototype.setResponseObject = function setResponseObject(response) { + this.clearTimeout(); + + this.authentication.setResponseObject(response); + + this.authenticated = this.authentication.isAuthenticated(); + if (this.authenticated && !Number.isNaN(this.authentication.exp)) { + this.setTimeout(this.getTtl() * 1000); + } + }; + + AuthService.prototype.getMe = function getMe(criteriaOrId) { + if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { + criteriaOrId = { id: criteriaOrId }; + } + return this.client.find(this.config.joinBase(this.config.profileUrl), criteriaOrId); + }; + + AuthService.prototype.updateMe = function updateMe(body, criteriaOrId) { + if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { + criteriaOrId = { id: criteriaOrId }; + } + if (this.config.profileMethod === 'put') { + return this.client.update(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); + } + return this.client.patch(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); + }; + + AuthService.prototype.getAccessToken = function getAccessToken() { + return this.authentication.getAccessToken(); + }; + + AuthService.prototype.getCurrentToken = function getCurrentToken() { + return this.getAccessToken(); + }; + + AuthService.prototype.getRefreshToken = function getRefreshToken() { + return this.authentication.getRefreshToken(); + }; + + AuthService.prototype.isAuthenticated = function isAuthenticated() { + var authenticated = this.authentication.isAuthenticated(); + + if (!authenticated && this.config.autoUpdateToken && this.authentication.getAccessToken() && this.authentication.getRefreshToken()) { + this.updateToken(); + authenticated = true; + } + + return authenticated; + }; + + AuthService.prototype.getExp = function getExp() { + return this.authentication.getExp(); + }; + + AuthService.prototype.getTtl = function getTtl() { + return this.authentication.getTtl(); + }; + + AuthService.prototype.isTokenExpired = function isTokenExpired() { + return this.authentication.isTokenExpired(); + }; + + AuthService.prototype.getTokenPayload = function getTokenPayload() { + return this.authentication.getPayload(); + }; + + AuthService.prototype.updateToken = function updateToken() { + var _this4 = this; + + if (!this.authentication.getRefreshToken()) { + return Promise.reject(new Error('refreshToken not set')); + } + + if (this.authentication.updateTokenCallstack.length === 0) { + var content = { + grant_type: 'refresh_token', + refresh_token: this.authentication.getRefreshToken(), + client_id: this.config.clientId ? this.config.clientId : undefined + }; + + this.client.post(this.config.joinBase(this.config.refreshTokenUrl ? this.config.refreshTokenUrl : this.config.loginUrl), content).then(function (response) { + _this4.setResponseObject(response); + _this4.authentication.resolveUpdateTokenCallstack(_this4.isAuthenticated()); + }).catch(function (err) { + _this4.setResponseObject(null); + _this4.authentication.resolveUpdateTokenCallstack(Promise.reject(err)); + }); + } + + return this.authentication.toUpdateTokenCallstack(); + }; + + AuthService.prototype.signup = function signup(displayNameOrCredentials, emailOrOptions, passwordOrRedirectUri, options, redirectUri) { + var _this5 = this; + + var content = void 0; + + if (_typeof(arguments[0]) === 'object') { + content = arguments[0]; + options = arguments[1]; + redirectUri = arguments[2]; + } else { + content = { + 'displayName': displayNameOrCredentials, + 'email': emailOrOptions, + 'password': passwordOrRedirectUri + }; + } + return this.client.post(this.config.joinBase(this.config.signupUrl), content, options).then(function (response) { + if (_this5.config.loginOnSignup) { + _this5.setResponseObject(response); + } + _this5.authentication.redirect(redirectUri, _this5.config.signupRedirect); + + return response; + }); + }; + + AuthService.prototype.login = function login(emailOrCredentials, passwordOrOptions, optionsOrRedirectUri, redirectUri) { + var _this6 = this; + + var content = void 0; + + if (_typeof(arguments[0]) === 'object') { + content = arguments[0]; + optionsOrRedirectUri = arguments[1]; + redirectUri = arguments[2]; + } else { + content = { + 'email': emailOrCredentials, + 'password': passwordOrOptions + }; + optionsOrRedirectUri = optionsOrRedirectUri; + } + + if (this.config.clientId) { + content.client_id = this.config.clientId; + } + + return this.client.post(this.config.joinBase(this.config.loginUrl), content, optionsOrRedirectUri).then(function (response) { + _this6.setResponseObject(response); + + _this6.authentication.redirect(redirectUri, _this6.config.loginRedirect); + + return response; + }); + }; + + AuthService.prototype.logout = function logout(redirectUri) { + var _this7 = this; + + var localLogout = function localLogout(response) { + return new Promise(function (resolve) { + _this7.setResponseObject(null); + + _this7.authentication.redirect(redirectUri, _this7.config.logoutRedirect); + + if (typeof _this7.onLogout === 'function') { + _this7.onLogout(response); + } + + resolve(response); + }); + }; + + return this.config.logoutUrl ? this.client.request(this.config.logoutMethod, this.config.joinBase(this.config.logoutUrl)).then(localLogout) : localLogout(); + }; + + AuthService.prototype.authenticate = function authenticate(name, redirectUri) { + var _this8 = this; + + var userData = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; + + return this.authentication.authenticate(name, userData).then(function (response) { + _this8.setResponseObject(response); + + _this8.authentication.redirect(redirectUri, _this8.config.loginRedirect); + + return response; + }); + }; + + AuthService.prototype.unlink = function unlink(name, redirectUri) { + var _this9 = this; + + var unlinkUrl = this.config.joinBase(this.config.unlinkUrl) + name; + return this.client.request(this.config.unlinkMethod, unlinkUrl).then(function (response) { + _this9.authentication.redirect(redirectUri); + + return response; + }); + }; + + _createClass(AuthService, [{ + key: 'client', + get: function get() { + return this.config.client; + } + }, { + key: 'auth', + get: function get() { + LogManager.getLogger('authentication').warn('AuthService.auth is deprecated. Use .authentication instead.'); + return this.authentication; + } + }]); + + return AuthService; +}(), (_applyDecoratedDescriptor(_class7.prototype, 'getCurrentToken', [_dec12], Object.getOwnPropertyDescriptor(_class7.prototype, 'getCurrentToken'), _class7.prototype)), _class7)) || _class6); + +export var BaseConfig = function () { + function BaseConfig() { + + + this.client = null; + this.endpoint = null; + this.configureEndpoints = null; + this.loginRedirect = '#/'; + this.logoutRedirect = '#/'; + this.loginRoute = '/login'; + this.loginOnSignup = true; + this.signupRedirect = '#/login'; + this.expiredRedirect = 0; + this.baseUrl = ''; + this.loginUrl = '/auth/login'; + this.logoutUrl = null; + this.logoutMethod = 'get'; + this.signupUrl = '/auth/signup'; + this.profileUrl = '/auth/me'; + this.profileMethod = 'put'; + this.unlinkUrl = '/auth/unlink/'; + this.unlinkMethod = 'get'; + this.refreshTokenUrl = null; + this.authHeader = 'Authorization'; + this.authTokenType = 'Bearer'; + this.accessTokenProp = 'access_token'; + this.accessTokenName = 'token'; + this.accessTokenRoot = false; + this.useRefreshToken = false; + this.autoUpdateToken = true; + this.clientId = false; + this.refreshTokenProp = 'refresh_token'; + this.refreshTokenName = 'token'; + this.refreshTokenRoot = false; + this.httpInterceptor = true; + this.withCredentials = true; + this.platform = 'browser'; + this.storage = 'localStorage'; + this.storageKey = 'aurelia_authentication'; + this.globalValueConverters = ['authFilterValueConverter']; + this.providers = { + facebook: { + name: 'facebook', + url: '/auth/facebook', + authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', + redirectUri: PLATFORM.location.origin + '/', + requiredUrlParams: ['display', 'scope'], + scope: ['email'], + scopeDelimiter: ',', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 580, height: 400 } + }, + google: { + name: 'google', + url: '/auth/google', + authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['scope'], + optionalUrlParams: ['display', 'state'], + scope: ['profile', 'email'], + scopePrefix: 'openid', + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 452, height: 633 }, + state: randomState + }, + github: { + name: 'github', + url: '/auth/github', + authorizationEndpoint: 'https://github.com/login/oauth/authorize', + redirectUri: PLATFORM.location.origin, + optionalUrlParams: ['scope'], + scope: ['user:email'], + scopeDelimiter: ' ', + oauthType: '2.0', + popupOptions: { width: 1020, height: 618 } + }, + instagram: { + name: 'instagram', + url: '/auth/instagram', + authorizationEndpoint: 'https://api.instagram.com/oauth/authorize', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['scope'], + scope: ['basic'], + scopeDelimiter: '+', + oauthType: '2.0' + }, + linkedin: { + name: 'linkedin', + url: '/auth/linkedin', + authorizationEndpoint: 'https://www.linkedin.com/uas/oauth2/authorization', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['state'], + scope: ['r_emailaddress'], + scopeDelimiter: ' ', + state: 'STATE', + oauthType: '2.0', + popupOptions: { width: 527, height: 582 } + }, + twitter: { + name: 'twitter', + url: '/auth/twitter', + authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', + redirectUri: PLATFORM.location.origin, + oauthType: '1.0', + popupOptions: { width: 495, height: 645 } + }, + twitch: { + name: 'twitch', + url: '/auth/twitch', + authorizationEndpoint: 'https://api.twitch.tv/kraken/oauth2/authorize', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['scope'], + scope: ['user_read'], + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 500, height: 560 } + }, + live: { + name: 'live', + url: '/auth/live', + authorizationEndpoint: 'https://login.live.com/oauth20_authorize.srf', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['display', 'scope'], + scope: ['wl.emails'], + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 500, height: 560 } + }, + yahoo: { + name: 'yahoo', + url: '/auth/yahoo', + authorizationEndpoint: 'https://api.login.yahoo.com/oauth2/request_auth', + redirectUri: PLATFORM.location.origin, + scope: [], + scopeDelimiter: ',', + oauthType: '2.0', + popupOptions: { width: 559, height: 519 } + }, + bitbucket: { + name: 'bitbucket', + url: '/auth/bitbucket', + authorizationEndpoint: 'https://bitbucket.org/site/oauth2/authorize', + redirectUri: PLATFORM.location.origin + '/', + requiredUrlParams: ['scope'], + scope: ['email'], + 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: randomState + } + }; + this._authToken = 'Bearer'; + this._responseTokenProp = 'access_token'; + this._tokenName = 'token'; + this._tokenRoot = false; + this._tokenPrefix = 'aurelia'; + } + + BaseConfig.prototype.joinBase = function joinBase(url) { + return join(this.baseUrl, url); + }; + + BaseConfig.prototype.configure = function configure(incomming) { + for (var key in incomming) { + var value = incomming[key]; + if (value !== undefined) { + if (Array.isArray(value) || (typeof value === 'undefined' ? 'undefined' : _typeof(value)) !== 'object' || value === null) { + this[key] = value; + } else { + extend(true, this[key], value); + } + } + } + }; + + _createClass(BaseConfig, [{ + key: 'authToken', + set: function set(authToken) { + LogManager.getLogger('authentication').warn('BaseConfig.authToken is deprecated. Use BaseConfig.authTokenType instead.'); + this._authTokenType = authToken; + this.authTokenType = authToken; + return authToken; + }, + get: function get() { + return this._authTokenType; + } + }, { + key: 'responseTokenProp', + set: function set(responseTokenProp) { + LogManager.getLogger('authentication').warn('BaseConfig.responseTokenProp is deprecated. Use BaseConfig.accessTokenProp instead.'); + this._responseTokenProp = responseTokenProp; + this.accessTokenProp = responseTokenProp; + return responseTokenProp; + }, + get: function get() { + return this._responseTokenProp; + } + }, { + key: 'tokenRoot', + set: function set(tokenRoot) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenRoot is deprecated. Use BaseConfig.accessTokenRoot instead.'); + this._tokenRoot = tokenRoot; + this.accessTokenRoot = tokenRoot; + return tokenRoot; + }, + get: function get() { + return this._tokenRoot; + } + }, { + key: 'tokenName', + set: function set(tokenName) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenName is deprecated. Use BaseConfig.accessTokenName instead.'); + this._tokenName = tokenName; + this.accessTokenName = tokenName; + return tokenName; + }, + get: function get() { + return this._tokenName; + } + }, { + key: 'tokenPrefix', + set: function set(tokenPrefix) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenPrefix is obsolete. Use BaseConfig.storageKey instead.'); + this._tokenPrefix = tokenPrefix; + return tokenPrefix; + }, + get: function get() { + return this._tokenPrefix || 'aurelia'; + } + }, { + key: 'current', + get: function get() { + LogManager.getLogger('authentication').warn('Getter BaseConfig.current is deprecated. Use BaseConfig directly instead.'); + return this; + }, + set: function set(_) { + throw new Error('Setter BaseConfig.current is obsolete. Use BaseConfig directly instead.'); + } + }, { + key: '_current', + get: function get() { + LogManager.getLogger('authentication').warn('Getter BaseConfig._current is deprecated. Use BaseConfig directly instead.'); + return this; + }, + set: function set(_) { + throw new Error('Setter BaseConfig._current is obsolete. Use BaseConfig directly instead.'); + } + }]); + + return BaseConfig; +}(); + +function randomState() { + var rand = Math.random().toString(36).substr(2); + return encodeURIComponent(rand); +} + +export var FetchConfig = (_dec13 = inject(HttpClient, Config, AuthService, BaseConfig), _dec13(_class10 = function () { + function FetchConfig(httpClient, clientConfig, authService, config) { + + + this.httpClient = httpClient; + this.clientConfig = clientConfig; + this.authService = authService; + this.config = config; + } + + FetchConfig.prototype.configure = function configure(client) { + var _this10 = this; + + if (Array.isArray(client)) { + var _ret = function () { + var configuredClients = []; + client.forEach(function (toConfigure) { + configuredClients.push(_this10.configure(toConfigure)); + }); + + return { + v: configuredClients + }; + }(); + + if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; + } + + if (typeof client === 'string') { + var endpoint = this.clientConfig.getEndpoint(client); + if (!endpoint) { + throw new Error('There is no \'' + (client || 'default') + '\' endpoint registered.'); + } + client = endpoint.client; + } else if (client instanceof Rest) { + client = client.client; + } else if (!(client instanceof HttpClient)) { + client = this.httpClient; + } + + client.interceptors.push(this.interceptor); + + return client; + }; + + _createClass(FetchConfig, [{ + key: 'interceptor', + get: function get() { + var _this11 = this; + + return { + request: function request(_request) { + if (!_this11.config.httpInterceptor || !_this11.authService.isAuthenticated()) { + return _request; + } + var token = _this11.authService.getAccessToken(); + + if (_this11.config.authTokenType) { + token = _this11.config.authTokenType + ' ' + token; + } + + _request.headers.set(_this11.config.authHeader, token); + + return _request; + }, + response: function response(_response, request) { + return new Promise(function (resolve, reject) { + if (_response.ok) { + return resolve(_response); + } + if (_response.status !== 401) { + return resolve(_response); + } + if (!_this11.config.httpInterceptor || !_this11.authService.isTokenExpired()) { + return resolve(_response); + } + if (!_this11.config.useRefreshToken || !_this11.authService.getRefreshToken()) { + return resolve(_response); + } + + return _this11.authService.updateToken().then(function () { + var token = _this11.authService.getAccessToken(); + + if (_this11.config.authTokenType) { + token = _this11.config.authTokenType + ' ' + token; + } + + request.headers.set(_this11.config.authHeader, token); + + return _this11.client.fetch(request).then(resolve); + }); + }); + } + }; + } + }]); + + return FetchConfig; +}()) || _class10); + +export var OAuth1 = (_dec14 = inject(Storage, Popup, BaseConfig), _dec14(_class11 = function () { + function OAuth1(storage, popup, config) { + + + this.storage = storage; + this.config = config; + this.popup = popup; + this.defaults = { + url: null, + name: null, + popupOptions: null, + redirectUri: null, + authorizationEndpoint: null + }; + } + + OAuth1.prototype.open = function open(options, userData) { + var _this12 = this; + + var provider = extend(true, {}, this.defaults, options); + var serverUrl = this.config.joinBase(provider.url); + + if (this.config.platform !== 'mobile') { + this.popup = this.popup.open('', provider.name, provider.popupOptions); + } + + return this.config.client.post(serverUrl).then(function (response) { + var url = provider.authorizationEndpoint + '?' + buildQueryString(response); + + if (_this12.config.platform === 'mobile') { + _this12.popup = _this12.popup.open(url, provider.name, provider.popupOptions); + } else { + _this12.popup.popupWindow.location = url; + } + + var popupListener = _this12.config.platform === 'mobile' ? _this12.popup.eventListener(provider.redirectUri) : _this12.popup.pollPopup(); + + return popupListener.then(function (result) { + return _this12.exchangeForToken(result, userData, provider); + }); + }); + }; + + OAuth1.prototype.exchangeForToken = function exchangeForToken(oauthData, userData, provider) { + var data = extend(true, {}, userData, oauthData); + var serverUrl = this.config.joinBase(provider.url); + var credentials = this.config.withCredentials ? 'include' : 'same-origin'; + + return this.config.client.post(serverUrl, data, { credentials: credentials }); + }; + + return OAuth1; +}()) || _class11); + +export var OAuth2 = (_dec15 = inject(Storage, Popup, BaseConfig), _dec15(_class12 = function () { + function OAuth2(storage, popup, config) { + + + this.storage = storage; + this.config = config; + this.popup = popup; + this.defaults = { + url: null, + name: null, + state: null, + scope: null, + scopeDelimiter: null, + redirectUri: null, + popupOptions: null, + authorizationEndpoint: null, + responseParams: null, + requiredUrlParams: null, + optionalUrlParams: null, + defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], + responseType: 'code' + }; + } + + OAuth2.prototype.open = function open(options, userData) { + var _this13 = this; + + var provider = extend(true, {}, this.defaults, options); + var 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); + } + + var url = provider.authorizationEndpoint + '?' + buildQueryString(this.buildQuery(provider)); + var popup = this.popup.open(url, provider.name, provider.popupOptions); + var openPopup = this.config.platform === 'mobile' ? popup.eventListener(provider.redirectUri) : popup.pollPopup(); + + return openPopup.then(function (oauthData) { + if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { + return oauthData; + } + if (oauthData.state && oauthData.state !== _this13.storage.get(stateName)) { + return Promise.reject('OAuth 2.0 state parameter mismatch.'); + } + return _this13.exchangeForToken(oauthData, userData, provider); + }); + }; + + OAuth2.prototype.exchangeForToken = function exchangeForToken(oauthData, userData, provider) { + var data = extend(true, {}, userData, { + clientId: provider.clientId, + redirectUri: provider.redirectUri + }, oauthData); + + var serverUrl = this.config.joinBase(provider.url); + var credentials = this.config.withCredentials ? 'include' : 'same-origin'; + + return this.config.client.post(serverUrl, data, { credentials: credentials }); + }; + + OAuth2.prototype.buildQuery = function buildQuery(provider) { + var _this14 = this; + + var query = {}; + var urlParams = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams']; + + urlParams.forEach(function (params) { + (provider[params] || []).forEach(function (paramName) { + var camelizedName = camelCase(paramName); + var paramValue = typeof provider[paramName] === 'function' ? provider[paramName]() : provider[camelizedName]; + + if (paramName === 'state') { + paramValue = encodeURIComponent(_this14.storage.get(provider.name + '_state')); + } + + if (paramName === 'scope' && Array.isArray(paramValue)) { + paramValue = paramValue.join(provider.scopeDelimiter); + + if (provider.scopePrefix) { + paramValue = provider.scopePrefix + provider.scopeDelimiter + paramValue; + } + } + + query[paramName] = paramValue; + }); + }); + return query; + }; + + return OAuth2; +}()) || _class12); + +var camelCase = function camelCase(name) { + return name.replace(/([\:\-\_]+(.))/g, function (_, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }); +}; + +export var Popup = function () { + function Popup() { + + + this.popupWindow = null; + this.polling = null; + this.url = ''; + } + + Popup.prototype.open = function open(url, windowName, options) { + this.url = url; + var optionsString = buildPopupWindowOptions(options || {}); + + this.popupWindow = PLATFORM.global.open(url, windowName, optionsString); + + if (this.popupWindow && this.popupWindow.focus) { + this.popupWindow.focus(); + } + + return this; + }; + + Popup.prototype.eventListener = function eventListener(redirectUri) { + var _this15 = this; + + return new Promise(function (resolve, reject) { + _this15.popupWindow.addEventListener('loadstart', function (event) { + if (event.url.indexOf(redirectUri) !== 0) { + return; + } + + var parser = DOM.createElement('a'); + parser.href = event.url; + + if (parser.search || parser.hash) { + var qs = parseUrl(parser); + + if (qs.error) { + reject({ error: qs.error }); + } else { + resolve(qs); + } + + _this15.popupWindow.close(); + } + }); + + _this15.popupWindow.addEventListener('exit', function () { + reject({ data: 'Provider Popup was closed' }); + }); + + _this15.popupWindow.addEventListener('loaderror', function () { + reject({ data: 'Authorization Failed' }); + }); + }); + }; + + Popup.prototype.pollPopup = function pollPopup() { + var _this16 = this; + + return new Promise(function (resolve, reject) { + _this16.polling = PLATFORM.global.setInterval(function () { + var errorData = void 0; + + try { + if (_this16.popupWindow.location.host === PLATFORM.global.document.location.host && (_this16.popupWindow.location.search || _this16.popupWindow.location.hash)) { + var qs = parseUrl(_this16.popupWindow.location); + + if (qs.error) { + reject({ error: qs.error }); + } else { + resolve(qs); + } + + _this16.popupWindow.close(); + PLATFORM.global.clearInterval(_this16.polling); + } + } catch (error) { + errorData = error; + } + + if (!_this16.popupWindow) { + PLATFORM.global.clearInterval(_this16.polling); + reject({ + error: errorData, + data: 'Provider Popup Blocked' + }); + } else if (_this16.popupWindow.closed) { + PLATFORM.global.clearInterval(_this16.polling); + reject({ + error: errorData, + data: 'Problem poll popup' + }); + } + }, 35); + }); + }; + + return Popup; +}(); + +var buildPopupWindowOptions = function buildPopupWindowOptions(options) { + var width = options.width || 500; + var height = options.height || 500; + + var extended = extend({ + width: width, + height: height, + left: PLATFORM.global.screenX + (PLATFORM.global.outerWidth - width) / 2, + top: PLATFORM.global.screenY + (PLATFORM.global.outerHeight - height) / 2.5 + }, options); + + var parts = []; + Object.keys(extended).map(function (key) { + return parts.push(key + '=' + extended[key]); + }); + + return parts.join(','); +}; + +var parseUrl = function parseUrl(url) { + return extend(true, {}, parseQueryString(url.search), parseQueryString(url.hash)); +}; + +export var Storage = (_dec16 = inject(BaseConfig), _dec16(_class13 = function () { + function Storage(config) { + + + this.config = config; + } + + Storage.prototype.get = function get(key) { + return PLATFORM.global[this.config.storage].getItem(key); + }; + + Storage.prototype.set = function set(key, value) { + PLATFORM.global[this.config.storage].setItem(key, value); + }; + + Storage.prototype.remove = function remove(key) { + PLATFORM.global[this.config.storage].removeItem(key); + }; + + return Storage; +}()) || _class13); \ No newline at end of file diff --git a/dist/native-modules/authFilterValueConverter.js b/dist/native-modules/authFilterValueConverter.js new file mode 100644 index 0000000..b8f34e4 --- /dev/null +++ b/dist/native-modules/authFilterValueConverter.js @@ -0,0 +1,15 @@ + + +export var AuthFilterValueConverter = function () { + function AuthFilterValueConverter() { + + } + + AuthFilterValueConverter.prototype.toView = function toView(routes, isAuthenticated) { + return routes.filter(function (route) { + return typeof route.config.auth !== 'boolean' || route.config.auth === isAuthenticated; + }); + }; + + return AuthFilterValueConverter; +}(); \ No newline at end of file diff --git a/dist/native-modules/authenticatedFilterValueConverter.js b/dist/native-modules/authenticatedFilterValueConverter.js new file mode 100644 index 0000000..d556fdf --- /dev/null +++ b/dist/native-modules/authenticatedFilterValueConverter.js @@ -0,0 +1,24 @@ +var _dec, _class; + + + +import { inject } from 'aurelia-dependency-injection'; +import { AuthService } from './aurelia-authentication'; + +export var AuthenticatedFilterValueConverter = (_dec = inject(AuthService), _dec(_class = function () { + function AuthenticatedFilterValueConverter(authService) { + + + this.authService = authService; + } + + AuthenticatedFilterValueConverter.prototype.toView = function toView(routes) { + var isAuthenticated = arguments.length <= 1 || arguments[1] === undefined ? this.authService.authenticated : arguments[1]; + + return routes.filter(function (route) { + return typeof route.config.auth !== 'boolean' || route.config.auth === isAuthenticated; + }); + }; + + return AuthenticatedFilterValueConverter; +}()) || _class); \ No newline at end of file diff --git a/dist/native-modules/authenticatedValueConverter.js b/dist/native-modules/authenticatedValueConverter.js new file mode 100644 index 0000000..283b18a --- /dev/null +++ b/dist/native-modules/authenticatedValueConverter.js @@ -0,0 +1,20 @@ +var _dec, _class; + + + +import { inject } from 'aurelia-dependency-injection'; +import { AuthService } from './aurelia-authentication'; + +export var AuthenticatedValueConverter = (_dec = inject(AuthService), _dec(_class = function () { + function AuthenticatedValueConverter(authService) { + + + this.authService = authService; + } + + AuthenticatedValueConverter.prototype.toView = function toView() { + return this.authService.authenticated; + }; + + return AuthenticatedValueConverter; +}()) || _class); \ No newline at end of file diff --git a/dist/native-modules/index.js b/dist/native-modules/index.js new file mode 100644 index 0000000..f4493eb --- /dev/null +++ b/dist/native-modules/index.js @@ -0,0 +1 @@ +export * from './aurelia-authentication'; \ No newline at end of file diff --git a/dist/system/aurelia-authentication.d.ts b/dist/system/aurelia-authentication.d.ts deleted file mode 100644 index ba2a7d6..0000000 --- a/dist/system/aurelia-authentication.d.ts +++ /dev/null @@ -1,495 +0,0 @@ -declare module 'aurelia-authentication' { - export class Auth0Lock { - constructor(storage?: any, config?: any); - open(options?: any, userData?: any): any; - } - export class AuthenticatedFilterValueConverter { - constructor(authService?: any); - - /** - * route toView predictator on route.config.auth === (parameter || authService.isAuthenticated()) - * @param {RouteConfig} routes the routes array to convert - * @param {[Boolean]} [isAuthenticated] optional isAuthenticated value. default: this.authService.authenticated - * @return {Boolean} show/hide element - */ - toView(routes?: any, isAuthenticated?: any): any; - } - export class AuthenticatedValueConverter { - constructor(authService?: any); - - /** - * element toView predictator on authService.isAuthenticated() - * @return {Boolean} show/hide element - */ - toView(): any; - } - export class AuthenticateStep { - constructor(authService?: any); - run(routingContext?: any, next?: any): any; - } - export class Authentication { - constructor(storage?: any, config?: any, oAuth1?: any, oAuth2?: any, auth0Lock?: any); - - /* deprecated methods */ - getLoginRoute(): any; - getLoginRedirect(): any; - getLoginUrl(): any; - getSignupUrl(): any; - getProfileUrl(): any; - getToken(): any; - responseObject: any; - - /* get/set responseObject */ - getResponseObject(): any; - setResponseObject(response?: any): any; - - /* get data, update if needed first */ - getAccessToken(): any; - getRefreshToken(): any; - getPayload(): any; - getExp(): any; - - /* get status from data */ - getTtl(): any; - isTokenExpired(): any; - isAuthenticated(): any; - - /* get and set from response */ - getDataFromResponse(response?: any): any; - getTokenFromResponse(response?: any, tokenProp?: any, tokenName?: any, tokenRoot?: any): any; - toUpdateTokenCallstack(): any; - resolveUpdateTokenCallstack(response?: any): any; - - /** - * Authenticate with third-party - * - * @param {String} name of the provider - * @param {[{}]} [userData] - * - * @return {Promise} - */ - authenticate(name?: any, userData?: any): any; - redirect(redirectUrl?: any, defaultRedirectUrl?: any): any; - } - export class AuthFilterValueConverter { - - /** - * route toView predictator on route.config.auth === isAuthenticated - * @param {RouteConfig} routes the routes array to convert - * @param {Boolean} isAuthenticated authentication status - * @return {Boolean} show/hide element - */ - toView(routes?: any, isAuthenticated?: any): any; - } - export class AuthorizeStep { - constructor(authService?: any); - run(routingContext?: any, next?: any): any; - } - export class AuthService { - - /** - * The Authentication instance that handles the token - * - * @param {Authentication} - */ - authentication: any; - - /** - * The Config instance that contains the current configuration setting - * - * @param {Config} - */ - config: any; - - /** - * The current login status - * - * @param {Boolean} - */ - authenticated: any; - - /** - * The currently set timeoutID - * - * @param {Number} - */ - timeoutID: any; - - /** - * Create an AuthService instance - * - * @param {Authentication} authentication The Authentication instance to be used - * @param {Config} config The Config instance to be used - */ - constructor(authentication?: any, config?: any); - - /** - * Getter: The configured client for all aurelia-authentication requests - * - * @return {HttpClient} - */ - client: any; - auth: any; - - /** - * Sets the login timeout - * - * @param {Number} ttl Timeout time in ms - */ - setTimeout(ttl?: any): any; - - /** - * Clears the login timeout - */ - clearTimeout(): any; - - /** - * Stores and analyses the servers responseObject. Sets login status and timeout - * - * @param {Object} response The servers response as GOJO - */ - setResponseObject(response?: any): any; - - /** - * Get current user profile from server - * - * @param {[{}|number|string]} [criteriaOrId object or a Number|String converted to {id: criteriaOrId}] - * - * @return {Promise} - */ - getMe(criteriaOrId?: any): any; - - /** - * Send current user profile update to server - - * @param {any} Request body with data. - * @param {[{}|Number|String]} [criteriaOrId object or a Number|String converted to {id: criteriaOrId}] - * - * @return {Promise} - */ - updateMe(body?: any, criteriaOrId?: any): any; - - /** - * Get accessToken from storage - * - * @returns {String} Current accessToken - */ - getAccessToken(): any; - getCurrentToken(): any; - - /** - * Get refreshToken from storage - * - * @returns {String} Current refreshToken - */ - getRefreshToken(): any; - - /** - * Gets authentication status - * - * @returns {Boolean} For Non-JWT and unexpired JWT: true, else: false - */ - isAuthenticated(): any; - - /** - * Gets exp in milliseconds - * - * @returns {Number} Exp for JWT tokens, NaN for all other tokens - */ - getExp(): any; - - /** - * Gets ttl in seconds - * - * @returns {Number} Ttl for JWT tokens, NaN for all other tokens - */ - getTtl(): any; - - /** - * Gets exp from token payload and compares to current time - * - * @returns {Boolean} Returns (ttl > 0)? for JWT, undefined other tokens - */ - isTokenExpired(): any; - - /** - * Get payload from tokens - * - * @returns {Object} Payload for JWT, else null - */ - getTokenPayload(): any; - - /** - * Request new accesss token - * - * @returns {Promise} Requests new token. can be called multiple times - */ - updateToken(): any; - - /** - * Signup locally. Login and redirect depending on config - * - * @param {String|{}} displayNameOrCredentials displayName | object with signup data. - * @param {[String]|{}} emailOrOptions [email | options for post request] - * @param {[String]} passwordOrRedirectUri [password | optional redirectUri overwrite] - * @param {[{}]} options [options] - * @param {[String]} redirectUri [optional redirectUri overwrite] - * - * @return {Promise|Promise} Server response as Object - */ - signup(displayNameOrCredentials?: any, emailOrOptions?: any, passwordOrRedirectUri?: any, options?: any, redirectUri?: any): any; - - /** - * login locally. Redirect depending on config - * - * @param {[String]|{}} emailOrCredentials email | object with signup data. - * @param {[String]} [passwordOrOptions] [password | options for post request] - * @param {[{}]} [optionsOrRedirectUri] [options | redirectUri overwrite]] - * @param {[String]} [redirectUri] [optional redirectUri overwrite] - * - * @return {Promise|Promise} Server response as Object - */ - login(emailOrCredentials?: any, passwordOrOptions?: any, optionsOrRedirectUri?: any, redirectUri?: any): any; - - /** - * logout locally and redirect to redirectUri (if set) or redirectUri of config. Sends logout request first, if set in config - * - * @param {[String]} [redirectUri] [optional redirectUri overwrite] - * - * @return {Promise<>|Promise|Promise} Server response as Object - */ - logout(redirectUri?: any): any; - - /** - * Authenticate with third-party and redirect to redirectUri (if set) or redirectUri of config - * - * @param {String} name Name of the provider - * @param {[String]} [redirectUri] [optional redirectUri overwrite] - * @param {[{}]} [userData] [optional userData for the local authentication server] - * - * @return {Promise|Promise} Server response as Object - */ - authenticate(name?: any, redirectUri?: any, userData?: any): any; - - /** - * Unlink third-party - * - * @param {String} name Name of the provider - * - * @return {Promise|Promise} Server response as Object - */ - unlink(name?: any, redirectUri?: any): any; - } - export class BaseConfig { - - /** - * Prepends baseUrl to a given url - * @param {String} url The relative url to append - * @return {String} joined baseUrl and url - */ - joinBase(url?: any): any; - - /** - * Merge current settings with incomming settings - * @param {Object} incomming Settings object to be merged into the current configuration - * @return {Config} this - */ - configure(incomming?: any): any; - - /* ----------- default config ----------- */ - // Used internally. The used Rest instance; set during configuration (see index.js) - client: any; - - // If using aurelia-api: - // ===================== - // This is the name of the endpoint used for any requests made in relation to authentication (login, logout, etc.). An empty string selects the default endpoint of aurelia-api. - endpoint: any; - - // When authenticated, these endpoints will have the token added to the header of any requests (for authorization). Accepts an array of endpoint names. An empty string selects the default endpoint of aurelia-api. - configureEndpoints: any; - - // SPA related options - // =================== - // The SPA url to which the user is redirected after a successful login - loginRedirect: any; - - // The SPA url to which the user is redirected after a successful logout - logoutRedirect: any; - - // The SPA route used when an unauthenticated user tries to access an SPA page that requires authentication - loginRoute: any; - - // Whether or not an authentication token is provided in the response to a successful signup - loginOnSignup: any; - - // If loginOnSignup == false: The SPA url to which the user is redirected after a successful signup (else loginRedirect is used) - signupRedirect: any; - - // redirect when token expires. 0 = don't redirect (default), 1 = use logoutRedirect, string = redirect there - expiredRedirect: any; - - // API related options - // =================== - // The base url used for all authentication related requests, including provider.url below. - // This appends to the httpClient/endpoint base url, it does not override it. - baseUrl: any; - - // The API endpoint to which login requests are sent - loginUrl: any; - - // The API endpoint to which logout requests are sent (not needed for jwt) - logoutUrl: any; - - // The HTTP method used for 'unlink' requests (Options: 'get' or 'post') - logoutMethod: any; - - // The API endpoint to which signup requests are sent - signupUrl: any; - - // The API endpoint used in profile requests (inc. `find/get` and `update`) - profileUrl: any; - - // The method used to update the profile ('put' or 'patch') - profileMethod: any; - - // The API endpoint used with oAuth to unlink authentication - unlinkUrl: any; - - // The HTTP method used for 'unlink' requests (Options: 'get' or 'post') - unlinkMethod: any; - - // The API endpoint to which refreshToken requests are sent. null = loginUrl - refreshTokenUrl: any; - - // Token Options - // ============= - // The header property used to contain the authToken in the header of API requests that require authentication - authHeader: any; - - // The token name used in the header of API requests that require authentication - authTokenType: any; - - // The the property from which to get the access token after a successful login or signup. Can also be dotted eg "accessTokenProp.accessTokenName" - accessTokenProp: any; - - // If the property defined by `accessTokenProp` is an object: - // ------------------------------------------------------------ - //This is the property from which to get the token `{ "accessTokenProp": { "accessTokenName" : '...' } }` - accessTokenName: any; - - // This allows the token to be a further object deeper `{ "accessTokenProp": { "accessTokenRoot" : { "accessTokenName" : '...' } } }` - accessTokenRoot: any; - - // Refresh Token Options - // ===================== - // Option to turn refresh tokens On/Off - useRefreshToken: any; - - // The option to enable/disable the automatic refresh of Auth tokens using Refresh Tokens - autoUpdateToken: any; - - // Oauth Client Id - clientId: any; - - // The the property from which to get the refresh token after a successful token refresh. Can also be dotted eg "refreshTokenProp.refreshTokenProp" - refreshTokenProp: any; - - // If the property defined by `refreshTokenProp` is an object: - // ----------------------------------------------------------- - // This is the property from which to get the token `{ "refreshTokenProp": { "refreshTokenName" : '...' } }` - refreshTokenName: any; - - // This allows the refresh token to be a further object deeper `{ "refreshTokenProp": { "refreshTokenRoot" : { "refreshTokenName" : '...' } } }` - refreshTokenRoot: any; - - // Miscellaneous Options - // ===================== - // Whether to enable the fetch interceptor which automatically adds the authentication headers - // (or not... e.g. if using a session based API or you want to override the default behaviour) - httpInterceptor: any; - - // For OAuth only: Tell the API whether or not to include token cookies in the response (for session based APIs) - withCredentials: any; - - // Controls how the popup is shown for different devices (Options: 'browser' or 'mobile') - platform: any; - - // Determines the `PLATFORM` property name upon which aurelia-authentication data is stored (Default: `PLATFORM.localStorage`) - storage: any; - - // The key used for storing the authentication response locally - storageKey: any; - - // List of value-converters to make global - globalValueConverters: any; - - //OAuth provider specific related configuration - // ============================================ - providers: any; - - /* deprecated defaults */ - _authToken: any; - _responseTokenProp: any; - _tokenName: any; - _tokenRoot: any; - _tokenPrefix: any; - authToken: any; - responseTokenProp: any; - tokenRoot: any; - tokenName: any; - tokenPrefix: any; - current: any; - _current: any; - } - export class FetchConfig { - - /** - * Construct the FetchConfig - * - * @param {HttpClient} httpClient - * @param {Config} clientConfig - * @param {Authentication} authService - * @param {BaseConfig} config - */ - constructor(httpClient?: any, clientConfig?: any, authService?: any, config?: any); - - /** - * Interceptor for HttpClient - * - * @return {{request: Function, response: Function}} - */ - interceptor: any; - - /** - * Configure client(s) with authorization interceptor - * - * @param {HttpClient|Rest|string[]} client HttpClient, rest client or api endpoint name, or an array thereof - * - * @return {HttpClient[]} - */ - configure(client?: any): any; - } - export class OAuth1 { - constructor(storage?: any, popup?: any, config?: any); - open(options?: any, userData?: any): any; - exchangeForToken(oauthData?: any, userData?: any, provider?: any): any; - } - export class OAuth2 { - constructor(storage?: any, popup?: any, config?: any); - open(options?: any, userData?: any): any; - exchangeForToken(oauthData?: any, userData?: any, provider?: any): any; - buildQuery(provider?: any): any; - } - export class Popup { - constructor(); - open(url?: any, windowName?: any, options?: any): any; - eventListener(redirectUri?: any): any; - pollPopup(): any; - } - export class Storage { - constructor(config?: any); - get(key?: any): any; - set(key?: any, value?: any): any; - remove(key?: any): any; - } -} \ No newline at end of file diff --git a/dist/system/aurelia-authentication.js b/dist/system/aurelia-authentication.js index ded63df..da2cfcc 100644 --- a/dist/system/aurelia-authentication.js +++ b/dist/system/aurelia-authentication.js @@ -1,7 +1,9 @@ 'use strict'; -System.register(['extend', 'aurelia-logging', 'jwt-decode', 'aurelia-pal', 'aurelia-path', 'aurelia-dependency-injection', 'aurelia-metadata', 'aurelia-router', 'aurelia-fetch-client', 'aurelia-api', './authFilterValueConverter'], function (_export, _context) { - var extend, LogManager, jwtDecode, PLATFORM, DOM, parseQueryString, join, buildQueryString, inject, deprecated, Redirect, HttpClient, Config, Rest, _dec, _class2, _dec2, _class3, _dec3, _class4, _dec4, _class5, _dec5, _dec6, _dec7, _dec8, _dec9, _dec10, _dec11, _class6, _desc, _value, _class7, _dec12, _dec13, _class8, _desc2, _value2, _class9, _dec14, _class11, _dec15, _class12, _dec16, _class13, _typeof, _createClass, Popup, buildPopupWindowOptions, parseUrl, BaseConfig, Storage, Auth0Lock, OAuth1, OAuth2, camelCase, Authentication, AuthService, AuthenticateStep, AuthorizeStep, FetchConfig; +System.register(['aurelia-logging', 'extend', 'jwt-decode', 'aurelia-pal', 'aurelia-fetch-client', 'aurelia-api', 'aurelia-dependency-injection', 'aurelia-router', 'aurelia-metadata', 'aurelia-path', './authFilterValueConverter'], function (_export, _context) { + "use strict"; + + var LogManager, extend, jwtDecode, PLATFORM, DOM, HttpClient, Config, Rest, inject, Redirect, deprecated, join, buildQueryString, parseQueryString, _createClass, _dec, _class, _dec2, _class2, _dec3, _dec4, _dec5, _dec6, _dec7, _dec8, _dec9, _class3, _desc, _value, _class4, _dec10, _class5, _dec11, _dec12, _class6, _desc2, _value2, _class7, _dec13, _class10, _dec14, _class11, _dec15, _class12, _dec16, _class13, _typeof, Auth0Lock, AuthenticateStep, Authentication, AuthorizeStep, AuthService, BaseConfig, FetchConfig, OAuth1, OAuth2, camelCase, Popup, buildPopupWindowOptions, parseUrl, Storage; function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; @@ -32,110 +34,40 @@ System.register(['extend', 'aurelia-logging', 'jwt-decode', 'aurelia-pal', 'aure return desc; } - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } + function randomState() { var rand = Math.random().toString(36).substr(2); return encodeURIComponent(rand); } - function configure(aurelia, config) { - if (!PLATFORM.location.origin) { - PLATFORM.location.origin = PLATFORM.location.protocol + '//' + PLATFORM.location.hostname + (PLATFORM.location.port ? ':' + PLATFORM.location.port : ''); - } - - var baseConfig = aurelia.container.get(BaseConfig); - - if (typeof config === 'function') { - config(baseConfig); - } else if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') { - baseConfig.configure(config); - } - - for (var _iterator = baseConfig.globalValueConverters, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; - - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } - - var converter = _ref; - - aurelia.globalResources('./' + converter); - LogManager.getLogger('authentication').info('Add globalResources value-converter: ' + converter); - } - var fetchConfig = aurelia.container.get(FetchConfig); - var clientConfig = aurelia.container.get(Config); - - if (Array.isArray(baseConfig.configureEndpoints)) { - baseConfig.configureEndpoints.forEach(function (endpointToPatch) { - fetchConfig.configure(endpointToPatch); - }); - } - - var client = void 0; - - if (baseConfig.endpoint !== null) { - if (typeof baseConfig.endpoint === 'string') { - var endpoint = clientConfig.getEndpoint(baseConfig.endpoint); - if (!endpoint) { - throw new Error('There is no \'' + (baseConfig.endpoint || 'default') + '\' endpoint registered.'); - } - client = endpoint; - } else if (baseConfig.endpoint instanceof HttpClient) { - client = new Rest(baseConfig.endpoint); - } - } - - if (!(client instanceof Rest)) { - client = new Rest(aurelia.container.get(HttpClient)); - } - - baseConfig.client = client; - } - return { - setters: [function (_extend) { - extend = _extend.default; - }, function (_aureliaLogging) { + setters: [function (_aureliaLogging) { LogManager = _aureliaLogging; + }, function (_extend) { + extend = _extend.default; }, function (_jwtDecode) { jwtDecode = _jwtDecode.default; }, function (_aureliaPal) { PLATFORM = _aureliaPal.PLATFORM; DOM = _aureliaPal.DOM; - }, function (_aureliaPath) { - parseQueryString = _aureliaPath.parseQueryString; - join = _aureliaPath.join; - buildQueryString = _aureliaPath.buildQueryString; - }, function (_aureliaDependencyInjection) { - inject = _aureliaDependencyInjection.inject; - }, function (_aureliaMetadata) { - deprecated = _aureliaMetadata.deprecated; - }, function (_aureliaRouter) { - Redirect = _aureliaRouter.Redirect; }, function (_aureliaFetchClient) { HttpClient = _aureliaFetchClient.HttpClient; }, function (_aureliaApi) { Config = _aureliaApi.Config; Rest = _aureliaApi.Rest; + }, function (_aureliaDependencyInjection) { + inject = _aureliaDependencyInjection.inject; + }, function (_aureliaRouter) { + Redirect = _aureliaRouter.Redirect; + }, function (_aureliaMetadata) { + deprecated = _aureliaMetadata.deprecated; + }, function (_aureliaPath) { + join = _aureliaPath.join; + buildQueryString = _aureliaPath.buildQueryString; + parseQueryString = _aureliaPath.parseQueryString; }, function (_authFilterValueConverter) {}], execute: function () { - _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { - return typeof obj; - } : function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; - }; - _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { @@ -154,1324 +86,1381 @@ System.register(['extend', 'aurelia-logging', 'jwt-decode', 'aurelia-pal', 'aure }; }(); - _export('Popup', Popup = function () { - function Popup() { - _classCallCheck(this, Popup); + _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; + } : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; + }; + function configure(aurelia, config) { + if (!PLATFORM.location.origin) { + PLATFORM.location.origin = PLATFORM.location.protocol + '//' + PLATFORM.location.hostname + (PLATFORM.location.port ? ':' + PLATFORM.location.port : ''); + } - this.popupWindow = null; - this.polling = null; - this.url = ''; + var baseConfig = aurelia.container.get(BaseConfig); + + if (typeof config === 'function') { + config(baseConfig); + } else if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') { + baseConfig.configure(config); } - Popup.prototype.open = function open(url, windowName, options) { - this.url = url; - var optionsString = buildPopupWindowOptions(options || {}); + for (var _iterator = baseConfig.globalValueConverters, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; - this.popupWindow = PLATFORM.global.open(url, windowName, optionsString); + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; + } - if (this.popupWindow && this.popupWindow.focus) { - this.popupWindow.focus(); + var converter = _ref; + + aurelia.globalResources('./' + converter); + LogManager.getLogger('authentication').info('Add globalResources value-converter: ' + converter); + } + var fetchConfig = aurelia.container.get(FetchConfig); + var clientConfig = aurelia.container.get(Config); + + if (Array.isArray(baseConfig.configureEndpoints)) { + baseConfig.configureEndpoints.forEach(function (endpointToPatch) { + fetchConfig.configure(endpointToPatch); + }); + } + + var client = void 0; + + if (baseConfig.endpoint !== null) { + if (typeof baseConfig.endpoint === 'string') { + var endpoint = clientConfig.getEndpoint(baseConfig.endpoint); + if (!endpoint) { + throw new Error('There is no \'' + (baseConfig.endpoint || 'default') + '\' endpoint registered.'); + } + client = endpoint; + } else if (baseConfig.endpoint instanceof HttpClient) { + client = new Rest(baseConfig.endpoint); } + } - return this; - }; + if (!(client instanceof Rest)) { + client = new Rest(aurelia.container.get(HttpClient)); + } - Popup.prototype.eventListener = function eventListener(redirectUri) { + baseConfig.client = client; + } + + _export('configure', configure); + + _export('Auth0Lock', Auth0Lock = (_dec = inject(Storage, BaseConfig), _dec(_class = function () { + function Auth0Lock(storage, config) { + _classCallCheck(this, Auth0Lock); + + 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' + }; + } + + Auth0Lock.prototype.open = function open(options, userData) { var _this = this; - return new Promise(function (resolve, reject) { - _this.popupWindow.addEventListener('loadstart', function (event) { - if (event.url.indexOf(redirectUri) !== 0) { - return; - } + if (typeof PLATFORM.global.Auth0Lock !== 'function') { + throw new Error('Auth0Lock was not found in global scope. Please load it before using this provider.'); + } + var provider = extend(true, {}, this.defaults, options); + var stateName = provider.name + '_state'; - var parser = DOM.createElement('a'); - parser.href = event.url; + if (typeof provider.state === 'function') { + this.storage.set(stateName, provider.state()); + } else if (typeof provider.state === 'string') { + this.storage.set(stateName, provider.state); + } - if (parser.search || parser.hash) { - var qs = parseUrl(parser); + this.lock = this.lock || new PLATFORM.global.Auth0Lock(provider.clientId, provider.clientDomain); - if (qs.error) { - reject({ error: qs.error }); - } else { - resolve(qs); - } + var openPopup = new Promise(function (resolve, reject) { + var 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.popupWindow.close(); + _this.lock.show(provider.lockOptions, function (err, profile, tokenOrCode) { + if (err) { + reject(err); + } else { + resolve({ + access_token: tokenOrCode + }); } }); + }); - _this.popupWindow.addEventListener('exit', function () { - reject({ data: 'Provider Popup was closed' }); - }); + return openPopup.then(function (lockResponse) { + if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { + return lockResponse; + } - _this.popupWindow.addEventListener('loaderror', function () { - reject({ data: 'Authorization Failed' }); - }); + throw new Error('Only `token` responseType is supported'); }); }; - Popup.prototype.pollPopup = function pollPopup() { - var _this2 = this; + return Auth0Lock; + }()) || _class)); - return new Promise(function (resolve, reject) { - _this2.polling = PLATFORM.global.setInterval(function () { - var errorData = void 0; + _export('Auth0Lock', Auth0Lock); - try { - if (_this2.popupWindow.location.host === PLATFORM.global.document.location.host && (_this2.popupWindow.location.search || _this2.popupWindow.location.hash)) { - var qs = parseUrl(_this2.popupWindow.location); + _export('AuthenticateStep', AuthenticateStep = (_dec2 = inject(AuthService), _dec2(_class2 = function () { + function AuthenticateStep(authService) { + - if (qs.error) { - reject({ error: qs.error }); - } else { - resolve(qs); - } + this.authService = authService; + } - _this2.popupWindow.close(); - PLATFORM.global.clearInterval(_this2.polling); - } - } catch (error) { - errorData = error; - } + AuthenticateStep.prototype.run = function run(routingContext, next) { + var isLoggedIn = this.authService.authenticated; + var loginRoute = this.authService.config.loginRoute; - if (!_this2.popupWindow) { - PLATFORM.global.clearInterval(_this2.polling); - reject({ - error: errorData, - data: 'Provider Popup Blocked' - }); - } else if (_this2.popupWindow.closed) { - PLATFORM.global.clearInterval(_this2.polling); - reject({ - error: errorData, - data: 'Problem poll popup' - }); - } - }, 35); - }); - }; + if (routingContext.getAllInstructions().some(function (route) { + return route.config.auth === true; + })) { + if (!isLoggedIn) { + return next.cancel(new Redirect(loginRoute)); + } + } else if (isLoggedIn && routingContext.getAllInstructions().some(function (route) { + return route.fragment === loginRoute; + })) { + return next.cancel(new Redirect(this.authService.config.loginRedirect)); + } - return Popup; - }()); + return next(); + }; - _export('Popup', Popup); + return AuthenticateStep; + }()) || _class2)); - buildPopupWindowOptions = function buildPopupWindowOptions(options) { - var width = options.width || 500; - var height = options.height || 500; + _export('AuthenticateStep', AuthenticateStep); - var extended = extend({ - width: width, - height: height, - left: PLATFORM.global.screenX + (PLATFORM.global.outerWidth - width) / 2, - top: PLATFORM.global.screenY + (PLATFORM.global.outerHeight - height) / 2.5 - }, options); + _export('Authentication', Authentication = (_dec3 = inject(Storage, BaseConfig, OAuth1, OAuth2, Auth0Lock), _dec4 = deprecated({ message: 'Use baseConfig.loginRoute instead.' }), _dec5 = deprecated({ message: 'Use baseConfig.loginRedirect instead.' }), _dec6 = deprecated({ message: 'Use baseConfig.joinBase(baseConfig.loginUrl) instead.' }), _dec7 = deprecated({ message: 'Use baseConfig.joinBase(baseConfig.signupUrl) instead.' }), _dec8 = deprecated({ message: 'Use baseConfig.joinBase(baseConfig.profileUrl) instead.' }), _dec9 = deprecated({ message: 'Use .getAccessToken() instead.' }), _dec3(_class3 = (_class4 = function () { + function Authentication(storage, config, oAuth1, oAuth2, auth0Lock) { + - var parts = []; - Object.keys(extended).map(function (key) { - return parts.push(key + '=' + extended[key]); - }); + this.storage = storage; + this.config = config; + this.oAuth1 = oAuth1; + this.oAuth2 = oAuth2; + this.auth0Lock = auth0Lock; + this.updateTokenCallstack = []; + this.accessToken = null; + this.refreshToken = null; + this.payload = null; + this.exp = null; + this.hasDataStored = false; + } - return parts.join(','); - }; + Authentication.prototype.getLoginRoute = function getLoginRoute() { + return this.config.loginRoute; + }; - parseUrl = function parseUrl(url) { - return extend(true, {}, parseQueryString(url.search), parseQueryString(url.hash)); - }; + Authentication.prototype.getLoginRedirect = function getLoginRedirect() { + return this.config.loginRedirect; + }; - _export('BaseConfig', BaseConfig = function () { - function BaseConfig() { - _classCallCheck(this, BaseConfig); + Authentication.prototype.getLoginUrl = function getLoginUrl() { + return this.Config.joinBase(this.config.loginUrl); + }; - this.client = null; - this.endpoint = null; - this.configureEndpoints = null; - this.loginRedirect = '#/'; - this.logoutRedirect = '#/'; - this.loginRoute = '/login'; - this.loginOnSignup = true; - this.signupRedirect = '#/login'; - this.expiredRedirect = 0; - this.baseUrl = ''; - this.loginUrl = '/auth/login'; - this.logoutUrl = null; - this.logoutMethod = 'get'; - this.signupUrl = '/auth/signup'; - this.profileUrl = '/auth/me'; - this.profileMethod = 'put'; - this.unlinkUrl = '/auth/unlink/'; - this.unlinkMethod = 'get'; - this.refreshTokenUrl = null; - this.authHeader = 'Authorization'; - this.authTokenType = 'Bearer'; - this.accessTokenProp = 'access_token'; - this.accessTokenName = 'token'; - this.accessTokenRoot = false; - this.useRefreshToken = false; - this.autoUpdateToken = true; - this.clientId = false; - this.refreshTokenProp = 'refresh_token'; - this.refreshTokenName = 'token'; - this.refreshTokenRoot = false; - this.httpInterceptor = true; - this.withCredentials = true; - this.platform = 'browser'; - this.storage = 'localStorage'; - this.storageKey = 'aurelia_authentication'; - this.globalValueConverters = ['authFilterValueConverter']; - this.providers = { - facebook: { - name: 'facebook', - url: '/auth/facebook', - authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', - redirectUri: PLATFORM.location.origin + '/', - requiredUrlParams: ['display', 'scope'], - scope: ['email'], - scopeDelimiter: ',', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 580, height: 400 } - }, - google: { - name: 'google', - url: '/auth/google', - authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth', - redirectUri: PLATFORM.location.origin, - requiredUrlParams: ['scope'], - optionalUrlParams: ['display', 'state'], - scope: ['profile', 'email'], - scopePrefix: 'openid', - scopeDelimiter: ' ', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 452, height: 633 }, - state: randomState - }, - github: { - name: 'github', - url: '/auth/github', - authorizationEndpoint: 'https://github.com/login/oauth/authorize', - redirectUri: PLATFORM.location.origin, - optionalUrlParams: ['scope'], - scope: ['user:email'], - scopeDelimiter: ' ', - oauthType: '2.0', - popupOptions: { width: 1020, height: 618 } - }, - instagram: { - name: 'instagram', - url: '/auth/instagram', - authorizationEndpoint: 'https://api.instagram.com/oauth/authorize', - redirectUri: PLATFORM.location.origin, - requiredUrlParams: ['scope'], - scope: ['basic'], - scopeDelimiter: '+', - oauthType: '2.0' - }, - linkedin: { - name: 'linkedin', - url: '/auth/linkedin', - authorizationEndpoint: 'https://www.linkedin.com/uas/oauth2/authorization', - redirectUri: PLATFORM.location.origin, - requiredUrlParams: ['state'], - scope: ['r_emailaddress'], - scopeDelimiter: ' ', - state: 'STATE', - oauthType: '2.0', - popupOptions: { width: 527, height: 582 } - }, - twitter: { - name: 'twitter', - url: '/auth/twitter', - authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', - redirectUri: PLATFORM.location.origin, - oauthType: '1.0', - popupOptions: { width: 495, height: 645 } - }, - twitch: { - name: 'twitch', - url: '/auth/twitch', - authorizationEndpoint: 'https://api.twitch.tv/kraken/oauth2/authorize', - redirectUri: PLATFORM.location.origin, - requiredUrlParams: ['scope'], - scope: ['user_read'], - scopeDelimiter: ' ', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 500, height: 560 } - }, - live: { - name: 'live', - url: '/auth/live', - authorizationEndpoint: 'https://login.live.com/oauth20_authorize.srf', - redirectUri: PLATFORM.location.origin, - requiredUrlParams: ['display', 'scope'], - scope: ['wl.emails'], - scopeDelimiter: ' ', - display: 'popup', - oauthType: '2.0', - popupOptions: { width: 500, height: 560 } - }, - yahoo: { - name: 'yahoo', - url: '/auth/yahoo', - authorizationEndpoint: 'https://api.login.yahoo.com/oauth2/request_auth', - redirectUri: PLATFORM.location.origin, - scope: [], - scopeDelimiter: ',', - oauthType: '2.0', - popupOptions: { width: 559, height: 519 } - }, - bitbucket: { - name: 'bitbucket', - url: '/auth/bitbucket', - authorizationEndpoint: 'https://bitbucket.org/site/oauth2/authorize', - redirectUri: PLATFORM.location.origin + '/', - requiredUrlParams: ['scope'], - scope: ['email'], - 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: randomState - } - }; - this._authToken = 'Bearer'; - this._responseTokenProp = 'access_token'; - this._tokenName = 'token'; - this._tokenRoot = false; - this._tokenPrefix = 'aurelia'; - } + Authentication.prototype.getSignupUrl = function getSignupUrl() { + return this.Config.joinBase(this.config.signupUrl); + }; - BaseConfig.prototype.joinBase = function joinBase(url) { - return join(this.baseUrl, url); + Authentication.prototype.getProfileUrl = function getProfileUrl() { + return this.Config.joinBase(this.config.profileUrl); }; - BaseConfig.prototype.configure = function configure(incomming) { - for (var key in incomming) { - var value = incomming[key]; - if (value !== undefined) { - if (Array.isArray(value) || (typeof value === 'undefined' ? 'undefined' : _typeof(value)) !== 'object' || value === null) { - this[key] = value; - } else { - extend(true, this[key], value); - } - } - } + Authentication.prototype.getToken = function getToken() { + return this.getAccessToken(); }; - _createClass(BaseConfig, [{ - key: 'authToken', - set: function set(authToken) { - LogManager.getLogger('authentication').warn('BaseConfig.authToken is deprecated. Use BaseConfig.authTokenType instead.'); - this._authTokenType = authToken; - this.authTokenType = authToken; - return authToken; - }, - get: function get() { - return this._authTokenType; - } - }, { - key: 'responseTokenProp', - set: function set(responseTokenProp) { - LogManager.getLogger('authentication').warn('BaseConfig.responseTokenProp is deprecated. Use BaseConfig.accessTokenProp instead.'); - this._responseTokenProp = responseTokenProp; - this.accessTokenProp = responseTokenProp; - return responseTokenProp; - }, - get: function get() { - return this._responseTokenProp; - } - }, { - key: 'tokenRoot', - set: function set(tokenRoot) { - LogManager.getLogger('authentication').warn('BaseConfig.tokenRoot is deprecated. Use BaseConfig.accessTokenRoot instead.'); - this._tokenRoot = tokenRoot; - this.accessTokenRoot = tokenRoot; - return tokenRoot; - }, - get: function get() { - return this._tokenRoot; - } - }, { - key: 'tokenName', - set: function set(tokenName) { - LogManager.getLogger('authentication').warn('BaseConfig.tokenName is deprecated. Use BaseConfig.accessTokenName instead.'); - this._tokenName = tokenName; - this.accessTokenName = tokenName; - return tokenName; - }, - get: function get() { - return this._tokenName; + Authentication.prototype.getResponseObject = function getResponseObject() { + return JSON.parse(this.storage.get(this.config.storageKey)); + }; + + Authentication.prototype.setResponseObject = function setResponseObject(response) { + if (response) { + this.getDataFromResponse(response); + this.storage.set(this.config.storageKey, JSON.stringify(response)); + return; } - }, { - key: 'tokenPrefix', - set: function set(tokenPrefix) { - LogManager.getLogger('authentication').warn('BaseConfig.tokenPrefix is obsolete. Use BaseConfig.storageKey instead.'); - this._tokenPrefix = tokenPrefix; - return tokenPrefix; - }, - get: function get() { - return this._tokenPrefix || 'aurelia'; + this.accessToken = null; + this.refreshToken = null; + this.payload = null; + this.exp = null; + + this.hasDataStored = false; + + this.storage.remove(this.config.storageKey); + }; + + Authentication.prototype.getAccessToken = function getAccessToken() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.accessToken; + }; + + Authentication.prototype.getRefreshToken = function getRefreshToken() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.refreshToken; + }; + + Authentication.prototype.getPayload = function getPayload() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.payload; + }; + + Authentication.prototype.getExp = function getExp() { + if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); + return this.exp; + }; + + Authentication.prototype.getTtl = function getTtl() { + var exp = this.getExp(); + return Number.isNaN(exp) ? NaN : exp - Math.round(new Date().getTime() / 1000); + }; + + Authentication.prototype.isTokenExpired = function isTokenExpired() { + var timeLeft = this.getTtl(); + return Number.isNaN(timeLeft) ? undefined : timeLeft < 0; + }; + + Authentication.prototype.isAuthenticated = function isAuthenticated() { + var isTokenExpired = this.isTokenExpired(); + if (isTokenExpired === undefined) return this.accessToken ? true : false; + return !isTokenExpired; + }; + + Authentication.prototype.getDataFromResponse = function getDataFromResponse(response) { + var config = this.config; + + this.accessToken = this.getTokenFromResponse(response, config.accessTokenProp, config.accessTokenName, config.accessTokenRoot); + + this.refreshToken = null; + if (config.useRefreshToken) { + try { + this.refreshToken = this.getTokenFromResponse(response, config.refreshTokenProp, config.refreshTokenName, config.refreshTokenRoot); + } catch (e) { + this.refreshToken = null; + } } - }, { - key: 'current', - get: function get() { - LogManager.getLogger('authentication').warn('Getter BaseConfig.current is deprecated. Use BaseConfig directly instead.'); - return this; - }, - set: function set(_) { - throw new Error('Setter BaseConfig.current is obsolete. Use BaseConfig directly instead.'); + + this.payload = null; + + try { + this.payload = this.accessToken ? jwtDecode(this.accessToken) : null; + } catch (_) { + _; } - }, { - key: '_current', - get: function get() { - LogManager.getLogger('authentication').warn('Getter BaseConfig._current is deprecated. Use BaseConfig directly instead.'); - return this; - }, - set: function set(_) { - throw new Error('Setter BaseConfig._current is obsolete. Use BaseConfig directly instead.'); + + this.exp = this.payload ? parseInt(this.payload.exp, 10) : NaN; + + this.hasDataStored = true; + + return { + accessToken: this.accessToken, + refreshToken: this.refreshToken, + payload: this.payload, + exp: this.exp + }; + }; + + Authentication.prototype.getTokenFromResponse = function getTokenFromResponse(response, tokenProp, tokenName, tokenRoot) { + if (!response) return undefined; + + var responseTokenProp = tokenProp.split('.').reduce(function (o, x) { + return o[x]; + }, response); + + if (typeof responseTokenProp === 'string') { + return responseTokenProp; } - }]); - return BaseConfig; - }()); + if ((typeof responseTokenProp === 'undefined' ? 'undefined' : _typeof(responseTokenProp)) === 'object') { + var tokenRootData = tokenRoot && tokenRoot.split('.').reduce(function (o, x) { + return o[x]; + }, responseTokenProp); + var _token = tokenRootData ? tokenRootData[tokenName] : responseTokenProp[tokenName]; - _export('BaseConfig', BaseConfig); + if (!_token) throw new Error('Token not found in response'); - _export('Storage', Storage = (_dec = inject(BaseConfig), _dec(_class2 = function () { - function Storage(config) { - _classCallCheck(this, Storage); + return _token; + } - this.config = config; - } + var token = response[tokenName] === undefined ? null : response[tokenName]; - Storage.prototype.get = function get(key) { - return PLATFORM.global[this.config.storage].getItem(key); + if (!token) throw new Error('Token not found in response'); + + return token; }; - Storage.prototype.set = function set(key, value) { - PLATFORM.global[this.config.storage].setItem(key, value); + Authentication.prototype.toUpdateTokenCallstack = function toUpdateTokenCallstack() { + var _this2 = this; + + return new Promise(function (resolve) { + return _this2.updateTokenCallstack.push(resolve); + }); }; - Storage.prototype.remove = function remove(key) { - PLATFORM.global[this.config.storage].removeItem(key); + Authentication.prototype.resolveUpdateTokenCallstack = function resolveUpdateTokenCallstack(response) { + this.updateTokenCallstack.map(function (resolve) { + return resolve(response); + }); + this.updateTokenCallstack = []; }; - return Storage; - }()) || _class2)); + Authentication.prototype.authenticate = function authenticate(name) { + var userData = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - _export('Storage', Storage); + var oauthType = this.config.providers[name].type; - _export('Auth0Lock', Auth0Lock = (_dec2 = inject(Storage, BaseConfig), _dec2(_class3 = function () { - function Auth0Lock(storage, config) { - _classCallCheck(this, Auth0Lock); + if (oauthType) { + LogManager.getLogger('authentication').warn('DEPRECATED: Setting provider.type is deprecated and replaced by provider.oauthType'); + } else { + oauthType = this.config.providers[name].oauthType; + } - 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' - }; - } + var providerLogin = void 0; + if (oauthType === 'auth0-lock') { + providerLogin = this.auth0Lock; + } else { + providerLogin = oauthType === '1.0' ? this.oAuth1 : this.oAuth2; + } - Auth0Lock.prototype.open = function open(options, userData) { - var _this3 = this; + return providerLogin.open(this.config.providers[name], userData); + }; - if (typeof PLATFORM.global.Auth0Lock !== 'function') { - throw new Error('Auth0Lock was not found in global scope. Please load it before using this provider.'); + Authentication.prototype.redirect = function redirect(redirectUrl, defaultRedirectUrl) { + if (redirectUrl === true) { + LogManager.getLogger('authentication').warn('DEPRECATED: Setting redirectUrl === true to actually *not redirect* is deprecated. Set redirectUrl === 0 instead.'); + return; } - var provider = extend(true, {}, this.defaults, options); - var 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); + if (redirectUrl === false) { + LogManager.getLogger('authentication').warn('BREAKING CHANGE: Setting redirectUrl === false to actually *do redirect* is deprecated. Set redirectUrl to undefined or null to use the defaultRedirectUrl if so desired.'); } - this.lock = this.lock || new PLATFORM.global.Auth0Lock(provider.clientId, provider.clientDomain); + if (redirectUrl === 0) { + return; + } + if (typeof redirectUrl === 'string') { + PLATFORM.location.href = encodeURI(redirectUrl); + } else if (defaultRedirectUrl) { + PLATFORM.location.href = defaultRedirectUrl; + } + }; - var openPopup = new Promise(function (resolve, reject) { - var 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 = _this3.storage.get(provider.name + '_state'); + _createClass(Authentication, [{ + key: 'responseObject', + get: function get() { + LogManager.getLogger('authentication').warn('Getter Authentication.responseObject is deprecated. Use Authentication.getResponseObject() instead.'); + return this.getResponseObject(); + }, + set: function set(response) { + LogManager.getLogger('authentication').warn('Setter Authentication.responseObject is deprecated. Use AuthServive.setResponseObject(response) instead.'); + this.setResponseObject(response); + } + }]); - _this3.lock.show(provider.lockOptions, function (err, profile, tokenOrCode) { - if (err) { - reject(err); - } else { - resolve({ - access_token: tokenOrCode - }); - } - }); - }); + return Authentication; + }(), (_applyDecoratedDescriptor(_class4.prototype, 'getLoginRoute', [_dec4], Object.getOwnPropertyDescriptor(_class4.prototype, 'getLoginRoute'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getLoginRedirect', [_dec5], Object.getOwnPropertyDescriptor(_class4.prototype, 'getLoginRedirect'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getLoginUrl', [_dec6], Object.getOwnPropertyDescriptor(_class4.prototype, 'getLoginUrl'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getSignupUrl', [_dec7], Object.getOwnPropertyDescriptor(_class4.prototype, 'getSignupUrl'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getProfileUrl', [_dec8], Object.getOwnPropertyDescriptor(_class4.prototype, 'getProfileUrl'), _class4.prototype), _applyDecoratedDescriptor(_class4.prototype, 'getToken', [_dec9], Object.getOwnPropertyDescriptor(_class4.prototype, 'getToken'), _class4.prototype)), _class4)) || _class3)); - return openPopup.then(function (lockResponse) { - if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { - return lockResponse; + _export('Authentication', Authentication); + + _export('AuthorizeStep', AuthorizeStep = (_dec10 = inject(AuthService), _dec10(_class5 = function () { + function AuthorizeStep(authService) { + + + LogManager.getLogger('authentication').warn('AuthorizeStep is deprecated. Use AuthenticationStep instead.'); + + this.authService = authService; + } + + AuthorizeStep.prototype.run = function run(routingContext, next) { + var isLoggedIn = this.authService.isAuthenticated(); + var loginRoute = this.authService.config.loginRoute; + + if (routingContext.getAllInstructions().some(function (route) { + return route.config.auth; + })) { + if (!isLoggedIn) { + return next.cancel(new Redirect(loginRoute)); } + } else if (isLoggedIn && routingContext.getAllInstructions().some(function (route) { + return route.fragment === loginRoute; + })) { + return next.cancel(new Redirect(this.authService.config.loginRedirect)); + } - throw new Error('Only `token` responseType is supported'); - }); + return next(); }; - return Auth0Lock; - }()) || _class3)); + return AuthorizeStep; + }()) || _class5)); - _export('Auth0Lock', Auth0Lock); + _export('AuthorizeStep', AuthorizeStep); - _export('OAuth1', OAuth1 = (_dec3 = inject(Storage, Popup, BaseConfig), _dec3(_class4 = function () { - function OAuth1(storage, popup, config) { - _classCallCheck(this, OAuth1); + _export('AuthService', AuthService = (_dec11 = inject(Authentication, BaseConfig), _dec12 = deprecated({ message: 'Use .getAccessToken() instead.' }), _dec11(_class6 = (_class7 = function () { + function AuthService(authentication, config) { + - this.storage = storage; - this.config = config; - this.popup = popup; - this.defaults = { - url: null, - name: null, - popupOptions: null, - redirectUri: null, - authorizationEndpoint: null - }; - } + this.authenticated = false; + this.timeoutID = 0; - OAuth1.prototype.open = function open(options, userData) { - var _this4 = this; + this.authentication = authentication; + this.config = config; - var provider = extend(true, {}, this.defaults, options); - var serverUrl = this.config.joinBase(provider.url); + var oldStorageKey = config.tokenPrefix ? config.tokenPrefix + '_' + config.tokenName : config.tokenName; + var oldToken = authentication.storage.get(oldStorageKey); - if (this.config.platform !== 'mobile') { - this.popup = this.popup.open('', provider.name, provider.popupOptions); + if (oldToken) { + LogManager.getLogger('authentication').info('Found token with deprecated format in storage. Converting it to new format. No further action required.'); + var fakeOldResponse = {}; + fakeOldResponse[config.accessTokenProp] = oldToken; + this.setResponseObject(fakeOldResponse); + authentication.storage.remove(oldStorageKey); } - return this.config.client.post(serverUrl).then(function (response) { - var url = provider.authorizationEndpoint + '?' + buildQueryString(response); + this.setResponseObject(this.authentication.getResponseObject()); + } + + AuthService.prototype.setTimeout = function setTimeout(ttl) { + var _this3 = this; - if (_this4.config.platform === 'mobile') { - _this4.popup = _this4.popup.open(url, provider.name, provider.popupOptions); + this.clearTimeout(); + + this.timeoutID = PLATFORM.global.setTimeout(function () { + if (_this3.config.autoUpdateToken && _this3.authentication.getAccessToken() && _this3.authentication.getRefreshToken()) { + _this3.updateToken(); } else { - _this4.popup.popupWindow.location = url; + _this3.logout(_this3.config.expiredRedirect); } + }, ttl); + }; - var popupListener = _this4.config.platform === 'mobile' ? _this4.popup.eventListener(provider.redirectUri) : _this4.popup.pollPopup(); - - return popupListener.then(function (result) { - return _this4.exchangeForToken(result, userData, provider); - }); - }); + AuthService.prototype.clearTimeout = function clearTimeout() { + if (this.timeoutID) { + PLATFORM.global.clearTimeout(this.timeoutID); + } + this.timeoutID = 0; }; - OAuth1.prototype.exchangeForToken = function exchangeForToken(oauthData, userData, provider) { - var data = extend(true, {}, userData, oauthData); - var serverUrl = this.config.joinBase(provider.url); - var credentials = this.config.withCredentials ? 'include' : 'same-origin'; + AuthService.prototype.setResponseObject = function setResponseObject(response) { + this.clearTimeout(); - return this.config.client.post(serverUrl, data, { credentials: credentials }); + this.authentication.setResponseObject(response); + + this.authenticated = this.authentication.isAuthenticated(); + if (this.authenticated && !Number.isNaN(this.authentication.exp)) { + this.setTimeout(this.getTtl() * 1000); + } }; - return OAuth1; - }()) || _class4)); + AuthService.prototype.getMe = function getMe(criteriaOrId) { + if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { + criteriaOrId = { id: criteriaOrId }; + } + return this.client.find(this.config.joinBase(this.config.profileUrl), criteriaOrId); + }; - _export('OAuth1', OAuth1); + AuthService.prototype.updateMe = function updateMe(body, criteriaOrId) { + if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { + criteriaOrId = { id: criteriaOrId }; + } + if (this.config.profileMethod === 'put') { + return this.client.update(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); + } + return this.client.patch(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); + }; - _export('OAuth2', OAuth2 = (_dec4 = inject(Storage, Popup, BaseConfig), _dec4(_class5 = function () { - function OAuth2(storage, popup, config) { - _classCallCheck(this, OAuth2); + AuthService.prototype.getAccessToken = function getAccessToken() { + return this.authentication.getAccessToken(); + }; - this.storage = storage; - this.config = config; - this.popup = popup; - this.defaults = { - url: null, - name: null, - state: null, - scope: null, - scopeDelimiter: null, - redirectUri: null, - popupOptions: null, - authorizationEndpoint: null, - responseParams: null, - requiredUrlParams: null, - optionalUrlParams: null, - defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], - responseType: 'code' - }; - } + AuthService.prototype.getCurrentToken = function getCurrentToken() { + return this.getAccessToken(); + }; - OAuth2.prototype.open = function open(options, userData) { - var _this5 = this; + AuthService.prototype.getRefreshToken = function getRefreshToken() { + return this.authentication.getRefreshToken(); + }; - var provider = extend(true, {}, this.defaults, options); - var stateName = provider.name + '_state'; + AuthService.prototype.isAuthenticated = function isAuthenticated() { + var authenticated = this.authentication.isAuthenticated(); - if (typeof provider.state === 'function') { - this.storage.set(stateName, provider.state()); - } else if (typeof provider.state === 'string') { - this.storage.set(stateName, provider.state); + if (!authenticated && this.config.autoUpdateToken && this.authentication.getAccessToken() && this.authentication.getRefreshToken()) { + this.updateToken(); + authenticated = true; } - var url = provider.authorizationEndpoint + '?' + buildQueryString(this.buildQuery(provider)); - var popup = this.popup.open(url, provider.name, provider.popupOptions); - var openPopup = this.config.platform === 'mobile' ? popup.eventListener(provider.redirectUri) : popup.pollPopup(); + return authenticated; + }; - return openPopup.then(function (oauthData) { - if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { - return oauthData; - } - if (oauthData.state && oauthData.state !== _this5.storage.get(stateName)) { - return Promise.reject('OAuth 2.0 state parameter mismatch.'); - } - return _this5.exchangeForToken(oauthData, userData, provider); - }); + AuthService.prototype.getExp = function getExp() { + return this.authentication.getExp(); }; - OAuth2.prototype.exchangeForToken = function exchangeForToken(oauthData, userData, provider) { - var data = extend(true, {}, userData, { - clientId: provider.clientId, - redirectUri: provider.redirectUri - }, oauthData); + AuthService.prototype.getTtl = function getTtl() { + return this.authentication.getTtl(); + }; - var serverUrl = this.config.joinBase(provider.url); - var credentials = this.config.withCredentials ? 'include' : 'same-origin'; + AuthService.prototype.isTokenExpired = function isTokenExpired() { + return this.authentication.isTokenExpired(); + }; - return this.config.client.post(serverUrl, data, { credentials: credentials }); + AuthService.prototype.getTokenPayload = function getTokenPayload() { + return this.authentication.getPayload(); }; - OAuth2.prototype.buildQuery = function buildQuery(provider) { - var _this6 = this; + AuthService.prototype.updateToken = function updateToken() { + var _this4 = this; - var query = {}; - var urlParams = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams']; + if (!this.authentication.getRefreshToken()) { + return Promise.reject(new Error('refreshToken not set')); + } - urlParams.forEach(function (params) { - (provider[params] || []).forEach(function (paramName) { - var camelizedName = camelCase(paramName); - var paramValue = typeof provider[paramName] === 'function' ? provider[paramName]() : provider[camelizedName]; + if (this.authentication.updateTokenCallstack.length === 0) { + var content = { + grant_type: 'refresh_token', + refresh_token: this.authentication.getRefreshToken(), + client_id: this.config.clientId ? this.config.clientId : undefined + }; - if (paramName === 'state') { - paramValue = encodeURIComponent(_this6.storage.get(provider.name + '_state')); - } + this.client.post(this.config.joinBase(this.config.refreshTokenUrl ? this.config.refreshTokenUrl : this.config.loginUrl), content).then(function (response) { + _this4.setResponseObject(response); + _this4.authentication.resolveUpdateTokenCallstack(_this4.isAuthenticated()); + }).catch(function (err) { + _this4.setResponseObject(null); + _this4.authentication.resolveUpdateTokenCallstack(Promise.reject(err)); + }); + } - if (paramName === 'scope' && Array.isArray(paramValue)) { - paramValue = paramValue.join(provider.scopeDelimiter); + return this.authentication.toUpdateTokenCallstack(); + }; - if (provider.scopePrefix) { - paramValue = provider.scopePrefix + provider.scopeDelimiter + paramValue; - } - } + AuthService.prototype.signup = function signup(displayNameOrCredentials, emailOrOptions, passwordOrRedirectUri, options, redirectUri) { + var _this5 = this; - query[paramName] = paramValue; - }); - }); - return query; - }; + var content = void 0; - return OAuth2; - }()) || _class5)); + if (_typeof(arguments[0]) === 'object') { + content = arguments[0]; + options = arguments[1]; + redirectUri = arguments[2]; + } else { + content = { + 'displayName': displayNameOrCredentials, + 'email': emailOrOptions, + 'password': passwordOrRedirectUri + }; + } + return this.client.post(this.config.joinBase(this.config.signupUrl), content, options).then(function (response) { + if (_this5.config.loginOnSignup) { + _this5.setResponseObject(response); + } + _this5.authentication.redirect(redirectUri, _this5.config.signupRedirect); - _export('OAuth2', OAuth2); + return response; + }); + }; - camelCase = function camelCase(name) { - return name.replace(/([\:\-\_]+(.))/g, function (_, separator, letter, offset) { - return offset ? letter.toUpperCase() : letter; - }); - }; + AuthService.prototype.login = function login(emailOrCredentials, passwordOrOptions, optionsOrRedirectUri, redirectUri) { + var _this6 = this; - _export('Authentication', Authentication = (_dec5 = inject(Storage, BaseConfig, OAuth1, OAuth2, Auth0Lock), _dec6 = deprecated({ message: 'Use baseConfig.loginRoute instead.' }), _dec7 = deprecated({ message: 'Use baseConfig.loginRedirect instead.' }), _dec8 = deprecated({ message: 'Use baseConfig.joinBase(baseConfig.loginUrl) instead.' }), _dec9 = deprecated({ message: 'Use baseConfig.joinBase(baseConfig.signupUrl) instead.' }), _dec10 = deprecated({ message: 'Use baseConfig.joinBase(baseConfig.profileUrl) instead.' }), _dec11 = deprecated({ message: 'Use .getAccessToken() instead.' }), _dec5(_class6 = (_class7 = function () { - function Authentication(storage, config, oAuth1, oAuth2, auth0Lock) { - _classCallCheck(this, Authentication); + var content = void 0; - this.storage = storage; - this.config = config; - this.oAuth1 = oAuth1; - this.oAuth2 = oAuth2; - this.auth0Lock = auth0Lock; - this.updateTokenCallstack = []; - this.accessToken = null; - this.refreshToken = null; - this.payload = null; - this.exp = null; - this.hasDataStored = false; - } + if (_typeof(arguments[0]) === 'object') { + content = arguments[0]; + optionsOrRedirectUri = arguments[1]; + redirectUri = arguments[2]; + } else { + content = { + 'email': emailOrCredentials, + 'password': passwordOrOptions + }; + optionsOrRedirectUri = optionsOrRedirectUri; + } - Authentication.prototype.getLoginRoute = function getLoginRoute() { - return this.config.loginRoute; - }; + if (this.config.clientId) { + content.client_id = this.config.clientId; + } - Authentication.prototype.getLoginRedirect = function getLoginRedirect() { - return this.config.loginRedirect; - }; + return this.client.post(this.config.joinBase(this.config.loginUrl), content, optionsOrRedirectUri).then(function (response) { + _this6.setResponseObject(response); - Authentication.prototype.getLoginUrl = function getLoginUrl() { - return this.Config.joinBase(this.config.loginUrl); - }; + _this6.authentication.redirect(redirectUri, _this6.config.loginRedirect); - Authentication.prototype.getSignupUrl = function getSignupUrl() { - return this.Config.joinBase(this.config.signupUrl); + return response; + }); }; - Authentication.prototype.getProfileUrl = function getProfileUrl() { - return this.Config.joinBase(this.config.profileUrl); - }; + AuthService.prototype.logout = function logout(redirectUri) { + var _this7 = this; - Authentication.prototype.getToken = function getToken() { - return this.getAccessToken(); - }; + var localLogout = function localLogout(response) { + return new Promise(function (resolve) { + _this7.setResponseObject(null); - Authentication.prototype.getResponseObject = function getResponseObject() { - return JSON.parse(this.storage.get(this.config.storageKey)); - }; + _this7.authentication.redirect(redirectUri, _this7.config.logoutRedirect); - Authentication.prototype.setResponseObject = function setResponseObject(response) { - if (response) { - this.getDataFromResponse(response); - this.storage.set(this.config.storageKey, JSON.stringify(response)); - return; - } - this.accessToken = null; - this.refreshToken = null; - this.payload = null; - this.exp = null; + if (typeof _this7.onLogout === 'function') { + _this7.onLogout(response); + } - this.hasDataStored = false; + resolve(response); + }); + }; - this.storage.remove(this.config.storageKey); + return this.config.logoutUrl ? this.client.request(this.config.logoutMethod, this.config.joinBase(this.config.logoutUrl)).then(localLogout) : localLogout(); }; - Authentication.prototype.getAccessToken = function getAccessToken() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.accessToken; - }; + AuthService.prototype.authenticate = function authenticate(name, redirectUri) { + var _this8 = this; - Authentication.prototype.getRefreshToken = function getRefreshToken() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.refreshToken; - }; + var userData = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - Authentication.prototype.getPayload = function getPayload() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.payload; - }; + return this.authentication.authenticate(name, userData).then(function (response) { + _this8.setResponseObject(response); - Authentication.prototype.getExp = function getExp() { - if (!this.hasDataStored) this.getDataFromResponse(this.getResponseObject()); - return this.exp; - }; + _this8.authentication.redirect(redirectUri, _this8.config.loginRedirect); - Authentication.prototype.getTtl = function getTtl() { - var exp = this.getExp(); - return Number.isNaN(exp) ? NaN : exp - Math.round(new Date().getTime() / 1000); + return response; + }); }; - Authentication.prototype.isTokenExpired = function isTokenExpired() { - var timeLeft = this.getTtl(); - return Number.isNaN(timeLeft) ? undefined : timeLeft < 0; - }; + AuthService.prototype.unlink = function unlink(name, redirectUri) { + var _this9 = this; - Authentication.prototype.isAuthenticated = function isAuthenticated() { - var isTokenExpired = this.isTokenExpired(); - if (isTokenExpired === undefined) return this.accessToken ? true : false; - return !isTokenExpired; + var unlinkUrl = this.config.joinBase(this.config.unlinkUrl) + name; + return this.client.request(this.config.unlinkMethod, unlinkUrl).then(function (response) { + _this9.authentication.redirect(redirectUri); + + return response; + }); }; - Authentication.prototype.getDataFromResponse = function getDataFromResponse(response) { - var config = this.config; + _createClass(AuthService, [{ + key: 'client', + get: function get() { + return this.config.client; + } + }, { + key: 'auth', + get: function get() { + LogManager.getLogger('authentication').warn('AuthService.auth is deprecated. Use .authentication instead.'); + return this.authentication; + } + }]); - this.accessToken = this.getTokenFromResponse(response, config.accessTokenProp, config.accessTokenName, config.accessTokenRoot); + return AuthService; + }(), (_applyDecoratedDescriptor(_class7.prototype, 'getCurrentToken', [_dec12], Object.getOwnPropertyDescriptor(_class7.prototype, 'getCurrentToken'), _class7.prototype)), _class7)) || _class6)); - this.refreshToken = null; - if (config.useRefreshToken) { - try { - this.refreshToken = this.getTokenFromResponse(response, config.refreshTokenProp, config.refreshTokenName, config.refreshTokenRoot); - } catch (e) { - this.refreshToken = null; + _export('AuthService', AuthService); + + _export('BaseConfig', BaseConfig = function () { + function BaseConfig() { + + + this.client = null; + this.endpoint = null; + this.configureEndpoints = null; + this.loginRedirect = '#/'; + this.logoutRedirect = '#/'; + this.loginRoute = '/login'; + this.loginOnSignup = true; + this.signupRedirect = '#/login'; + this.expiredRedirect = 0; + this.baseUrl = ''; + this.loginUrl = '/auth/login'; + this.logoutUrl = null; + this.logoutMethod = 'get'; + this.signupUrl = '/auth/signup'; + this.profileUrl = '/auth/me'; + this.profileMethod = 'put'; + this.unlinkUrl = '/auth/unlink/'; + this.unlinkMethod = 'get'; + this.refreshTokenUrl = null; + this.authHeader = 'Authorization'; + this.authTokenType = 'Bearer'; + this.accessTokenProp = 'access_token'; + this.accessTokenName = 'token'; + this.accessTokenRoot = false; + this.useRefreshToken = false; + this.autoUpdateToken = true; + this.clientId = false; + this.refreshTokenProp = 'refresh_token'; + this.refreshTokenName = 'token'; + this.refreshTokenRoot = false; + this.httpInterceptor = true; + this.withCredentials = true; + this.platform = 'browser'; + this.storage = 'localStorage'; + this.storageKey = 'aurelia_authentication'; + this.globalValueConverters = ['authFilterValueConverter']; + this.providers = { + facebook: { + name: 'facebook', + url: '/auth/facebook', + authorizationEndpoint: 'https://www.facebook.com/v2.5/dialog/oauth', + redirectUri: PLATFORM.location.origin + '/', + requiredUrlParams: ['display', 'scope'], + scope: ['email'], + scopeDelimiter: ',', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 580, height: 400 } + }, + google: { + name: 'google', + url: '/auth/google', + authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['scope'], + optionalUrlParams: ['display', 'state'], + scope: ['profile', 'email'], + scopePrefix: 'openid', + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 452, height: 633 }, + state: randomState + }, + github: { + name: 'github', + url: '/auth/github', + authorizationEndpoint: 'https://github.com/login/oauth/authorize', + redirectUri: PLATFORM.location.origin, + optionalUrlParams: ['scope'], + scope: ['user:email'], + scopeDelimiter: ' ', + oauthType: '2.0', + popupOptions: { width: 1020, height: 618 } + }, + instagram: { + name: 'instagram', + url: '/auth/instagram', + authorizationEndpoint: 'https://api.instagram.com/oauth/authorize', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['scope'], + scope: ['basic'], + scopeDelimiter: '+', + oauthType: '2.0' + }, + linkedin: { + name: 'linkedin', + url: '/auth/linkedin', + authorizationEndpoint: 'https://www.linkedin.com/uas/oauth2/authorization', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['state'], + scope: ['r_emailaddress'], + scopeDelimiter: ' ', + state: 'STATE', + oauthType: '2.0', + popupOptions: { width: 527, height: 582 } + }, + twitter: { + name: 'twitter', + url: '/auth/twitter', + authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate', + redirectUri: PLATFORM.location.origin, + oauthType: '1.0', + popupOptions: { width: 495, height: 645 } + }, + twitch: { + name: 'twitch', + url: '/auth/twitch', + authorizationEndpoint: 'https://api.twitch.tv/kraken/oauth2/authorize', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['scope'], + scope: ['user_read'], + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 500, height: 560 } + }, + live: { + name: 'live', + url: '/auth/live', + authorizationEndpoint: 'https://login.live.com/oauth20_authorize.srf', + redirectUri: PLATFORM.location.origin, + requiredUrlParams: ['display', 'scope'], + scope: ['wl.emails'], + scopeDelimiter: ' ', + display: 'popup', + oauthType: '2.0', + popupOptions: { width: 500, height: 560 } + }, + yahoo: { + name: 'yahoo', + url: '/auth/yahoo', + authorizationEndpoint: 'https://api.login.yahoo.com/oauth2/request_auth', + redirectUri: PLATFORM.location.origin, + scope: [], + scopeDelimiter: ',', + oauthType: '2.0', + popupOptions: { width: 559, height: 519 } + }, + bitbucket: { + name: 'bitbucket', + url: '/auth/bitbucket', + authorizationEndpoint: 'https://bitbucket.org/site/oauth2/authorize', + redirectUri: PLATFORM.location.origin + '/', + requiredUrlParams: ['scope'], + scope: ['email'], + 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: randomState } - } - - this.payload = null; - - try { - this.payload = this.accessToken ? jwtDecode(this.accessToken) : null; - } catch (_) { - _; - } - - this.exp = this.payload ? parseInt(this.payload.exp, 10) : NaN; - - this.hasDataStored = true; - - return { - accessToken: this.accessToken, - refreshToken: this.refreshToken, - payload: this.payload, - exp: this.exp }; - }; - - Authentication.prototype.getTokenFromResponse = function getTokenFromResponse(response, tokenProp, tokenName, tokenRoot) { - if (!response) return undefined; - - var responseTokenProp = tokenProp.split('.').reduce(function (o, x) { - return o[x]; - }, response); - - if (typeof responseTokenProp === 'string') { - return responseTokenProp; - } - - if ((typeof responseTokenProp === 'undefined' ? 'undefined' : _typeof(responseTokenProp)) === 'object') { - var tokenRootData = tokenRoot && tokenRoot.split('.').reduce(function (o, x) { - return o[x]; - }, responseTokenProp); - var _token = tokenRootData ? tokenRootData[tokenName] : responseTokenProp[tokenName]; - - if (!_token) throw new Error('Token not found in response'); - - return _token; - } - - var token = response[tokenName] === undefined ? null : response[tokenName]; - - if (!token) throw new Error('Token not found in response'); - - return token; - }; - - Authentication.prototype.toUpdateTokenCallstack = function toUpdateTokenCallstack() { - var _this7 = this; - - return new Promise(function (resolve) { - return _this7.updateTokenCallstack.push(resolve); - }); - }; + this._authToken = 'Bearer'; + this._responseTokenProp = 'access_token'; + this._tokenName = 'token'; + this._tokenRoot = false; + this._tokenPrefix = 'aurelia'; + } - Authentication.prototype.resolveUpdateTokenCallstack = function resolveUpdateTokenCallstack(response) { - this.updateTokenCallstack.map(function (resolve) { - return resolve(response); - }); - this.updateTokenCallstack = []; + BaseConfig.prototype.joinBase = function joinBase(url) { + return join(this.baseUrl, url); }; - Authentication.prototype.authenticate = function authenticate(name) { - var userData = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - - var oauthType = this.config.providers[name].type; - - if (oauthType) { - LogManager.getLogger('authentication').warn('DEPRECATED: Setting provider.type is deprecated and replaced by provider.oauthType'); - } else { - oauthType = this.config.providers[name].oauthType; - } - - var providerLogin = void 0; - if (oauthType === 'auth0-lock') { - providerLogin = this.auth0Lock; - } else { - providerLogin = oauthType === '1.0' ? this.oAuth1 : this.oAuth2; + BaseConfig.prototype.configure = function configure(incomming) { + for (var key in incomming) { + var value = incomming[key]; + if (value !== undefined) { + if (Array.isArray(value) || (typeof value === 'undefined' ? 'undefined' : _typeof(value)) !== 'object' || value === null) { + this[key] = value; + } else { + extend(true, this[key], value); + } + } } - - return providerLogin.open(this.config.providers[name], userData); }; - Authentication.prototype.redirect = function redirect(redirectUrl, defaultRedirectUrl) { - if (redirectUrl === true) { - LogManager.getLogger('authentication').warn('DEPRECATED: Setting redirectUrl === true to actually *not redirect* is deprecated. Set redirectUrl === 0 instead.'); - return; - } - - if (redirectUrl === false) { - LogManager.getLogger('authentication').warn('BREAKING CHANGE: Setting redirectUrl === false to actually *do redirect* is deprecated. Set redirectUrl to undefined or null to use the defaultRedirectUrl if so desired.'); - } - - if (redirectUrl === 0) { - return; + _createClass(BaseConfig, [{ + key: 'authToken', + set: function set(authToken) { + LogManager.getLogger('authentication').warn('BaseConfig.authToken is deprecated. Use BaseConfig.authTokenType instead.'); + this._authTokenType = authToken; + this.authTokenType = authToken; + return authToken; + }, + get: function get() { + return this._authTokenType; } - if (typeof redirectUrl === 'string') { - PLATFORM.location.href = encodeURI(redirectUrl); - } else if (defaultRedirectUrl) { - PLATFORM.location.href = defaultRedirectUrl; + }, { + key: 'responseTokenProp', + set: function set(responseTokenProp) { + LogManager.getLogger('authentication').warn('BaseConfig.responseTokenProp is deprecated. Use BaseConfig.accessTokenProp instead.'); + this._responseTokenProp = responseTokenProp; + this.accessTokenProp = responseTokenProp; + return responseTokenProp; + }, + get: function get() { + return this._responseTokenProp; } - }; - - _createClass(Authentication, [{ - key: 'responseObject', + }, { + key: 'tokenRoot', + set: function set(tokenRoot) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenRoot is deprecated. Use BaseConfig.accessTokenRoot instead.'); + this._tokenRoot = tokenRoot; + this.accessTokenRoot = tokenRoot; + return tokenRoot; + }, get: function get() { - LogManager.getLogger('authentication').warn('Getter Authentication.responseObject is deprecated. Use Authentication.getResponseObject() instead.'); - return this.getResponseObject(); + return this._tokenRoot; + } + }, { + key: 'tokenName', + set: function set(tokenName) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenName is deprecated. Use BaseConfig.accessTokenName instead.'); + this._tokenName = tokenName; + this.accessTokenName = tokenName; + return tokenName; }, - set: function set(response) { - LogManager.getLogger('authentication').warn('Setter Authentication.responseObject is deprecated. Use AuthServive.setResponseObject(response) instead.'); - this.setResponseObject(response); + get: function get() { + return this._tokenName; } - }]); - - return Authentication; - }(), (_applyDecoratedDescriptor(_class7.prototype, 'getLoginRoute', [_dec6], Object.getOwnPropertyDescriptor(_class7.prototype, 'getLoginRoute'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getLoginRedirect', [_dec7], Object.getOwnPropertyDescriptor(_class7.prototype, 'getLoginRedirect'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getLoginUrl', [_dec8], Object.getOwnPropertyDescriptor(_class7.prototype, 'getLoginUrl'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getSignupUrl', [_dec9], Object.getOwnPropertyDescriptor(_class7.prototype, 'getSignupUrl'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getProfileUrl', [_dec10], Object.getOwnPropertyDescriptor(_class7.prototype, 'getProfileUrl'), _class7.prototype), _applyDecoratedDescriptor(_class7.prototype, 'getToken', [_dec11], Object.getOwnPropertyDescriptor(_class7.prototype, 'getToken'), _class7.prototype)), _class7)) || _class6)); - - _export('Authentication', Authentication); - - _export('AuthService', _export('AuthService', AuthService = (_dec12 = inject(Authentication, BaseConfig), _dec13 = deprecated({ message: 'Use .getAccessToken() instead.' }), _dec12(_class8 = (_class9 = function () { - function AuthService(authentication, config) { - _classCallCheck(this, AuthService); - - this.authenticated = false; - this.timeoutID = 0; - - this.authentication = authentication; - this.config = config; - - var oldStorageKey = config.tokenPrefix ? config.tokenPrefix + '_' + config.tokenName : config.tokenName; - var oldToken = authentication.storage.get(oldStorageKey); - - if (oldToken) { - LogManager.getLogger('authentication').info('Found token with deprecated format in storage. Converting it to new format. No further action required.'); - var fakeOldResponse = {}; - fakeOldResponse[config.accessTokenProp] = oldToken; - this.setResponseObject(fakeOldResponse); - authentication.storage.remove(oldStorageKey); + }, { + key: 'tokenPrefix', + set: function set(tokenPrefix) { + LogManager.getLogger('authentication').warn('BaseConfig.tokenPrefix is obsolete. Use BaseConfig.storageKey instead.'); + this._tokenPrefix = tokenPrefix; + return tokenPrefix; + }, + get: function get() { + return this._tokenPrefix || 'aurelia'; } - - this.setResponseObject(this.authentication.getResponseObject()); - } - - AuthService.prototype.setTimeout = function setTimeout(ttl) { - var _this8 = this; - - this.clearTimeout(); - - this.timeoutID = PLATFORM.global.setTimeout(function () { - if (_this8.config.autoUpdateToken && _this8.authentication.getAccessToken() && _this8.authentication.getRefreshToken()) { - _this8.updateToken(); - } else { - _this8.logout(_this8.config.expiredRedirect); - } - }, ttl); - }; - - AuthService.prototype.clearTimeout = function clearTimeout() { - if (this.timeoutID) { - PLATFORM.global.clearTimeout(this.timeoutID); + }, { + key: 'current', + get: function get() { + LogManager.getLogger('authentication').warn('Getter BaseConfig.current is deprecated. Use BaseConfig directly instead.'); + return this; + }, + set: function set(_) { + throw new Error('Setter BaseConfig.current is obsolete. Use BaseConfig directly instead.'); } - this.timeoutID = 0; - }; - - AuthService.prototype.setResponseObject = function setResponseObject(response) { - this.clearTimeout(); - - this.authentication.setResponseObject(response); - - this.authenticated = this.authentication.isAuthenticated(); - if (this.authenticated && !Number.isNaN(this.authentication.exp)) { - this.setTimeout(this.getTtl() * 1000); + }, { + key: '_current', + get: function get() { + LogManager.getLogger('authentication').warn('Getter BaseConfig._current is deprecated. Use BaseConfig directly instead.'); + return this; + }, + set: function set(_) { + throw new Error('Setter BaseConfig._current is obsolete. Use BaseConfig directly instead.'); } - }; + }]); - AuthService.prototype.getMe = function getMe(criteriaOrId) { - if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { - criteriaOrId = { id: criteriaOrId }; - } - return this.client.find(this.config.joinBase(this.config.profileUrl), criteriaOrId); - }; + return BaseConfig; + }()); - AuthService.prototype.updateMe = function updateMe(body, criteriaOrId) { - if (typeof criteriaOrId === 'string' || typeof criteriaOrId === 'number') { - criteriaOrId = { id: criteriaOrId }; - } - if (this.config.profileMethod === 'put') { - return this.client.update(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); - } - return this.client.patch(this.config.joinBase(this.config.profileUrl), criteriaOrId, body); - }; + _export('BaseConfig', BaseConfig); - AuthService.prototype.getAccessToken = function getAccessToken() { - return this.authentication.getAccessToken(); - }; + _export('FetchConfig', FetchConfig = (_dec13 = inject(HttpClient, Config, AuthService, BaseConfig), _dec13(_class10 = function () { + function FetchConfig(httpClient, clientConfig, authService, config) { + - AuthService.prototype.getCurrentToken = function getCurrentToken() { - return this.getAccessToken(); - }; + this.httpClient = httpClient; + this.clientConfig = clientConfig; + this.authService = authService; + this.config = config; + } - AuthService.prototype.getRefreshToken = function getRefreshToken() { - return this.authentication.getRefreshToken(); - }; + FetchConfig.prototype.configure = function configure(client) { + var _this10 = this; - AuthService.prototype.isAuthenticated = function isAuthenticated() { - var authenticated = this.authentication.isAuthenticated(); + if (Array.isArray(client)) { + var _ret = function () { + var configuredClients = []; + client.forEach(function (toConfigure) { + configuredClients.push(_this10.configure(toConfigure)); + }); - if (!authenticated && this.config.autoUpdateToken && this.authentication.getAccessToken() && this.authentication.getRefreshToken()) { - this.updateToken(); - authenticated = true; - } + return { + v: configuredClients + }; + }(); - return authenticated; - }; + if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; + } - AuthService.prototype.getExp = function getExp() { - return this.authentication.getExp(); - }; + if (typeof client === 'string') { + var endpoint = this.clientConfig.getEndpoint(client); + if (!endpoint) { + throw new Error('There is no \'' + (client || 'default') + '\' endpoint registered.'); + } + client = endpoint.client; + } else if (client instanceof Rest) { + client = client.client; + } else if (!(client instanceof HttpClient)) { + client = this.httpClient; + } - AuthService.prototype.getTtl = function getTtl() { - return this.authentication.getTtl(); - }; + client.interceptors.push(this.interceptor); - AuthService.prototype.isTokenExpired = function isTokenExpired() { - return this.authentication.isTokenExpired(); + return client; }; - AuthService.prototype.getTokenPayload = function getTokenPayload() { - return this.authentication.getPayload(); - }; + _createClass(FetchConfig, [{ + key: 'interceptor', + get: function get() { + var _this11 = this; - AuthService.prototype.updateToken = function updateToken() { - var _this9 = this; + return { + request: function request(_request) { + if (!_this11.config.httpInterceptor || !_this11.authService.isAuthenticated()) { + return _request; + } + var token = _this11.authService.getAccessToken(); - if (!this.authentication.getRefreshToken()) { - return Promise.reject(new Error('refreshToken not set')); - } + if (_this11.config.authTokenType) { + token = _this11.config.authTokenType + ' ' + token; + } - if (this.authentication.updateTokenCallstack.length === 0) { - var content = { - grant_type: 'refresh_token', - refresh_token: this.authentication.getRefreshToken(), - client_id: this.config.clientId ? this.config.clientId : undefined - }; + _request.headers.set(_this11.config.authHeader, token); - this.client.post(this.config.joinBase(this.config.refreshTokenUrl ? this.config.refreshTokenUrl : this.config.loginUrl), content).then(function (response) { - _this9.setResponseObject(response); - _this9.authentication.resolveUpdateTokenCallstack(_this9.isAuthenticated()); - }).catch(function (err) { - _this9.setResponseObject(null); - _this9.authentication.resolveUpdateTokenCallstack(Promise.reject(err)); - }); - } + return _request; + }, + response: function response(_response, request) { + return new Promise(function (resolve, reject) { + if (_response.ok) { + return resolve(_response); + } + if (_response.status !== 401) { + return resolve(_response); + } + if (!_this11.config.httpInterceptor || !_this11.authService.isTokenExpired()) { + return resolve(_response); + } + if (!_this11.config.useRefreshToken || !_this11.authService.getRefreshToken()) { + return resolve(_response); + } - return this.authentication.toUpdateTokenCallstack(); - }; + return _this11.authService.updateToken().then(function () { + var token = _this11.authService.getAccessToken(); - AuthService.prototype.signup = function signup(displayNameOrCredentials, emailOrOptions, passwordOrRedirectUri, options, redirectUri) { - var _this10 = this; + if (_this11.config.authTokenType) { + token = _this11.config.authTokenType + ' ' + token; + } - var content = void 0; + request.headers.set(_this11.config.authHeader, token); - if (_typeof(arguments[0]) === 'object') { - content = arguments[0]; - options = arguments[1]; - redirectUri = arguments[2]; - } else { - content = { - 'displayName': displayNameOrCredentials, - 'email': emailOrOptions, - 'password': passwordOrRedirectUri + return _this11.client.fetch(request).then(resolve); + }); + }); + } }; } - return this.client.post(this.config.joinBase(this.config.signupUrl), content, options).then(function (response) { - if (_this10.config.loginOnSignup) { - _this10.setResponseObject(response); - } - _this10.authentication.redirect(redirectUri, _this10.config.signupRedirect); + }]); - return response; - }); - }; + return FetchConfig; + }()) || _class10)); - AuthService.prototype.login = function login(emailOrCredentials, passwordOrOptions, optionsOrRedirectUri, redirectUri) { - var _this11 = this; + _export('FetchConfig', FetchConfig); - var content = void 0; + _export('OAuth1', OAuth1 = (_dec14 = inject(Storage, Popup, BaseConfig), _dec14(_class11 = function () { + function OAuth1(storage, popup, config) { + - if (_typeof(arguments[0]) === 'object') { - content = arguments[0]; - optionsOrRedirectUri = arguments[1]; - redirectUri = arguments[2]; - } else { - content = { - 'email': emailOrCredentials, - 'password': passwordOrOptions - }; - optionsOrRedirectUri = optionsOrRedirectUri; - } + this.storage = storage; + this.config = config; + this.popup = popup; + this.defaults = { + url: null, + name: null, + popupOptions: null, + redirectUri: null, + authorizationEndpoint: null + }; + } - if (this.config.clientId) { - content.client_id = this.config.clientId; + OAuth1.prototype.open = function open(options, userData) { + var _this12 = this; + + var provider = extend(true, {}, this.defaults, options); + var serverUrl = this.config.joinBase(provider.url); + + if (this.config.platform !== 'mobile') { + this.popup = this.popup.open('', provider.name, provider.popupOptions); } - return this.client.post(this.config.joinBase(this.config.loginUrl), content, optionsOrRedirectUri).then(function (response) { - _this11.setResponseObject(response); + return this.config.client.post(serverUrl).then(function (response) { + var url = provider.authorizationEndpoint + '?' + buildQueryString(response); + + if (_this12.config.platform === 'mobile') { + _this12.popup = _this12.popup.open(url, provider.name, provider.popupOptions); + } else { + _this12.popup.popupWindow.location = url; + } - _this11.authentication.redirect(redirectUri, _this11.config.loginRedirect); + var popupListener = _this12.config.platform === 'mobile' ? _this12.popup.eventListener(provider.redirectUri) : _this12.popup.pollPopup(); - return response; + return popupListener.then(function (result) { + return _this12.exchangeForToken(result, userData, provider); + }); }); }; - AuthService.prototype.logout = function logout(redirectUri) { - var _this12 = this; + OAuth1.prototype.exchangeForToken = function exchangeForToken(oauthData, userData, provider) { + var data = extend(true, {}, userData, oauthData); + var serverUrl = this.config.joinBase(provider.url); + var credentials = this.config.withCredentials ? 'include' : 'same-origin'; - var localLogout = function localLogout(response) { - return new Promise(function (resolve) { - _this12.setResponseObject(null); + return this.config.client.post(serverUrl, data, { credentials: credentials }); + }; - _this12.authentication.redirect(redirectUri, _this12.config.logoutRedirect); + return OAuth1; + }()) || _class11)); - if (typeof _this12.onLogout === 'function') { - _this12.onLogout(response); - } + _export('OAuth1', OAuth1); - resolve(response); - }); - }; + _export('OAuth2', OAuth2 = (_dec15 = inject(Storage, Popup, BaseConfig), _dec15(_class12 = function () { + function OAuth2(storage, popup, config) { + - return this.config.logoutUrl ? this.client.request(this.config.logoutMethod, this.config.joinBase(this.config.logoutUrl)).then(localLogout) : localLogout(); - }; + this.storage = storage; + this.config = config; + this.popup = popup; + this.defaults = { + url: null, + name: null, + state: null, + scope: null, + scopeDelimiter: null, + redirectUri: null, + popupOptions: null, + authorizationEndpoint: null, + responseParams: null, + requiredUrlParams: null, + optionalUrlParams: null, + defaultUrlParams: ['response_type', 'client_id', 'redirect_uri'], + responseType: 'code' + }; + } - AuthService.prototype.authenticate = function authenticate(name, redirectUri) { + OAuth2.prototype.open = function open(options, userData) { var _this13 = this; - var userData = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - - return this.authentication.authenticate(name, userData).then(function (response) { - _this13.setResponseObject(response); - - _this13.authentication.redirect(redirectUri, _this13.config.loginRedirect); - - return response; - }); - }; + var provider = extend(true, {}, this.defaults, options); + var stateName = provider.name + '_state'; - AuthService.prototype.unlink = function unlink(name, redirectUri) { - var _this14 = this; + if (typeof provider.state === 'function') { + this.storage.set(stateName, provider.state()); + } else if (typeof provider.state === 'string') { + this.storage.set(stateName, provider.state); + } - var unlinkUrl = this.config.joinBase(this.config.unlinkUrl) + name; - return this.client.request(this.config.unlinkMethod, unlinkUrl).then(function (response) { - _this14.authentication.redirect(redirectUri); + var url = provider.authorizationEndpoint + '?' + buildQueryString(this.buildQuery(provider)); + var popup = this.popup.open(url, provider.name, provider.popupOptions); + var openPopup = this.config.platform === 'mobile' ? popup.eventListener(provider.redirectUri) : popup.pollPopup(); - return response; + return openPopup.then(function (oauthData) { + if (provider.responseType === 'token' || provider.responseType === 'id_token%20token' || provider.responseType === 'token%20id_token') { + return oauthData; + } + if (oauthData.state && oauthData.state !== _this13.storage.get(stateName)) { + return Promise.reject('OAuth 2.0 state parameter mismatch.'); + } + return _this13.exchangeForToken(oauthData, userData, provider); }); }; - _createClass(AuthService, [{ - key: 'client', - get: function get() { - return this.config.client; - } - }, { - key: 'auth', - get: function get() { - LogManager.getLogger('authentication').warn('AuthService.auth is deprecated. Use .authentication instead.'); - return this.authentication; - } - }]); + OAuth2.prototype.exchangeForToken = function exchangeForToken(oauthData, userData, provider) { + var data = extend(true, {}, userData, { + clientId: provider.clientId, + redirectUri: provider.redirectUri + }, oauthData); - return AuthService; - }(), (_applyDecoratedDescriptor(_class9.prototype, 'getCurrentToken', [_dec13], Object.getOwnPropertyDescriptor(_class9.prototype, 'getCurrentToken'), _class9.prototype)), _class9)) || _class8))); + var serverUrl = this.config.joinBase(provider.url); + var credentials = this.config.withCredentials ? 'include' : 'same-origin'; - _export('AuthService', AuthService); + return this.config.client.post(serverUrl, data, { credentials: credentials }); + }; - _export('AuthenticateStep', _export('AuthenticateStep', AuthenticateStep = (_dec14 = inject(AuthService), _dec14(_class11 = function () { - function AuthenticateStep(authService) { - _classCallCheck(this, AuthenticateStep); + OAuth2.prototype.buildQuery = function buildQuery(provider) { + var _this14 = this; - this.authService = authService; - } + var query = {}; + var urlParams = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams']; - AuthenticateStep.prototype.run = function run(routingContext, next) { - var isLoggedIn = this.authService.authenticated; - var loginRoute = this.authService.config.loginRoute; + urlParams.forEach(function (params) { + (provider[params] || []).forEach(function (paramName) { + var camelizedName = camelCase(paramName); + var paramValue = typeof provider[paramName] === 'function' ? provider[paramName]() : provider[camelizedName]; - if (routingContext.getAllInstructions().some(function (route) { - return route.config.auth === true; - })) { - if (!isLoggedIn) { - return next.cancel(new Redirect(loginRoute)); - } - } else if (isLoggedIn && routingContext.getAllInstructions().some(function (route) { - return route.fragment === loginRoute; - })) { - return next.cancel(new Redirect(this.authService.config.loginRedirect)); - } + if (paramName === 'state') { + paramValue = encodeURIComponent(_this14.storage.get(provider.name + '_state')); + } - return next(); + if (paramName === 'scope' && Array.isArray(paramValue)) { + paramValue = paramValue.join(provider.scopeDelimiter); + + if (provider.scopePrefix) { + paramValue = provider.scopePrefix + provider.scopeDelimiter + paramValue; + } + } + + query[paramName] = paramValue; + }); + }); + return query; }; - return AuthenticateStep; - }()) || _class11))); + return OAuth2; + }()) || _class12)); - _export('AuthenticateStep', AuthenticateStep); + _export('OAuth2', OAuth2); - _export('AuthorizeStep', _export('AuthorizeStep', AuthorizeStep = (_dec15 = inject(AuthService), _dec15(_class12 = function () { - function AuthorizeStep(authService) { - _classCallCheck(this, AuthorizeStep); + camelCase = function camelCase(name) { + return name.replace(/([\:\-\_]+(.))/g, function (_, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }); + }; - LogManager.getLogger('authentication').warn('AuthorizeStep is deprecated. Use AuthenticationStep instead.'); + _export('Popup', Popup = function () { + function Popup() { + - this.authService = authService; + this.popupWindow = null; + this.polling = null; + this.url = ''; } - AuthorizeStep.prototype.run = function run(routingContext, next) { - var isLoggedIn = this.authService.isAuthenticated(); - var loginRoute = this.authService.config.loginRoute; + Popup.prototype.open = function open(url, windowName, options) { + this.url = url; + var optionsString = buildPopupWindowOptions(options || {}); - if (routingContext.getAllInstructions().some(function (route) { - return route.config.auth; - })) { - if (!isLoggedIn) { - return next.cancel(new Redirect(loginRoute)); - } - } else if (isLoggedIn && routingContext.getAllInstructions().some(function (route) { - return route.fragment === loginRoute; - })) { - return next.cancel(new Redirect(this.authService.config.loginRedirect)); + this.popupWindow = PLATFORM.global.open(url, windowName, optionsString); + + if (this.popupWindow && this.popupWindow.focus) { + this.popupWindow.focus(); } - return next(); + return this; }; - return AuthorizeStep; - }()) || _class12))); + Popup.prototype.eventListener = function eventListener(redirectUri) { + var _this15 = this; - _export('AuthorizeStep', AuthorizeStep); + return new Promise(function (resolve, reject) { + _this15.popupWindow.addEventListener('loadstart', function (event) { + if (event.url.indexOf(redirectUri) !== 0) { + return; + } - _export('FetchConfig', _export('FetchConfig', FetchConfig = (_dec16 = inject(HttpClient, Config, AuthService, BaseConfig), _dec16(_class13 = function () { - function FetchConfig(httpClient, clientConfig, authService, config) { - _classCallCheck(this, FetchConfig); + var parser = DOM.createElement('a'); + parser.href = event.url; - this.httpClient = httpClient; - this.clientConfig = clientConfig; - this.authService = authService; - this.config = config; - } + if (parser.search || parser.hash) { + var qs = parseUrl(parser); - FetchConfig.prototype.configure = function configure(client) { - var _this15 = this; + if (qs.error) { + reject({ error: qs.error }); + } else { + resolve(qs); + } - if (Array.isArray(client)) { - var _ret = function () { - var configuredClients = []; - client.forEach(function (toConfigure) { - configuredClients.push(_this15.configure(toConfigure)); - }); + _this15.popupWindow.close(); + } + }); - return { - v: configuredClients - }; - }(); + _this15.popupWindow.addEventListener('exit', function () { + reject({ data: 'Provider Popup was closed' }); + }); - if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; - } + _this15.popupWindow.addEventListener('loaderror', function () { + reject({ data: 'Authorization Failed' }); + }); + }); + }; - if (typeof client === 'string') { - var endpoint = this.clientConfig.getEndpoint(client); - if (!endpoint) { - throw new Error('There is no \'' + (client || 'default') + '\' endpoint registered.'); - } - client = endpoint.client; - } else if (client instanceof Rest) { - client = client.client; - } else if (!(client instanceof HttpClient)) { - client = this.httpClient; - } + Popup.prototype.pollPopup = function pollPopup() { + var _this16 = this; - client.interceptors.push(this.interceptor); + return new Promise(function (resolve, reject) { + _this16.polling = PLATFORM.global.setInterval(function () { + var errorData = void 0; - return client; - }; + try { + if (_this16.popupWindow.location.host === PLATFORM.global.document.location.host && (_this16.popupWindow.location.search || _this16.popupWindow.location.hash)) { + var qs = parseUrl(_this16.popupWindow.location); - _createClass(FetchConfig, [{ - key: 'interceptor', - get: function get() { - var _this16 = this; + if (qs.error) { + reject({ error: qs.error }); + } else { + resolve(qs); + } - return { - request: function request(_request) { - if (!_this16.config.httpInterceptor || !_this16.authService.isAuthenticated()) { - return _request; + _this16.popupWindow.close(); + PLATFORM.global.clearInterval(_this16.polling); } - var token = _this16.authService.getAccessToken(); + } catch (error) { + errorData = error; + } - if (_this16.config.authTokenType) { - token = _this16.config.authTokenType + ' ' + token; - } + if (!_this16.popupWindow) { + PLATFORM.global.clearInterval(_this16.polling); + reject({ + error: errorData, + data: 'Provider Popup Blocked' + }); + } else if (_this16.popupWindow.closed) { + PLATFORM.global.clearInterval(_this16.polling); + reject({ + error: errorData, + data: 'Problem poll popup' + }); + } + }, 35); + }); + }; - _request.headers.set(_this16.config.authHeader, token); + return Popup; + }()); - return _request; - }, - response: function response(_response, request) { - return new Promise(function (resolve, reject) { - if (_response.ok) { - return resolve(_response); - } - if (_response.status !== 401) { - return resolve(_response); - } - if (!_this16.config.httpInterceptor || !_this16.authService.isTokenExpired()) { - return resolve(_response); - } - if (!_this16.config.useRefreshToken || !_this16.authService.getRefreshToken()) { - return resolve(_response); - } + _export('Popup', Popup); - _this16.authService.updateToken().then(function () { - var token = _this16.authService.getAccessToken(); + buildPopupWindowOptions = function buildPopupWindowOptions(options) { + var width = options.width || 500; + var height = options.height || 500; - if (_this16.config.authTokenType) { - token = _this16.config.authTokenType + ' ' + token; - } + var extended = extend({ + width: width, + height: height, + left: PLATFORM.global.screenX + (PLATFORM.global.outerWidth - width) / 2, + top: PLATFORM.global.screenY + (PLATFORM.global.outerHeight - height) / 2.5 + }, options); - request.headers.set(_this16.config.authHeader, token); + var parts = []; + Object.keys(extended).map(function (key) { + return parts.push(key + '=' + extended[key]); + }); - return _this16.client.fetch(request).then(resolve); - }); - }); - } - }; - } - }]); + return parts.join(','); + }; - return FetchConfig; - }()) || _class13))); + parseUrl = function parseUrl(url) { + return extend(true, {}, parseQueryString(url.search), parseQueryString(url.hash)); + }; - _export('FetchConfig', FetchConfig); + _export('Storage', Storage = (_dec16 = inject(BaseConfig), _dec16(_class13 = function () { + function Storage(config) { + - _export('configure', configure); + this.config = config; + } - _export('FetchConfig', FetchConfig); + Storage.prototype.get = function get(key) { + return PLATFORM.global[this.config.storage].getItem(key); + }; - _export('AuthService', AuthService); + Storage.prototype.set = function set(key, value) { + PLATFORM.global[this.config.storage].setItem(key, value); + }; - _export('AuthorizeStep', AuthorizeStep); + Storage.prototype.remove = function remove(key) { + PLATFORM.global[this.config.storage].removeItem(key); + }; - _export('AuthenticateStep', AuthenticateStep); + return Storage; + }()) || _class13)); + + _export('Storage', Storage); } }; }); \ No newline at end of file diff --git a/dist/system/authFilterValueConverter.js b/dist/system/authFilterValueConverter.js index cfe639c..4861e37 100644 --- a/dist/system/authFilterValueConverter.js +++ b/dist/system/authFilterValueConverter.js @@ -1,20 +1,18 @@ 'use strict'; System.register([], function (_export, _context) { + "use strict"; + var AuthFilterValueConverter; - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } + return { setters: [], execute: function () { _export('AuthFilterValueConverter', AuthFilterValueConverter = function () { function AuthFilterValueConverter() { - _classCallCheck(this, AuthFilterValueConverter); + } AuthFilterValueConverter.prototype.toView = function toView(routes, isAuthenticated) { diff --git a/dist/system/authenticatedFilterValueConverter.js b/dist/system/authenticatedFilterValueConverter.js index d95021b..595531e 100644 --- a/dist/system/authenticatedFilterValueConverter.js +++ b/dist/system/authenticatedFilterValueConverter.js @@ -1,13 +1,11 @@ 'use strict'; System.register(['aurelia-dependency-injection', './aurelia-authentication'], function (_export, _context) { + "use strict"; + var inject, AuthService, _dec, _class, AuthenticatedFilterValueConverter; - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } + return { setters: [function (_aureliaDependencyInjection) { @@ -18,7 +16,7 @@ System.register(['aurelia-dependency-injection', './aurelia-authentication'], fu execute: function () { _export('AuthenticatedFilterValueConverter', AuthenticatedFilterValueConverter = (_dec = inject(AuthService), _dec(_class = function () { function AuthenticatedFilterValueConverter(authService) { - _classCallCheck(this, AuthenticatedFilterValueConverter); + this.authService = authService; } diff --git a/dist/system/authenticatedValueConverter.js b/dist/system/authenticatedValueConverter.js index 2e06802..aff7ab8 100644 --- a/dist/system/authenticatedValueConverter.js +++ b/dist/system/authenticatedValueConverter.js @@ -1,13 +1,11 @@ 'use strict'; System.register(['aurelia-dependency-injection', './aurelia-authentication'], function (_export, _context) { + "use strict"; + var inject, AuthService, _dec, _class, AuthenticatedValueConverter; - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } + return { setters: [function (_aureliaDependencyInjection) { @@ -18,7 +16,7 @@ System.register(['aurelia-dependency-injection', './aurelia-authentication'], fu execute: function () { _export('AuthenticatedValueConverter', AuthenticatedValueConverter = (_dec = inject(AuthService), _dec(_class = function () { function AuthenticatedValueConverter(authService) { - _classCallCheck(this, AuthenticatedValueConverter); + this.authService = authService; } diff --git a/dist/system/index.js b/dist/system/index.js new file mode 100644 index 0000000..4a78904 --- /dev/null +++ b/dist/system/index.js @@ -0,0 +1,18 @@ +'use strict'; + +System.register(['./aurelia-authentication'], function (_export, _context) { + "use strict"; + + return { + setters: [function (_aureliaAuthentication) { + var _exportObj = {}; + + for (var _key in _aureliaAuthentication) { + if (_key !== "default") _exportObj[_key] = _aureliaAuthentication[_key]; + } + + _export(_exportObj); + }], + execute: function () {} + }; +}); \ No newline at end of file diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 623d787..3af05ee 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,5 +1,9 @@ + +## [3.0.0-rc6](https://github.com/spoonx/aurelia-authentication/compare/3.0.0-rc5...v3.0.0-rc6) (2016-07-04) + + -### 3.0.1 (2016-06-15) +### 3.0.0-rc5 (2016-06-15) #### Bug Fixes diff --git a/doc/installation.md b/doc/installation.md index 6f24cc6..bb1eb89 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -1,19 +1,70 @@ # Installation -## Jspm/SystemJs +## Aureli-Cli -Run `jspm i aurelia-authentication` from your project root. +Run `npm i aurelia-authentication --save` from your project root. + +Add `aurelia-authentication` to the `build/bundles/dependencies` section of `aurelia-project/aurelia.json`. + +Aurelia-authentication has submodules (currently only the authFilter). You need to add it to the aurelia build resources in your package.json. + +```js +"aurelia": { + "build": { + "resources": ["aurelia-authentication/authFilterValueConverter"] + } +}, +``` + +## Jspm + +Run `jspm i aurelia-authentication` + +If the installation results in having forks, try resolving them by running: + +```sh +jspm inspect --forks +jspm resolve --only registry:package-name@version +``` + +E.g. + +```sh +jspm inspect --forks +> Installed Forks +> npm:aurelia-dependency-injection 1.0.0-beta.1.2.3 1.0.0-beta.2.1.0 + +jspm resolve --only npm:aurelia-dependency-injection@1.0.0-beta.2.1.0 +``` ## Webpack Run `npm i aurelia-authentication --save` from your project root. -Aurelia-authentication has submodules (currently only the `authFilterValueConverter`). So you need to add it to the AureliaWebpackPlugin includeSubModules list. +Add `'aurelia-authentication'` in the `coreBundles.aurelia section` of your `webpack.config.js`. + +Aurelia-authentication has submodules (currently only the authFilter). You need to add it to the aurelia build resources in your package.json. + +```js +"aurelia": { + "build": { + "resources": ["aurelia-authentication/authFilterValueConverter"] + } +}, +``` + +## Typescript + +Add to your `typings.json` ```js -AureliaWebpackPlugin({ - includeSubModules: [ - { moduleId: 'aurelia-authentication' } - ] - }), +"aurelia-authentication": "github:spoonx/aurelia-authentication", +``` + +and run `typings i` + +or run + +```sh +typings i github:spoonx/aurelia-authentication ``` diff --git a/gulpfile.js b/gulpfile.js index 533e14f..4b28ec1 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,3 +1,3 @@ // all gulp tasks are located in the ./build/tasks directory // gulp configuration is in files in ./build directory -require('require-dir')('build/tasks'); \ No newline at end of file +require('require-dir')('build/tasks'); diff --git a/package.json b/package.json index be1b6e5..0251c30 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aurelia-authentication", - "version": "3.0.0-rc5", + "version": "3.0.0-rc6", "description": "Plugin for social media authentication and local authentication together with other authentication utilities.", "keywords": [ "aurelia", @@ -80,46 +80,57 @@ "jwt-decode": "^2.0.1" }, "devDependencies": { - "aurelia-tools": "^0.1.20", - "babel-dts-generator": "^0.5.0", - "babel-eslint": "^4.1.3", - "babel-plugin-syntax-flow": "^6.5.0", + "aurelia-tools": "^0.2.3", + "babel-dts-generator": "^0.6.0", + "babel-eslint": "^6.0.5", + "babel-plugin-syntax-flow": "^6.8.0", "babel-plugin-transform-decorators-legacy": "^1.3.4", - "babel-plugin-transform-es2015-modules-amd": "^6.6.5", - "babel-plugin-transform-es2015-modules-commonjs": "^6.7.0", - "babel-plugin-transform-es2015-modules-systemjs": "^6.6.5", - "babel-plugin-transform-flow-strip-types": "^6.7.0", - "babel-preset-es2015": "^6.6.0", + "babel-plugin-transform-es2015-modules-amd": "^6.8.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.10.3", + "babel-plugin-transform-es2015-modules-systemjs": "^6.9.0", + "babel-plugin-transform-flow-strip-types": "^6.8.0", + "babel-preset-es2015": "^6.9.0", "babel-preset-es2015-loose": "^7.0.0", + "babel-preset-es2015-loose-native-modules": "^1.0.0", "babel-preset-stage-1": "^6.5.0", "body-parser": "^1.14.2", - "conventional-changelog": "0.0.17", "cors": "^2.7.1", - "del": "^2.2.0", + "del": "^2.2.1", "express": "^4.13.3", - "gulp": "^3.8.10", + "gulp": "^3.9.1", "gulp-babel": "^6.1.2", - "gulp-bump": "^2.0.1", + "gulp-bump": "^2.1.0", + "gulp-clean": "^0.3.2", "gulp-concat": "^2.6.0", - "gulp-eslint": "^1.1.1", + "gulp-conventional-changelog": "^1.1.0", + "gulp-empty": "^0.1.1", + "gulp-eslint": "^2.0.0", + "gulp-ignore": "^2.0.1", "gulp-insert": "^0.5.0", "gulp-rename": "^1.2.2", - "gulp-typedoc": "^1.2.1", - "gulp-typedoc-extractor": "0.0.8", - "jasmine-core": "^2.1.3", - "karma": "^0.13.15", + "gulp-typedoc": "^2.0.0", + "gulp-typedoc-extractor": "^0.0.8", + "gulp-typescript": "^2.13.6", + "gulp-util": "^3.0.7", + "jasmine-core": "^2.4.1", + "karma": "^0.13.22", "karma-babel-preprocessor": "^6.0.1", - "karma-chrome-launcher": "^0.2.2", - "karma-coverage": "^0.5.3", + "karma-chrome-launcher": "^1.0.1", + "karma-coverage": "^1.0.0", "karma-firefox-launcher": "^0.1.7", - "karma-jasmine": "^0.3.6", - "karma-jspm": "^2.0.2", - "object.assign": "^1.0.3", - "require-dir": "^0.1.0", - "run-sequence": "^1.0.2", + "karma-ie-launcher": "^1.0.0", + "karma-jasmine": "^1.0.2", + "karma-jspm": "^2.1.1", + "merge2": "^1.0.2", + "object.assign": "^4.0.3", + "require-dir": "^0.3.0", + "run-sequence": "^1.2.1", "snyk": "^1.8.4", - "through2": "^2.0.0", + "through2": "^2.0.1", + "typedoc": "^0.4.4", + "typescript": "^1.9.0-dev.20160622-1.0", + "vinyl": "^1.1.1", "vinyl-paths": "^2.1.0", - "yargs": "^4.3.2" + "yargs": "^4.7.1" } } diff --git a/src/aurelia-authentication.js b/src/aurelia-authentication.js index ed7397f..07fb976 100644 --- a/src/aurelia-authentication.js +++ b/src/aurelia-authentication.js @@ -1,9 +1,6 @@ import {PLATFORM} from 'aurelia-pal'; import {HttpClient} from 'aurelia-fetch-client'; import {Config, Rest} from 'aurelia-api'; -import {AuthService} from './authService'; -import {AuthorizeStep} from './authorizeStep'; -import {AuthenticateStep} from './authenticateStep'; import {BaseConfig} from './baseConfig'; import {FetchConfig} from './fetchClientConfig'; import * as LogManager from 'aurelia-logging'; @@ -16,7 +13,7 @@ import './authFilterValueConverter'; * @param {{globalResources: Function, container: {Container}}} aurelia * @param {{}|Function} config */ -function configure(aurelia, config) { +export function configure(aurelia, config) { // ie9 polyfill if (!PLATFORM.location.origin) { PLATFORM.location.origin = PLATFORM.location.protocol + '//' + PLATFORM.location.hostname + (PLATFORM.location.port ? ':' + PLATFORM.location.port : ''); @@ -68,11 +65,3 @@ function configure(aurelia, config) { // Set the client on the config, for use throughout the plugin. baseConfig.client = client; } - -export { - configure, - FetchConfig, - AuthService, - AuthorizeStep, - AuthenticateStep -}; diff --git a/src/fetchClientConfig.js b/src/fetchClientConfig.js index 17799f0..1df4159 100644 --- a/src/fetchClientConfig.js +++ b/src/fetchClientConfig.js @@ -57,7 +57,7 @@ export class FetchConfig { return resolve(response); } - this.authService.updateToken().then(() => { + return this.authService.updateToken().then(() => { let token = this.authService.getAccessToken(); if (this.config.authTokenType) { diff --git a/test/aurelia-authentication.spec.js b/test/aurelia-authentication.spec.js index 2196a55..7eb7e26 100644 --- a/test/aurelia-authentication.spec.js +++ b/test/aurelia-authentication.spec.js @@ -2,13 +2,11 @@ import {Container} from 'aurelia-dependency-injection'; import {Config, Rest} from 'aurelia-api'; import {HttpClient} from 'aurelia-fetch-client'; -import { - configure, - FetchConfig, - AuthService, - AuthorizeStep, - AuthenticateStep -} from '../src/aurelia-authentication'; +import {configure} from '../src/aurelia-authentication'; +import {AuthService} from '../src/authService'; +import {AuthorizeStep} from '../src/authorizeStep'; +import {AuthenticateStep} from '../src/authenticateStep'; +import {FetchConfig} from '../src/fetchClientConfig'; import {BaseConfig} from '../src/baseConfig'; let noop = () => {}; diff --git a/test/authService.spec.js b/test/authService.spec.js index 61366d7..e3ba87b 100644 --- a/test/authService.spec.js +++ b/test/authService.spec.js @@ -5,7 +5,7 @@ import {SignalBindingBehavior, BindingSignaler} from 'aurelia-templating-resourc import {Config, Rest} from 'aurelia-api'; import {configure} from '../src/aurelia-authentication'; -import {AuthService} from '../src/aurelia-authentication'; +import {AuthService} from '../src/authService'; import {Authentication} from '../src/authentication'; import {AuthFilterValueConverter} from '../src/authFilterValueConverter'; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..4adcc86 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2015", + "module": "es2015", + "experimentalDecorators": true, + "emitDecoratorMetadata": false, + "moduleResolution": "node", + "stripInternal": true, + "preserveConstEnums": true, + "listFiles": true, + "declaration": true, + "removeComments": true, + "lib": ["es2015", "dom"] + }, + "exclude": [ + "node_modules", + "dist", + "build", + "doc", + "test", + "config.js", + "gulpfile.js", + "karma.conf.js" + ] +} diff --git a/typings.json b/typings.json new file mode 100644 index 0000000..f9c0f0c --- /dev/null +++ b/typings.json @@ -0,0 +1,4 @@ +{ + "name": "aurelia-authentication", + "main": "dist/aurelia-authentication.d.ts" +}