From d36b82d53c2ecabf70094c0c187fbf2140965b78 Mon Sep 17 00:00:00 2001 From: Henrich Kraemer Date: Thu, 16 Jul 2015 11:52:19 +0200 Subject: [PATCH 01/14] Use webcrypto based connect-js library. This adopts usage of the WebCrypto Api based connect-js library as tracked on https://github.com/anvilresearch/connect-js/issues/7#issuecomment-175079961 With this most API methods are now returning promises. Summary of other changes: * Use npm and browserify to consume connect-js * Adopt latest anvil cli The changes were made over a period of time. Here are some of the more notable original commits with comments: COMMIT: callback_popup to check whether opener is available COMMIT: index.html shows userInfo.name may not be available COMMIT: add APP_AUTH_CALLBACK variable COMMIT: establish scope.session in SigninCtrl and MainCtrl COMMIT: scope to requireScope profile instead of realm COMMIT: adopt new cli COMMIT: Handle disconnected popup in passwordless login. The passwordless login method sends the user a link in an email. When the user follows this link a new browser window opens. However the original popup window will not redirect and remains open with a page allowing to resend the link. The connect-js library was changed to handle this case so that it would expect the page which is opened when the email link is followed to handle the callback so that the session is populated appropriately in localStorage. connect-js now listens for that and then closes the popup window. It also expect the email page to close itself. This may not work for all browsers. I did see this work in Chrome and Safari but fail in Firefox. Note that the popup Callback should behave in the same way it it is redirected by the server, which happens when other authentication methods are used. This also adds the newer dependencies and improves logging similar to how it was done in connect-js. COMMIT: refresh angular app on passwordless login COMMIT: adding jspm bundle-sfx for demo COMMIT: Manual angular bootstrap This was to avoid issue https://github.com/systemjs/systemjs/issues/1032 COMMIT: Switch to using npm and browserify This now requires anvil-connect-js ^0.2 bower is still used for shims and bootstrap. Some bugs were addressed found during manual testing. Also added more logging using bows. COMMIT: Added section Get connect-js client libraries I am uncertain about the remainder of the doc, but it looked OK to me at first glance although I have not done a client registration for quite some time. --- .gitignore | 8 +- Gruntfile.js | 234 ++++++++++++++++++++++++------- README.md | 129 ++++++++++++++--- app/callback_popup.html | 75 ++++++++-- app/index.html | 50 ++----- app/rp.html | 101 ++----------- app/scripts/app.js | 80 ++++++++--- app/scripts/rp.js | 73 ++++++++++ authconf.dev.b2d.json | 1 + authconf.dev.b2d.page.json | 1 + authconf.dev.localhost.json | 1 + authconf.dev.localhost.page.json | 1 + bower.json | 10 +- package.json | 49 +++++-- register_with_anvil_connect.sh | 47 +++++-- 15 files changed, 595 insertions(+), 265 deletions(-) create mode 100644 app/scripts/rp.js diff --git a/.gitignore b/.gitignore index 6c90f96..4403d71 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ -node_modules +app.browser +bower_components dist +jspm/jspm_packages +jspm.x*/* +keys +node_modules .tmp .sass-cache authconf.json -bower_components diff --git a/Gruntfile.js b/Gruntfile.js index 26f185f..71977e5 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -16,11 +16,11 @@ module.exports = function (grunt) { require('time-grunt')(grunt); var modRewrite = require('connect-modrewrite'); + var serveStatic = require('serve-static'); // Configurable paths for the application var appConfig = { app: require('./bower.json').appPath || 'app', - dist: 'dist' }; var authConfig = @@ -42,18 +42,18 @@ module.exports = function (grunt) { auth_config_data: authConfigData, // Watches files for changes and runs tasks based on the changed files + // to rebundle browserify bundle use watchify of npm... watch: { livereload: { options: { livereload: '<%= connect.options.livereload %>' }, files: [ - '<%= yeoman.app %>/{,*/}*.html', - '<%= yeoman.app %>/{,*/}*.js', - '.tmp/styles/{,*/}*.css', - '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}' + 'app/{,*/}*.html', + 'app/styles/{,*/}*.css', + 'app/scripts/{,*/}*.js' ], - tasks: ['newer:copy'] + tasks: ['newer:copy:browser'] } }, @@ -61,39 +61,58 @@ module.exports = function (grunt) { connect: { options: { port: "<%= auth_config_data.APP_PORT %>", // may need to convert to number? + livereload: 35729, // Change this to '0.0.0.0' to access the server from outside. hostname: '<%= auth_config_data.APP_HOST %>', - livereload: 35729 + open: false, + base: [ + 'app.browser' + ], + middleware: function (connect, options) { + var middlewares = []; + middlewares.push(connect().use( + '/node_modules', + serveStatic('./node_modules') + )); + middlewares.push(modRewrite(['^[^\\.]*$ /index.html [L]'])); + middlewares.push(connect().use( + '/bower_components', + serveStatic('./bower_components') + )); + options.base.forEach(function (base) { + return middlewares.push(serveStatic(base)); + }); + return middlewares; + }, }, livereload: { options: { open: true, + } + }, + dist: { + options: { + livereload: false, base: [ - '.tmp', - 'dist/app' + 'dist' ], - middleware: function (connect, options) { - var middlewares = []; - middlewares.push(modRewrite(['^[^\\.]*$ /index.html [L]'])); - middlewares.push(connect().use( - '/bower_components', - connect.static('./bower_components') - )); - options.base.forEach(function (base) { - return middlewares.push(connect['static'](base)); - }); - return middlewares; - } + keepalive: true, + } }, }, // Empties folders to start fresh clean: { - server: { + browser: { files: [{ - src: ['.tmp', 'dist'] + src: ['app.browser'] }] + }, + dist: { + files: [{ + src: ['dist'] + }] } }, @@ -102,63 +121,184 @@ module.exports = function (grunt) { copy: { styles: { expand: true, - cwd: '<%= yeoman.app %>/styles', - dest: '.tmp/styles/', + cwd: 'app/styles', src: '{,*/}*.css' }, - dist: { + browser: { + options: { + process: function (content, srcpath) { + return grunt.template.process( + content, + {data: authConfigData}); + }, + }, expand: true, - dest: 'dist', - src: [ - 'register_with_anvil_connect.sh', - '<%= yeoman.app %>/**'], + cwd: 'app', + dest: 'app.browser', + src: ['**/*'], + }, + browserscript: { + dest: 'app.browser/', + src: ['register_with_anvil_connect.sh'], options: { - process: function( content, srcpath) { + process: function (content, srcpath) { return grunt.template.process( - content, - {data: authConfigData}); + content, + {data: authConfigData}); }, }, }, + dist: { + expand: true, + cwd: 'app.browser', + dest: 'dist', + src: ['**/*'], + }, + }, + + browserify: { + options: { + browserifyOptions: { + debug: true + } + }, + // having lib and and browser to split bundles does not + // seem to buy much if one can use watchify also + //lib: { + // options: { + // alias: [ + // 'angular:', + // 'angular-animate:', + // 'angular-cookies:', + // 'angular-resource:', + // 'angular-route:', + // 'angular-sanitize:', + // 'angular-touch:', + // 'bows:' + // ] + // }, + // src: ['.'], + // dest: 'app.browser/scripts/dev-vendor-bundle.js' + //}, + browser: { + //options: { + // external: [ + // 'angular', + // 'angular-animate', + // 'angular-cookies', + // 'angular-resource', + // 'angular-route', + // 'angular-sanitize', + // 'angular-touch', + // 'bows' + // ] + //}, + files: { + 'app.browser/scripts/dev-bundle.js': + ['app.browser/scripts/app.js'], + 'app.browser/scripts/rp-dev-bundle.js': + ['app.browser/scripts/rp.js'] + } + }, + dist: { + options: { + browserifyOptions: { + debug: false + } + }, + files: { + 'dist/scripts/app-bundle.js': ['dist/scripts/app.js'], + 'dist/scripts/rp-bundle.js': ['dist/scripts/rp.js'] + } + } }, - // Run some tasks in parallel to speed up the build process - concurrent: { - server: [ - 'copy:styles', - 'copy:dist', - ] + uglify: { + options: { + mangle: false + }, + dist: { + files: { + 'dist/scripts/app-bundle.min.js': [ + 'bower_components/es5-shim/es5-shim.js', + 'bower_components/json3/lib/json3.min.js', + 'bower_components/promiz/promiz.js', + 'bower_components/webcrypto-shim/webcrypto-shim.js', + 'node_modules/text-encoder-lite/index.js', + 'dist/scripts/app-bundle.js' + ], + 'dist/scripts/rp-bundle.min.js': [ + 'bower_components/es5-shim/es5-shim.js', + 'bower_components/json3/lib/json3.min.js', + 'bower_components/promiz/promiz.js', + 'bower_components/webcrypto-shim/webcrypto-shim.js', + 'node_modules/text-encoder-lite/index.js', + 'dist/scripts/rp-bundle.js' + ] + + } + } }, + processhtml: { + dist: { + files: { + 'dist/index.html': ['app.browser/index.html'], + 'dist/rp.html': ['app.browser/rp.html'] + } + } + } }); grunt.registerTask('chmodScript', 'Makes script executable', function(target) { var fs = require('fs'); - fs.chmodSync('dist/register_with_anvil_connect.sh', '755'); + fs.chmodSync('app.browser/register_with_anvil_connect.sh', '755'); + }); + + + grunt.registerTask('build_browser', function (target) { + grunt.log.writeln('Build app in app.browser folder, matching auth server configuration in %s', grunt.config('auth_config')); + grunt.log.writeln('If not yet done register client using app.browser/register_with_anvil_connect.sh. See README.md'); + grunt.task.run([ + 'clean:browser', + 'copy:browser', + 'browserify:browser', + 'copy:browserscript', + 'chmodScript', + ]); }); grunt.registerTask('build', function (target) { grunt.log.writeln('Build app in dist folder, matching auth server configuration in %s', grunt.config('auth_config')); grunt.log.writeln('If not yet done register client using dist/register_with_anvil_connect.sh. See README.md'); grunt.task.run([ - 'clean', + 'build_browser', + 'clean:dist', 'copy:dist', + 'browserify:dist', + 'uglify:dist', + 'processhtml:dist', 'chmodScript', ]); }); + grunt.registerTask('serve_dist', 'serve already build files from dist', function (target) { + grunt.task.run([ + 'connect:dist' + ]); + }); + grunt.registerTask('serve', 'Compile then start a connect web server', function (target) { if (target === 'dist') { - return grunt.task.run(['build', 'connect:dist:keepalive']); + return grunt.task.run(['build', 'connect:dist']); } grunt.task.run([ - 'clean:server', - 'concurrent:server', - 'connect:livereload', - 'watch' - ]); + 'build_browser', + 'connect:livereload', + 'watch' + ]); }); diff --git a/README.md b/README.md index ec6348d..8080224 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,95 @@ This repository demonstrates authenticating users of an AngularJS app using Anvil Connect. ## Prerequisites + +### Setup server: Setup [Anvil Connect authorization server](https://github.com/anvilresearch/connect/blob/master/README.md) -As a result the Anvil Authorization server should run on localhost:3000. +These instructions assume that the Anvil Authorization server (issuer) runs on localhost:3000 in development mode (not using docker). However it should also be adaptable to using docker. + +One must register the application with the issuer. For this the cli is used. The [cli must be setup](https://github.com/anvilresearch/connect-docs/blob/master/cli.md) first so that you can login to the server. + +### Get connect-js client libraries + +There is currently work in progress to update the client libraries to use +webcrypto APIs instead of encryption libraries. See +[Webcrypto API · Issue #7 · anvilresearch/connect-js](https://github.com/anvilresearch/connect-js/issues/7) for more details. + +To get the webcrypto code for testing the fork is used: + +```console +$ # create or got to some suited directory then +$ git clone https://github.com/henrjk/connect-js.git +$ cd connect-js +$ git checkout webcrypto + +## These steps are described here: https://github.com/henrjk/connect-js/tree/webcrypto +$ npm install +$ jspm install +$ npm run test +``` + +Next get this project and npm link it with the connect-js library installed +in the previous step + +```console +$ # create or got to some suited directory. This could be the same as above. +$ git clone https://github.com/henrjk/connect-example-angularjs.git +$ cd connect-example-angularjs +$ git checkout use-npm +``` + +No go to the directory where connect-js was cloned to and issue these commands: +```console +connect-js dev (webcrypto)$ npm link + +> anvil-connect-js@0.2.0 prepublish /Users/dev/code/work/connect-js-fork/connect-js +> npm run build + + +> anvil-connect-js@0.2.0 build /Users/dev/code/work/connect-js-fork/connect-js +> grunt + +Running "clean:dist" (clean) task +>> 24 paths cleaned. + +Running "babel:compile" (babel) task + +Done, without errors. +/Users/dev/.nvm/versions/node/v0.12.6/lib/node_modules/anvil-connect-js -> /Users/dev/code/work/connect-js-fork/connect-js +connect-js dev (webcrypto)$ +``` + +Then go back to the `connect-example-angularjs` directory and do the following: + +```console +connect-js dev (webcrypto)$ cd ../connect-example-angularjs/ +connect-example-angularjs dev (use-npm)$ npm link anvil-connect-js +/Users/dev/code/work/connect-js-fork/connect-example-angularjs/node_modules/anvil-connect-js -> /Users/dev/.nvm/versions/node/v0.12.6/lib/node_modules/anvil-connect-js -> /Users/dev/code/work/connect-js-fork/connect-js + +connect-example-angularjs dev (use-npm)$ npm install +# this takes a few minutes... + + +connect-example-angularjs dev (use-npm)$ bower install +# this goes much quicker... +``` + +Now you will have to come up with a authconf.json file in the projects +root folder which matches your server. This is described below although I have +not recently verified these steps. Here is an outline of the steps: + +* Determine correct `authconf.json` file and place it in connect-example-angularjs root folder. +* grunt build +* login to the server (nvl login) +* execute script generated by grunt build: `./dist/register_with_anvil_connect.sh` +* Observe the assigned client ID and update `authconf.json` accordingly. +* grunt serve + +This should build the browser app configured correctly for the client registration +in folder app.browser, browserify the dependencies and the open a browser +showing the ![Angular example index page](png/home_page_localhost.png) + ## Register the client and generate matching angular sources. To allow the app to connect to the authorization server a client (representing) @@ -22,13 +108,14 @@ To achieve this the configuration information for the client is stored in file You will find that this file is **not** checked in. Instead there are files with similar names which are checked in. These are good starting points for your -client registration. For example they differ -depending on whether boot2docker is involved or not. Also there can be +client registration. They differ +depending on whether there is a distinct docker host which is *not* localhost or +not. Also there are differences on how the authentication is displayed, for example using a popup or a new page. Use one of these as a starting point and copy them to `authconf.json`. For -example when using docker via boot2docker the following is a good starting +example when running using docker via boot2docker the following is a good starting point: ```console @@ -41,7 +128,6 @@ via `grunt serve` then use: cp authconf.dev.localhost.json authconf.json ``` - The Anvil Authentication server recognizes clients by an id which is generated when they are registered. Therefore the starting point is not yet final as the id in there must be changed. To help with ensure that the client registration @@ -54,26 +140,29 @@ First generate the script by: grunt build ``` +Next login to the cli. + +```console +dev$ nvl login +? Select an Anvil Connect instance localhost:3000 (localhost-3000) +Selected issuer localhost:3000 (http://localhost:3000) +Warning: you are communicating over plain text. +? Enter your email example@gmail.com +? Enter your password ************ +You have been successfully logged in to localhost:3000 +``` + Use the generated `dist/register_with_anvil_connect.sh` as follows in the root directory of your Anvil Connect Authentication server: ```console -mac:anvil-connect dev$ /Users/dev/code/connect-example-angularjs/dist/register_with_anvil_connect.sh -Registring nv add client { - "client_name": "Angular Example App", - "default_max_age": 36000, - "redirect_uris": [ - "http://localhost:9000/callback_popup.html", - "http://localhost:9000/callback_page.html", - "http://localhost:9000/rp.html"], - "post_logout_redirect_uris": ["http://localhost:9000"], - "trusted": "true" -} +dev$ ./dist/register_with_anvil_connect.sh +Registering this client with localhost-3000 Succeeded. Define CLIENT_ID as follows in authconf.json: { ... - "CLIENT_ID" : "d20dc3cf-cdfd-4e14-a070-0cb40bdb5d92", + "CLIENT_ID" : "29dcbf2a-88d6-4038-b9f9-6bf425104b59", ... } ``` @@ -83,10 +172,12 @@ The id shown will be unique to your authentication server. Replace the existing ## Run with angular app served by grunt serve -In this scenaria we are using a simple build server via grunt. +In this scenario we are using a simple build server via grunt. + +TODO: ```console -igelmac:connect-example-angularjs dev$ grunt serve +dev$ grunt serve Using authconf.json Running "serve" task diff --git a/app/callback_popup.html b/app/callback_popup.html index 2621167..6b1b774 100644 --- a/app/callback_popup.html +++ b/app/callback_popup.html @@ -1,24 +1,79 @@ + + + + + + + + + + -

Anvil Connect sign-in successful

- -

Please close this window.

+
+

Anvil Connect signing in...

+
+ + diff --git a/app/index.html b/app/index.html index f9968b6..4d082d3 100644 --- a/app/index.html +++ b/app/index.html @@ -7,6 +7,7 @@ + @@ -15,7 +16,7 @@ @@ -29,54 +30,21 @@

Your Logo

+ - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ---> diff --git a/app/rp.html b/app/rp.html index 1cffd87..d56a57d 100644 --- a/app/rp.html +++ b/app/rp.html @@ -4,102 +4,21 @@ - - + + - - - - - - - - - - - - - - - - - + + + + + - - - - diff --git a/app/scripts/app.js b/app/scripts/app.js index 3f9e69d..5e86c0e 100644 --- a/app/scripts/app.js +++ b/app/scripts/app.js @@ -1,4 +1,15 @@ -'use strict'; +'use strict' +require('anvil-connect-js/lib/anvil-connect-angular') +require('angular-animate') +require('angular-cookies') +require('angular-resource') +require('angular-route') +require('angular-sanitize') +require('angular-touch') + +var bows = require('bows') + +var log = bows('app') /** * Anvil Connect AngularJS Example App @@ -6,6 +17,7 @@ angular .module('AnvilConnectClient', [ + 'anvil', 'ngAnimate', 'ngCookies', 'ngResource', @@ -22,9 +34,9 @@ angular issuer: '<%=AUTH_SERVER%>', client_id: '<%=CLIENT_ID%>', //redirect_uri: 'http://localhost:9000/callback.html', - redirect_uri: '<%=APP_SERVER%>/callback_<%= AUTH_DISPLAY%>.html', - display: '<%=AUTH_DISPLAY%>', - scope: 'realm email' + redirect_uri: '<%=APP_SERVER%>/<%=APP_AUTH_CALLBACK%>', + display: '<%=AUTH_DISPLAY%>' // , + // scope: 'realm email' }); $locationProvider.html5Mode(true); @@ -48,7 +60,7 @@ angular templateUrl: '/views/requiresScope.html', controller: 'RequiresScopeCtrl', resolve: { - session: AnvilProvider.requireScope('realm', '/unauthorized') + session: AnvilProvider.requireScope('profile', '/unauthorized') } }) @@ -58,16 +70,21 @@ angular }) // HANDLE CALLBACK (REQUIRED BY FULL PAGE NAVIGATION ONLY) - .when('/callback_page.html', { + .when('/<%= APP_AUTH_CALLBACK %>', { resolve: { session: function ($location, Anvil) { + log.debug('/<%= APP_AUTH_CALLBACK %>.resolve.session:', $location) if ($location.hash()) { - Anvil.authorize().then( + Anvil.promise.authorize().then( // handle successful authorization function (response) { - $location.url(localStorage['anvil.connect.destination'] || '/'); - delete localStorage['anvil.connect.destination'] + var dest = Anvil.destination(false) + // $location.url( dest || '/'); did not react for me + // there may be solutions with scope apply but this seems + // to work fine, although this may not be the best solution. + console.log('/<%= APP_AUTH_CALLBACK %> authorize() succeeded, destination=', dest) + location.href = dest || '/' }, // handle failed authorization @@ -88,31 +105,58 @@ angular redirectTo: '/' }); }) - .run(function (Anvil) { - Anvil.getKeys().then(function (jwks) { - console.log('Loaded JWKs', jwks) + log.debug('run() entering') + /** + * Reinstate an existing session + */ + Anvil.promise.deserialize().catch( function () { + log.debug('Ignore promise rejection when reinstating session') + }) + Anvil.promise.prepareAuthorization().then(function (result) { + log.debug('prepareAuthorization succeeded:', result) + }, function (err) { + log.error('prepareAuthorization failed:', err) }) }) - .controller('SigninCtrl', function ($scope, Anvil) { + $scope.session = Anvil.session; + + log.debug('SigninCtrl() init: adding Anvil.once("authenticated"..) listener') + Anvil.once('authenticated', function () { + log.debug('SigninCtrl() init: authenticated callback: calling $scope.$apply') + $scope.$apply(); + }) + $scope.signin = function () { - Anvil.authorize() + log.debug('SigninCtrl.signin(): entering function') + Anvil.promise.authorize() + Anvil.once('authenticated', function () { + log.debug('SigninCtrl.signin() authenticated callback: calling $scope.$apply') + $scope.$apply(); + }) }; $scope.signout = function () { Anvil.signout('/'); }; - $scope.$watch(function () { return Anvil.session }, function (newVal) { - $scope.session = newVal; - }); + $scope.$watch( + // proper formatting allows easier setting of breakpoints. + function () { + return Anvil.session + }, + function (newVal) { + $scope.session = newVal + }, + true + ); }) .controller('MainCtrl', function ($scope, Anvil) { - // ... + $scope.session = Anvil.session }) .controller('RequiresAuthenticationCtrl', function ($scope, session) { diff --git a/app/scripts/rp.js b/app/scripts/rp.js new file mode 100644 index 0000000..39db396 --- /dev/null +++ b/app/scripts/rp.js @@ -0,0 +1,73 @@ +'use strict' +var AnvilPlainInit = require('anvil-connect-js/lib/anvil-connect-plain').default +var bows = require('bows') + +var log = bows('rp.html') + +window.Anvil = AnvilPlainInit() + +Anvil.configure({ + issuer: '<%=AUTH_SERVER%>', + client_id: '<%= CLIENT_ID %>', + redirect_uri: '<%=APP_SERVER%>/rp.html', + display: 'page', + scope: 'realm' +}); + +Anvil.promise.deserialize(); + +var response = (location.hash) ? Anvil.parseFormUrlEncoded(location.hash.substring(1)) : {}; + +if (location.hash) { + Anvil.promise.prepareAuthorization().then( function () { + Anvil.promise.callback(response).then( + function success (session) { + log.info('RP CALLBACK SUCCESS', session, Anvil.sessionState); + window.parent.postMessage(location.hash, location.origin); + }, + function failure (fault) { + log.info('RP CALLBACK FAILURE', fault); + }) + }) +} + +// start checking the session every 30 seconds +function setTimer() { + var timer = setInterval("Anvil.checkSession('op')", 30*1000); +} + + + +// listen for changes to OP browser state +function receiveMessage(event) { + if (event.origin !== Anvil.issuer) { + log.debug('Houston, we have a problem', event.origin, Anvil.issuer); + return; + } + + if (event.data === 'error') { + log.debug('ERROR FROM OP', event.data); + } + + // SESSION STATE IS THE SAME + if (event.data === 'unchanged') { + log.info('session state: unchanged'); + } + + // SESSION STATE HAS CHANGED + else { + log.info('session state: changed'); + Anvil.promise.uri('authorize', { + prompt: 'none', + id_token_hint: Anvil.session.id_token + }).then( function (uri) { + log.info('RP REAUTHENTICATING', uri) + window.location = uri; + }); + } +} + +window.addEventListener("message", receiveMessage, false); + +setTimer() + diff --git a/authconf.dev.b2d.json b/authconf.dev.b2d.json index 5a3db51..bd6db57 100644 --- a/authconf.dev.b2d.json +++ b/authconf.dev.b2d.json @@ -4,6 +4,7 @@ "APP_HOST": "boot2docker", "APP_PORT": "9000", "APP_SERVER" : "<%= APP_PROTO %>://<%= APP_HOST %>:<%= APP_PORT %>", + "ISSUER_NAME" : "localhost-3000", "AUTH_SERVER" : "http://localhost:3000", "AUTH_DISPLAY": "popup", "APP_AUTH_CALLBACK": "callback_<%= AUTH_DISPLAY %>.html" diff --git a/authconf.dev.b2d.page.json b/authconf.dev.b2d.page.json index 5e5e9d0..cbe19c4 100644 --- a/authconf.dev.b2d.page.json +++ b/authconf.dev.b2d.page.json @@ -4,6 +4,7 @@ "APP_HOST": "boot2docker", "APP_PORT": "9000", "APP_SERVER" : "<%= APP_PROTO %>://<%= APP_HOST %>:<%= APP_PORT %>", + "ISSUER_NAME" : "localhost-3000", "AUTH_SERVER" : "http://localhost:3000", "AUTH_DISPLAY": "page", "APP_AUTH_CALLBACK": "callback_<%= AUTH_DISPLAY %>.html" diff --git a/authconf.dev.localhost.json b/authconf.dev.localhost.json index 7ee73bc..9bd9405 100644 --- a/authconf.dev.localhost.json +++ b/authconf.dev.localhost.json @@ -4,6 +4,7 @@ "APP_HOST": "localhost", "APP_PORT": "9000", "APP_SERVER" : "<%= APP_PROTO %>://<%= APP_HOST %>:<%= APP_PORT %>", + "ISSUER_NAME" : "localhost-3000", "AUTH_SERVER" : "http://localhost:3000", "AUTH_DISPLAY": "popup", "APP_AUTH_CALLBACK": "callback_<%= AUTH_DISPLAY %>.html" diff --git a/authconf.dev.localhost.page.json b/authconf.dev.localhost.page.json index d03349c..18115ac 100644 --- a/authconf.dev.localhost.page.json +++ b/authconf.dev.localhost.page.json @@ -4,6 +4,7 @@ "APP_HOST": "localhost", "APP_PORT": "9000", "APP_SERVER" : "<%= APP_PROTO %>://<%= APP_HOST %>:<%= APP_PORT %>", + "ISSUER_NAME" : "localhost-3000", "AUTH_SERVER" : "http://localhost:3000", "AUTH_DISPLAY": "page", "APP_AUTH_CALLBACK": "callback_<%= AUTH_DISPLAY %>.html" diff --git a/bower.json b/bower.json index 7a77d67..53970ab 100644 --- a/bower.json +++ b/bower.json @@ -2,18 +2,10 @@ "name": "anvil-connect-angularjs-example", "version": "0.0.0", "dependencies": { - "angular": "1.2.16", "json3": "~3.3.1", "es5-shim": "~3.1.0", "bootstrap": "~3.1.1", - "angular-resource": "1.2.16", - "angular-cookies": "1.2.16", - "angular-sanitize": "1.2.16", - "angular-animate": "1.2.16", - "angular-touch": "1.2.16", - "angular-route": "1.2.16", - "anvil-connect": "https://github.com/christiansmith/anvil-connect-js.git", - "jquery": "~2.1.3" + "webcrypto-shim": "*" }, "appPath": "app" } diff --git a/package.json b/package.json index ff31924..83cb927 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,46 @@ { "name": "anvil-connect-angularjs-example", "version": "0.0.0", - "dependencies": {}, + "dependencies": { + "angular": "^1.4.8", + "angular-animate": "^1.4.8", + "angular-cookies": "^1.4.8", + "angular-resource": "^1.4.8", + "angular-route": "^1.4.8", + "angular-sanitize": "^1.4.8", + "angular-touch": "^1.4.8", + "anvil-connect-js": "^0.2.0", + "bows": "^1.4.8", + "text-encoder-lite": "^1.0.0" + }, "devDependencies": { + "bower": "^1.7.2", + "browserify": "^13.0.0", "connect-modrewrite": "^0.7.6", "grunt": "^0.4.1", - "grunt-concurrent": "^0.5.0", - "grunt-contrib-clean": "^0.5.0", - "grunt-contrib-concat": "^0.4.0", - "grunt-contrib-connect": "^0.7.1", - "grunt-contrib-copy": "^0.5.0", - "grunt-contrib-cssmin": "^0.9.0", + "grunt-browserify": "^4.0.1", + "grunt-contrib-clean": "^0.7.0", + "grunt-contrib-connect": "^0.11.2", + "grunt-contrib-copy": "^0.8.2", + "grunt-contrib-uglify": "^0.11.0", "grunt-contrib-watch": "^0.6.1", - "grunt-newer": "^0.7.0", - "load-grunt-tasks": "^0.4.0", - "time-grunt": "^0.3.1" - }, - "engines": { - "node": ">=0.10.0" + "grunt-filerev": "^2.3.1", + "grunt-newer": "^1.1.1", + "grunt-processhtml": "^0.3.8", + "grunt-shell": "^1.1.2", + "load-grunt-tasks": "^3.4.0", + "serve-static": "^1.10.0", + "time-grunt": "^1.2.2", + "watchify": "^3.7.0" }, "scripts": { - "test": "grunt test" + "dev": "grunt serve", + "build": "grunt build", + "grunt-serve-build": "grunt serve_dist", + "clean": "grunt clean", + "clean-all": "grunt clean && rimraf bower_components && rimraf jspm/jspm_packages && rimraf node_modules", + "watchify": "watchify app.browser/scripts/app.js -o app.browser/scripts/dev-bundle.js --verbose", + "watchify-rp": "watchify app.browser/scripts/rp.js -o app.browser/scripts/rp-dev-bundle.js --verbose" + } } diff --git a/register_with_anvil_connect.sh b/register_with_anvil_connect.sh index 7852c83..104862a 100755 --- a/register_with_anvil_connect.sh +++ b/register_with_anvil_connect.sh @@ -4,27 +4,46 @@ # Register client with anvil connect -client='{ - "client_name": "Angular example with <%= AUTH_DISPLAY %> for <%= APP_SERVER %>", - "default_max_age": 36000, - "redirect_uris": [ - "<%= APP_SERVER %>/<%= APP_AUTH_CALLBACK %>", - "<%= APP_SERVER %>/rp.html"], - "post_logout_redirect_uris": ["<%= APP_SERVER %>"], - "trusted": true -}' - -echo "Registring nv add client $client" - -out=$(nv add client "$client") || { - echo "failed to register client" >&2 exit 2 +fail() { + local message + message=${1-"Failed to register client"} + echo "$message" >&2 + echo "You may need to login with 'nvl login'" >&2 + echo "Perhaps nvl is not yet setup? Consult connect-cli getting started documentation" >&2 + exit 2 } +echo "Registering this client with <%= ISSUER_NAME %>" + +out=$(nvl client:register \ + --issuer "<%= ISSUER_NAME %>" \ + --trusted \ + --name "Angular example with <%= AUTH_DISPLAY %> for <%= APP_SERVER %>" \ + --uri "<%= APP_SERVER %>" \ + --logo-uri "<%= APP_SERVER %>/logo" \ + --application-type "web" \ + --response-type "id_token token" \ + --grant-type "implicit" \ + --default-max-age "3600" \ + --redirect-uri "<%= APP_SERVER %>/<%= APP_AUTH_CALLBACK %>" \ + --redirect-uri "<%= APP_SERVER %>/rp.html" \ + --post-logout-redirect-uri "<%= APP_SERVER %>/" \ + --post-logout-redirect-uri "<%= APP_SERVER %>/") + +# duplication of --post-logout-redirect-uri see https://github.com/anvilresearch/connect-cli/issues/70 + +REGISTER_STATUS=$? +if [ $REGISTER_STATUS -ne 0 ]; then + fail "nvl client:register failed with status $REGISTER_STATUS" +fi + # From OS X: # $ echo "$out" | grep "_id" | grep -o -E '([[:xdigit:]]*-){1,10}[[:xdigit:]]*' # ec8262ae-28d5-4943-8237-d8145042c3e0 client_id=$(echo "$out" | grep "_id" | grep -o -E '([[:xdigit:]]*-){1,10}[[:xdigit:]]*') +[ -n "$client_id" ] || fail + echo "Succeeded." echo "Define CLIENT_ID as follows in authconf.json:" printf '{\n' From 1335aa1c98d4cadff8204ce10440f60e4b34527e Mon Sep 17 00:00:00 2001 From: Henrich Kraemer Date: Thu, 28 Jan 2016 17:43:29 +0100 Subject: [PATCH 02/14] Update example for anvil-connect-js 0.2.1 --- app/scripts/anvil-connect-angular.js | 94 ++++++++++++++++++++++++++++ app/scripts/app.js | 2 +- app/scripts/rp.js | 18 ++++-- package.json | 4 +- 4 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 app/scripts/anvil-connect-angular.js diff --git a/app/scripts/anvil-connect-angular.js b/app/scripts/anvil-connect-angular.js new file mode 100644 index 0000000..2631a3e --- /dev/null +++ b/app/scripts/anvil-connect-angular.js @@ -0,0 +1,94 @@ +'use strict' + +var Anvil = require('anvil-connect-js').default +require('angular') + +function init (providerOptions, $http, $q, $location, $window, $document) { + Anvil.init(providerOptions, { + http: { + request: function (config) { + return $http(config) + }, + getData: function (response) { + return response.data + } + }, + location: { + hash: function () { + return $location.hash() + }, + path: function () { + return $location.path() + } + }, + dom: { + getWindow: function () { + return $window + }, + getDocument: function () { + return $document[0] + } + } + }) + return Anvil +} + +angular.module('anvil', []) + + .provider('Anvil', function AnvilProvider () { + /** + * Require Authentication + */ + + function requireAuthentication ($location, Anvil) { + if (!Anvil.isAuthenticated()) { + Anvil.promise.authorize() + } + + return Anvil.session + } + + Anvil.requireAuthentication = ['$location', 'Anvil', requireAuthentication] + + /** + * Require Scope + */ + + Anvil.requireScope = function (scope, fail) { + return ['$location', 'Anvil', function requireScope ($location, Anvil) { + if (!Anvil.isAuthenticated()) { + return Anvil.promise.authorize() + } else if (Anvil.session.access_claims.scope.indexOf(scope) === -1) { + $location.path(fail) + return false + } else { + return Anvil.session + } + }] + } + + /** + * Provider configuration + */ + + this.configure = function (options) { + Anvil.configure(options) + } + + /** + * Factory + */ + + Anvil.$get = [ + '$q', + '$http', + '$rootScope', + '$location', + '$document', + '$window', function ($q, $http, $rootScope, $location, $document, $window) { + init(null, $http, $q, $location, $window, $document) + return Anvil + }] + + return Anvil + }) diff --git a/app/scripts/app.js b/app/scripts/app.js index 5e86c0e..56b0304 100644 --- a/app/scripts/app.js +++ b/app/scripts/app.js @@ -1,5 +1,5 @@ 'use strict' -require('anvil-connect-js/lib/anvil-connect-angular') +require('./anvil-connect-angular') require('angular-animate') require('angular-cookies') require('angular-resource') diff --git a/app/scripts/rp.js b/app/scripts/rp.js index 39db396..1b90413 100644 --- a/app/scripts/rp.js +++ b/app/scripts/rp.js @@ -1,17 +1,26 @@ 'use strict' -var AnvilPlainInit = require('anvil-connect-js/lib/anvil-connect-plain').default var bows = require('bows') +var Q = require('q') +window.Anvil = require('anvil-connect-js').default +// window allows access from setTimeout below. var log = bows('rp.html') -window.Anvil = AnvilPlainInit() - -Anvil.configure({ +Anvil.init({ issuer: '<%=AUTH_SERVER%>', client_id: '<%= CLIENT_ID %>', redirect_uri: '<%=APP_SERVER%>/rp.html', display: 'page', scope: 'realm' +}, { + http: { + request: function (config) { + return Q.xhr(config) + }, + getData: function (response) { + return response.data + } + } }); Anvil.promise.deserialize(); @@ -70,4 +79,3 @@ function receiveMessage(event) { window.addEventListener("message", receiveMessage, false); setTimer() - diff --git a/package.json b/package.json index 83cb927..e6792a0 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,9 @@ "angular-route": "^1.4.8", "angular-sanitize": "^1.4.8", "angular-touch": "^1.4.8", - "anvil-connect-js": "^0.2.0", + "anvil-connect-js": "^0.2.1", "bows": "^1.4.8", + "q": "^1.4.1", "text-encoder-lite": "^1.0.0" }, "devDependencies": { @@ -41,6 +42,5 @@ "clean-all": "grunt clean && rimraf bower_components && rimraf jspm/jspm_packages && rimraf node_modules", "watchify": "watchify app.browser/scripts/app.js -o app.browser/scripts/dev-bundle.js --verbose", "watchify-rp": "watchify app.browser/scripts/rp.js -o app.browser/scripts/rp-dev-bundle.js --verbose" - } } From 15067f3aa52da7961c8a04b9da41e642b2ee33ac Mon Sep 17 00:00:00 2001 From: Henrich Kraemer Date: Thu, 28 Jan 2016 17:26:51 +0100 Subject: [PATCH 03/14] Fix location updates Reverses usage of location.href in app.js in favor of $location.url. Also fixes refresh problem when the destination was not visited. Previously for example after signin the callback url would remain the browsers location. Now angular properly takes the changes made to the Url into account. --- app/scripts/app.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/scripts/app.js b/app/scripts/app.js index 56b0304..58267e5 100644 --- a/app/scripts/app.js +++ b/app/scripts/app.js @@ -75,8 +75,7 @@ angular session: function ($location, Anvil) { log.debug('/<%= APP_AUTH_CALLBACK %>.resolve.session:', $location) if ($location.hash()) { - Anvil.promise.authorize().then( - + return Anvil.promise.authorize().then( // handle successful authorization function (response) { var dest = Anvil.destination(false) @@ -84,7 +83,7 @@ angular // there may be solutions with scope apply but this seems // to work fine, although this may not be the best solution. console.log('/<%= APP_AUTH_CALLBACK %> authorize() succeeded, destination=', dest) - location.href = dest || '/' + $location.url(dest || '/') }, // handle failed authorization @@ -94,8 +93,8 @@ angular ); } else { - $location.url(localStorage['anvil.connect.destination'] || '/'); - delete localStorage['anvil.connect.destination'] + var dest = Anvil.destination(false) + $location.url(dest || '/'); } } } From d78a14af4ce6b07c3dc9cfc90fddb60eaba2fc01 Mon Sep 17 00:00:00 2001 From: Henrich Kraemer Date: Tue, 2 Feb 2016 09:05:50 +0100 Subject: [PATCH 04/14] pull app configuration into app.config Files in /app.config/ are now generated on grunt config based on the templates in /app.config.templ/* and the corresponding variables in /authconfig.json authconfig.json contains variables describing the issuer (connect server) and the server the app will run on as wells as other registration information. After `grunt config` there are 2 files in /app.config: 1. /app.config/anvil-config.js which is used by app/scripts/*.js files to configure the Anvil client library. 2. /app.config/register_with_anvil_connect.sh is a shell script which registers this client with the server using the `nvl` command line. This change allows to to have to eliminate intermediate copies of all app files which where previously in app.browser. Now /app.browser is used primarily to save dev-bundles generated by browserify. See npm run watchify to keep these up to date. In addition index.html still uses config values for declaring the session management iframes in particular for the op iframe. --- .gitignore | 3 +- Gruntfile.js | 122 +++++++++--------- app.config.templ/anvil-config.js | 6 + .../register_with_anvil_connect.sh | 0 app/callback_popup.html | 70 ++-------- app/rp.html | 9 -- app/scripts/app.js | 20 ++- app/scripts/callback_popup.js | 70 ++++++++++ app/scripts/rp.js | 22 ++-- package.json | 7 +- 10 files changed, 178 insertions(+), 151 deletions(-) create mode 100644 app.config.templ/anvil-config.js rename register_with_anvil_connect.sh => app.config.templ/register_with_anvil_connect.sh (100%) create mode 100644 app/scripts/callback_popup.js diff --git a/.gitignore b/.gitignore index 4403d71..5c420f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ app.browser +app.config bower_components dist -jspm/jspm_packages -jspm.x*/* keys node_modules .tmp diff --git a/Gruntfile.js b/Gruntfile.js index 71977e5..661f0b5 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -66,7 +66,9 @@ module.exports = function (grunt) { hostname: '<%= auth_config_data.APP_HOST %>', open: false, base: [ - 'app.browser' + 'app.config', + 'app.browser', + 'app', ], middleware: function (connect, options) { var middlewares = []; @@ -104,10 +106,15 @@ module.exports = function (grunt) { // Empties folders to start fresh clean: { + config: { + files: [{ + src: ['app.config'] + }] + }, browser: { - files: [{ - src: ['app.browser'] - }] + files: [{ + src: ['app.browser'] + }] }, dist: { files: [{ @@ -124,22 +131,23 @@ module.exports = function (grunt) { cwd: 'app/styles', src: '{,*/}*.css' }, - browser: { + config: { options: { process: function (content, srcpath) { return grunt.template.process( - content, - {data: authConfigData}); + content, + {data: authConfigData}); }, }, expand: true, - cwd: 'app', - dest: 'app.browser', - src: ['**/*'], + cwd: 'app.config.templ', + dest: 'app.config', + src: [ + 'anvil-config.js', + 'register_with_anvil_connect.sh' + ] }, - browserscript: { - dest: 'app.browser/', - src: ['register_with_anvil_connect.sh'], + browser: { options: { process: function (content, srcpath) { return grunt.template.process( @@ -147,8 +155,17 @@ module.exports = function (grunt) { {data: authConfigData}); }, }, + files: { + 'app.browser/index.html': ['app/index.html'] + } }, - dist: { + distApp: { + expand: true, + cwd: 'app', + dest: 'dist', + src: ['**/*'], + }, + distBrowser: { expand: true, cwd: 'app.browser', dest: 'dist', @@ -162,42 +179,14 @@ module.exports = function (grunt) { debug: true } }, - // having lib and and browser to split bundles does not - // seem to buy much if one can use watchify also - //lib: { - // options: { - // alias: [ - // 'angular:', - // 'angular-animate:', - // 'angular-cookies:', - // 'angular-resource:', - // 'angular-route:', - // 'angular-sanitize:', - // 'angular-touch:', - // 'bows:' - // ] - // }, - // src: ['.'], - // dest: 'app.browser/scripts/dev-vendor-bundle.js' - //}, browser: { - //options: { - // external: [ - // 'angular', - // 'angular-animate', - // 'angular-cookies', - // 'angular-resource', - // 'angular-route', - // 'angular-sanitize', - // 'angular-touch', - // 'bows' - // ] - //}, files: { 'app.browser/scripts/dev-bundle.js': - ['app.browser/scripts/app.js'], + ['app/scripts/app.js'], 'app.browser/scripts/rp-dev-bundle.js': - ['app.browser/scripts/rp.js'] + ['app/scripts/rp.js'], + 'app.browser/scripts/popup-dev-bundle.js': + ['app/scripts/callback_popup.js'] } }, dist: { @@ -208,7 +197,8 @@ module.exports = function (grunt) { }, files: { 'dist/scripts/app-bundle.js': ['dist/scripts/app.js'], - 'dist/scripts/rp-bundle.js': ['dist/scripts/rp.js'] + 'dist/scripts/rp-bundle.js': ['dist/scripts/rp.js'], + 'dist/scripts/popup-bundle.js': ['dist/scripts/callback_popup.js'] } } }, @@ -234,8 +224,15 @@ module.exports = function (grunt) { 'bower_components/webcrypto-shim/webcrypto-shim.js', 'node_modules/text-encoder-lite/index.js', 'dist/scripts/rp-bundle.js' + ], + 'dist/scripts/popup-bundle.min.js': [ + 'bower_components/es5-shim/es5-shim.js', + 'bower_components/json3/lib/json3.min.js', + 'bower_components/promiz/promiz.js', + 'bower_components/webcrypto-shim/webcrypto-shim.js', + 'node_modules/text-encoder-lite/index.js', + 'dist/scripts/popup-bundle.js' ] - } } }, @@ -244,7 +241,8 @@ module.exports = function (grunt) { dist: { files: { 'dist/index.html': ['app.browser/index.html'], - 'dist/rp.html': ['app.browser/rp.html'] + 'dist/rp.html': ['app.browser/rp.html'], + 'dist/callback_popup.html': ['app.browser/callback_popup.html'] } } } @@ -252,33 +250,39 @@ module.exports = function (grunt) { grunt.registerTask('chmodScript', 'Makes script executable', function(target) { var fs = require('fs'); - fs.chmodSync('app.browser/register_with_anvil_connect.sh', '755'); + fs.chmodSync('app.config/register_with_anvil_connect.sh', '755'); }); + grunt.registerTask('config', function (target) { + grunt.log.writeln('Generating config in app.config folder, matching auth server configuration in %s', grunt.config('auth_config')); + grunt.log.writeln('If not yet done register client using app.config/register_with_anvil_connect.sh. See README.md'); + grunt.task.run([ + 'clean:config', + 'copy:config', + 'chmodScript', + ]); + }); + grunt.registerTask('build_browser', function (target) { - grunt.log.writeln('Build app in app.browser folder, matching auth server configuration in %s', grunt.config('auth_config')); - grunt.log.writeln('If not yet done register client using app.browser/register_with_anvil_connect.sh. See README.md'); + grunt.log.writeln('Build app scripts in app.browser folder, matching auth server configuration in %s', grunt.config('auth_config')); grunt.task.run([ 'clean:browser', 'copy:browser', - 'browserify:browser', - 'copy:browserscript', - 'chmodScript', + 'browserify:browser' ]); }); grunt.registerTask('build', function (target) { grunt.log.writeln('Build app in dist folder, matching auth server configuration in %s', grunt.config('auth_config')); - grunt.log.writeln('If not yet done register client using dist/register_with_anvil_connect.sh. See README.md'); grunt.task.run([ 'build_browser', 'clean:dist', - 'copy:dist', + 'copy:distApp', + 'copy:distBrowser', 'browserify:dist', 'uglify:dist', - 'processhtml:dist', - 'chmodScript', + 'processhtml:dist' ]); }); diff --git a/app.config.templ/anvil-config.js b/app.config.templ/anvil-config.js new file mode 100644 index 0000000..bbd8745 --- /dev/null +++ b/app.config.templ/anvil-config.js @@ -0,0 +1,6 @@ +module.exports = { + issuer: '<%=AUTH_SERVER%>', + client_id: '<%=CLIENT_ID%>', + app_server: '<%=APP_SERVER%>', + display: '<%=AUTH_DISPLAY%>' +} \ No newline at end of file diff --git a/register_with_anvil_connect.sh b/app.config.templ/register_with_anvil_connect.sh similarity index 100% rename from register_with_anvil_connect.sh rename to app.config.templ/register_with_anvil_connect.sh diff --git a/app/callback_popup.html b/app/callback_popup.html index 6b1b774..255ff6a 100644 --- a/app/callback_popup.html +++ b/app/callback_popup.html @@ -1,67 +1,17 @@ - - - - - + + - - - - - + + + + +
diff --git a/app/rp.html b/app/rp.html index d56a57d..6b0449f 100644 --- a/app/rp.html +++ b/app/rp.html @@ -1,7 +1,3 @@ - - - - @@ -10,16 +6,11 @@ - - - diff --git a/app/scripts/app.js b/app/scripts/app.js index 58267e5..014cba1 100644 --- a/app/scripts/app.js +++ b/app/scripts/app.js @@ -6,6 +6,7 @@ require('angular-resource') require('angular-route') require('angular-sanitize') require('angular-touch') +var anvilConfig = require('../../app.config/anvil-config') var bows = require('bows') @@ -28,16 +29,13 @@ angular ]) .config(function ($locationProvider, $routeProvider, AnvilProvider) { + var auth_callback_route = '/' + 'callback_' + anvilConfig.display; // CONFIGURE ANVIL CONNECT - AnvilProvider.configure({ - issuer: '<%=AUTH_SERVER%>', - client_id: '<%=CLIENT_ID%>', - //redirect_uri: 'http://localhost:9000/callback.html', - redirect_uri: '<%=APP_SERVER%>/<%=APP_AUTH_CALLBACK%>', - display: '<%=AUTH_DISPLAY%>' // , - // scope: 'realm email' - }); + AnvilProvider.configure( + angular.merge({ + redirect_uri: anvilConfig.app_server + auth_callback_route + }, anvilConfig)); $locationProvider.html5Mode(true); $locationProvider.hashPrefix = '!'; @@ -70,10 +68,10 @@ angular }) // HANDLE CALLBACK (REQUIRED BY FULL PAGE NAVIGATION ONLY) - .when('/<%= APP_AUTH_CALLBACK %>', { + .when(auth_callback_route, { resolve: { session: function ($location, Anvil) { - log.debug('/<%= APP_AUTH_CALLBACK %>.resolve.session:', $location) + log.debug('' + auth_callback_route + '.resolve.session:', $location) if ($location.hash()) { return Anvil.promise.authorize().then( // handle successful authorization @@ -82,7 +80,7 @@ angular // $location.url( dest || '/'); did not react for me // there may be solutions with scope apply but this seems // to work fine, although this may not be the best solution. - console.log('/<%= APP_AUTH_CALLBACK %> authorize() succeeded, destination=', dest) + console.log('' + auth_callback_route + ' authorize() succeeded, destination=', dest) $location.url(dest || '/') }, diff --git a/app/scripts/callback_popup.js b/app/scripts/callback_popup.js new file mode 100644 index 0000000..da4c1b3 --- /dev/null +++ b/app/scripts/callback_popup.js @@ -0,0 +1,70 @@ +'use strict' +var bows = require('bows') +var Q = require('q-xhr')(window.XMLHttpRequest, require('q')) +var Anvil = require('anvil-connect-js').default +var anvilConfig = require('../../app.config/anvil-config') + +function copy(dst, src) { + for (var prop in src) { + if (src.hasOwnProperty(prop)) + dst[prop] = obj[prop]; + } + return dst; +} + +window.addEventListener('load', function () { + var log = bows('popup callback') + + Anvil.init( + copy( + { + scope: 'realm' + }, + anvilConfig), + { + http: { + request: function (config) { + return Q.xhr(config) + }, + getData: function (response) { + return response.data + } + } + }); + + function show (id) { + document.getElementById('signing_in').style.display = 'none' + document.getElementById(id).style.display = 'block' + } + + var pageUrl = window.location.href + , opener = window.opener + , pageOrigin + if (opener) { + pageOrigin = opener.location.origin; + } else { + log.debug("No opener for window: ", window.location) + } + + log.debug("load callback") + log.debug("pageUrl=" , pageUrl); + log.debug("pageOrigin=" , pageOrigin); + log.debug("opener=" , opener); + if (opener) { + opener.postMessage(pageUrl, pageOrigin); + } else { + var fragment = Anvil.getUrlFragment(pageUrl) + var response = Anvil.parseFormUrlEncoded(fragment) + Anvil.promise.callback(response).then( + function (result) { + log.info("Anvil.callback succeeded: ", result) + show('signed_in') + window.close(); + }, + function (fault) { + log.info("Anvil.callback failed: ", fault) + show('not_signed_in') + } + ) + } +}); \ No newline at end of file diff --git a/app/scripts/rp.js b/app/scripts/rp.js index 1b90413..64730e5 100644 --- a/app/scripts/rp.js +++ b/app/scripts/rp.js @@ -1,18 +1,24 @@ 'use strict' var bows = require('bows') -var Q = require('q') +var Q = require('q-xhr')(window.XMLHttpRequest, require('q')) window.Anvil = require('anvil-connect-js').default // window allows access from setTimeout below. +var anvilConfig = require('../../app.config/anvil-config') var log = bows('rp.html') -Anvil.init({ - issuer: '<%=AUTH_SERVER%>', - client_id: '<%= CLIENT_ID %>', - redirect_uri: '<%=APP_SERVER%>/rp.html', - display: 'page', - scope: 'realm' -}, { +function copy(dst, src) { + for (var prop in src) { + if (src.hasOwnProperty(prop)) + dst[prop] = src[prop]; + } + return dst; +} + +Anvil.init(copy({ + redirect_uri: anvilConfig.app_server + '/rp.html', + scope: 'realm' + }, anvilConfig), { http: { request: function (config) { return Q.xhr(config) diff --git a/package.json b/package.json index e6792a0..528360e 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "anvil-connect-js": "^0.2.1", "bows": "^1.4.8", "q": "^1.4.1", + "q-xhr": "^1.0.0", "text-encoder-lite": "^1.0.0" }, "devDependencies": { @@ -40,7 +41,9 @@ "grunt-serve-build": "grunt serve_dist", "clean": "grunt clean", "clean-all": "grunt clean && rimraf bower_components && rimraf jspm/jspm_packages && rimraf node_modules", - "watchify": "watchify app.browser/scripts/app.js -o app.browser/scripts/dev-bundle.js --verbose", - "watchify-rp": "watchify app.browser/scripts/rp.js -o app.browser/scripts/rp-dev-bundle.js --verbose" + "watchify": "npm run watchify-app | npm run watchify-rp | npm run watchify-popup", + "watchify-app": "watchify app/scripts/app.js -o app.browser/scripts/dev-bundle.js --verbose", + "watchify-rp": "watchify app/scripts/rp.js -o app.browser/scripts/rp-dev-bundle.js --verbose", + "watchify-popup": "watchify app/scripts/callback_popup.js -o app.browser/scripts/popup-dev-bundle.js --verbose" } } From c9d0f5e80bc952e5c620b799a698e70a66eadcd4 Mon Sep 17 00:00:00 2001 From: Henrich Kraemer Date: Tue, 2 Feb 2016 09:51:58 +0100 Subject: [PATCH 05/14] improve grunt -h --- Gruntfile.js | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 661f0b5..e83adae 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -248,13 +248,22 @@ module.exports = function (grunt) { } }); - grunt.registerTask('chmodScript', 'Makes script executable', function(target) { + grunt.registerTask('chmodScript', '(internal) Makes script executable. Used by config task', function(target) { var fs = require('fs'); fs.chmodSync('app.config/register_with_anvil_connect.sh', '755'); }); - grunt.registerTask('config', function (target) { + grunt.registerTask('build_browser', '(internal) Builds app in app.browser. Used by serve task.', function (target) { + grunt.log.writeln('Build app scripts in app.browser folder, matching auth server configuration in %s', grunt.config('auth_config')); + grunt.task.run([ + 'clean:browser', + 'copy:browser', + 'browserify:browser' + ]); + }); + + grunt.registerTask('config', 'Primary task: Generates config in app.config based on authconf.json', function (target) { grunt.log.writeln('Generating config in app.config folder, matching auth server configuration in %s', grunt.config('auth_config')); grunt.log.writeln('If not yet done register client using app.config/register_with_anvil_connect.sh. See README.md'); grunt.task.run([ @@ -264,17 +273,23 @@ module.exports = function (grunt) { ]); }); - grunt.registerTask('build_browser', function (target) { - grunt.log.writeln('Build app scripts in app.browser folder, matching auth server configuration in %s', grunt.config('auth_config')); + grunt.registerTask('serve', 'Primary task: Build then start a connect web server\n (serve for livereload app or serve:dist) ', function (target) { + if (target === 'dist') { + return grunt.task.run(['build', 'connect:dist']); + } + + grunt.log.writeln('Builds app, starts livereload server and opens browser.'); + grunt.log.writeln('NOTE: also start `npm run watchify` to rebuild the browserify bundles on changes.'); + grunt.task.run([ - 'clean:browser', - 'copy:browser', - 'browserify:browser' + 'build_browser', + 'connect:livereload', + 'watch' ]); }); - grunt.registerTask('build', function (target) { - grunt.log.writeln('Build app in dist folder, matching auth server configuration in %s', grunt.config('auth_config')); + grunt.registerTask('build', 'Builds app in /dist folder', function (target) { + grunt.log.writeln('** Build app in dist folder, matching auth server configuration in %s', grunt.config('auth_config')); grunt.task.run([ 'build_browser', 'clean:dist', @@ -286,24 +301,10 @@ module.exports = function (grunt) { ]); }); - grunt.registerTask('serve_dist', 'serve already build files from dist', function (target) { + grunt.registerTask('serve_dist', 'Starts server on /dist', function (target) { grunt.task.run([ 'connect:dist' ]); }); - - grunt.registerTask('serve', 'Compile then start a connect web server', function (target) { - if (target === 'dist') { - return grunt.task.run(['build', 'connect:dist']); - } - - grunt.task.run([ - 'build_browser', - 'connect:livereload', - 'watch' - ]); - }); - - }; From 6d21201ecb3d666682142586bf641d0a3a624ceb Mon Sep 17 00:00:00 2001 From: Henrich Kraemer Date: Tue, 2 Feb 2016 12:38:56 +0100 Subject: [PATCH 06/14] Eliminate bower usage and cleanup Forked webcrypto-shim to add package.json so it can be consumed: `npm install --save-dev henrjk/webcrypto-shim#add-package-json` Remove no longer used script replace-client-id --- .bowerrc | 3 --- Gruntfile.js | 26 ++++++++++++------------ app/callback_popup.html | 8 ++++---- app/index.html | 14 ++++++------- app/rp.html | 8 ++++---- bin/replace-client-id.sh | 44 ---------------------------------------- bower.json | 11 ---------- package.json | 8 ++++++-- 8 files changed, 34 insertions(+), 88 deletions(-) delete mode 100644 .bowerrc delete mode 100755 bin/replace-client-id.sh delete mode 100644 bower.json diff --git a/.bowerrc b/.bowerrc deleted file mode 100644 index 69fad35..0000000 --- a/.bowerrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "directory": "bower_components" -} diff --git a/Gruntfile.js b/Gruntfile.js index e83adae..c752412 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -20,7 +20,7 @@ module.exports = function (grunt) { // Configurable paths for the application var appConfig = { - app: require('./bower.json').appPath || 'app', + app: 'app', }; var authConfig = @@ -210,26 +210,26 @@ module.exports = function (grunt) { dist: { files: { 'dist/scripts/app-bundle.min.js': [ - 'bower_components/es5-shim/es5-shim.js', - 'bower_components/json3/lib/json3.min.js', - 'bower_components/promiz/promiz.js', - 'bower_components/webcrypto-shim/webcrypto-shim.js', + 'node_modules/es5-shim/es5-shim.js', + 'node_modules/json3/lib/json3.min.js', + 'node_modules/promiz/promiz.js', + 'node_modules/webcrypto-shim/webcrypto-shim.js', 'node_modules/text-encoder-lite/index.js', 'dist/scripts/app-bundle.js' ], 'dist/scripts/rp-bundle.min.js': [ - 'bower_components/es5-shim/es5-shim.js', - 'bower_components/json3/lib/json3.min.js', - 'bower_components/promiz/promiz.js', - 'bower_components/webcrypto-shim/webcrypto-shim.js', + 'node_modules/es5-shim/es5-shim.js', + 'node_modules/json3/lib/json3.min.js', + 'node_modules/promiz/promiz.js', + 'node_modules/webcrypto-shim/webcrypto-shim.js', 'node_modules/text-encoder-lite/index.js', 'dist/scripts/rp-bundle.js' ], 'dist/scripts/popup-bundle.min.js': [ - 'bower_components/es5-shim/es5-shim.js', - 'bower_components/json3/lib/json3.min.js', - 'bower_components/promiz/promiz.js', - 'bower_components/webcrypto-shim/webcrypto-shim.js', + 'node_modules/es5-shim/es5-shim.js', + 'node_modules/json3/lib/json3.min.js', + 'node_modules/promiz/promiz.js', + 'node_modules/webcrypto-shim/webcrypto-shim.js', 'node_modules/text-encoder-lite/index.js', 'dist/scripts/popup-bundle.js' ] diff --git a/app/callback_popup.html b/app/callback_popup.html index 255ff6a..57e4f03 100644 --- a/app/callback_popup.html +++ b/app/callback_popup.html @@ -3,12 +3,12 @@ - - + + diff --git a/app/index.html b/app/index.html index 4d082d3..b400606 100644 --- a/app/index.html +++ b/app/index.html @@ -5,7 +5,7 @@ - + @@ -32,19 +32,19 @@

Your Logo

- - + + - + diff --git a/app/rp.html b/app/rp.html index 6b0449f..1768b09 100644 --- a/app/rp.html +++ b/app/rp.html @@ -2,12 +2,12 @@ - - + + diff --git a/bin/replace-client-id.sh b/bin/replace-client-id.sh deleted file mode 100755 index 257eebe..0000000 --- a/bin/replace-client-id.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -echo This is no longer working. Use grunt instead. See README.md -exit - -client_id=$1 -proto=$2 -host=$3 -port=$4 -server_host=$5 - -# sed with in place editing is not portable between OSX and Linux (see http://stackoverflow.com/questions/5694228/sed-in-place-flag-that-works-both-on-mac-bsd-and-linux) -# Changing a file, see pitfall http://mywiki.wooledge.org/BashPitfalls#cat_file_.7C_sed_s.2Ffoo.2Fbar.2F_.3E_file and -# http://unix.stackexchange.com/a/38106 used for a function to chomp. -sponge() { - local file=$1 - local line lines - while IFS= read -r line; do - lines+=( "$line" ) - done - printf '%s\n' "${lines[@]}" > "$file" -} - -sed_app() { - local file=$1 - sed s,APP_PROTO,"$proto",g "$file" | sed s,APP_HOST,"$host",g | sed s,APP_HOST,"$host",g -} - - -if [ "$#" -eq 4 ]; then - sed s,CLIENT_ID,"$client_id",g app/rp.html | sponge app/rp.html - sed s,CLIENT_ID,"$client_id",g app/scripts/app.js | sponge app/scripts/app.js - sed s,APP_HOST,"$host",g app/rp.html | sponge app/rp.html - sed s,APP_HOST,"$host",g app/scripts/app.js | sponge app/scripts/app.js - sed s,APP_PORT,"$port",g app/rp.html | sponge app/rp.html - sed s,APP_PORT,"$port",g app/scripts/app.js | sponge app/scripts/app.js - sed s,APP_HOST,"$host",g Gruntfile.js | sponge Gruntfile.js - sed s,APP_PORT,"$port",g Gruntfile.js | sponge Gruntfile.js - sed s,SERVER_HOST,"$server_host",g app/rp.html | sponge app/rp.html - sed s,SERVER_HOST,"$server_host",g app/scripts/app.js | sponge app/scripts/app.js - sed s,SERVER_HOST,"$server_host",g app/index.html | sponge app/index.html -else - echo Please pass client id, host, port, and server host as arguments. for example: bin/replace-client-id.sh 6d327f50-0aa7-4b0a-9bab-b558d9027e27 my-server.com 9000 https://connect.anvil.io -fi diff --git a/bower.json b/bower.json deleted file mode 100644 index 53970ab..0000000 --- a/bower.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "anvil-connect-angularjs-example", - "version": "0.0.0", - "dependencies": { - "json3": "~3.3.1", - "es5-shim": "~3.1.0", - "bootstrap": "~3.1.1", - "webcrypto-shim": "*" - }, - "appPath": "app" -} diff --git a/package.json b/package.json index 528360e..4cccadd 100644 --- a/package.json +++ b/package.json @@ -10,13 +10,17 @@ "angular-sanitize": "^1.4.8", "angular-touch": "^1.4.8", "anvil-connect-js": "^0.2.1", + "bootstrap": "^3.3.6", "bows": "^1.4.8", + "es5-shim": "^4.5.2", + "json3": "^3.3.2", + "promiz": "^1.0.5", "q": "^1.4.1", "q-xhr": "^1.0.0", - "text-encoder-lite": "^1.0.0" + "text-encoder-lite": "^1.0.0", + "webcrypto-shim": "henrjk/webcrypto-shim#add-package-json" }, "devDependencies": { - "bower": "^1.7.2", "browserify": "^13.0.0", "connect-modrewrite": "^0.7.6", "grunt": "^0.4.1", From 21af662842e98f9262bfa3327e65027773bc6eb1 Mon Sep 17 00:00:00 2001 From: Henrich Kraemer Date: Tue, 2 Feb 2016 20:23:35 +0100 Subject: [PATCH 07/14] Some rp changes. --- app/scripts/rp.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/scripts/rp.js b/app/scripts/rp.js index 64730e5..0ec534b 100644 --- a/app/scripts/rp.js +++ b/app/scripts/rp.js @@ -29,7 +29,9 @@ Anvil.init(copy({ } }); -Anvil.promise.deserialize(); +Anvil.promise.deserialize().catch(function (err) { + Anvil.reset() +}); var response = (location.hash) ? Anvil.parseFormUrlEncoded(location.hash.substring(1)) : {}; @@ -42,6 +44,9 @@ if (location.hash) { }, function failure (fault) { log.info('RP CALLBACK FAILURE', fault); + log.debug('location', location) + var dest = Anvil.destination() + log.debug('dest=',dest) }) }) } From 688c8e8b82b1258fc195b4cd587239ad595c8918 Mon Sep 17 00:00:00 2001 From: Henrich Kraemer Date: Wed, 3 Feb 2016 09:43:43 +0100 Subject: [PATCH 08/14] register_with_anvil_connect.sh improve nvl error reporting The script issues a lengthy nvl client:register command. Before when errors occurred one could not see the nvl output. Changed the script to write stdout and stderr of the nvl client:register command to file `nvl.log`. One can see further details in that file which should help quite a bit although relevant messages might be quite buried. Anyways this should be good enough! --- .gitignore | 1 + app.config.templ/register_with_anvil_connect.sh | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 5c420f7..3a057c4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ node_modules .tmp .sass-cache authconf.json +nvl.log \ No newline at end of file diff --git a/app.config.templ/register_with_anvil_connect.sh b/app.config.templ/register_with_anvil_connect.sh index 104862a..a2deac6 100755 --- a/app.config.templ/register_with_anvil_connect.sh +++ b/app.config.templ/register_with_anvil_connect.sh @@ -10,12 +10,15 @@ fail() { echo "$message" >&2 echo "You may need to login with 'nvl login'" >&2 echo "Perhaps nvl is not yet setup? Consult connect-cli getting started documentation" >&2 + echo "" >&2 + echo "For other problems consult the output of the nvl command in nvl.log" >&2 exit 2 } echo "Registering this client with <%= ISSUER_NAME %>" +echo "Command output written to nvl.log" -out=$(nvl client:register \ +nvl client:register \ --issuer "<%= ISSUER_NAME %>" \ --trusted \ --name "Angular example with <%= AUTH_DISPLAY %> for <%= APP_SERVER %>" \ @@ -28,7 +31,8 @@ out=$(nvl client:register \ --redirect-uri "<%= APP_SERVER %>/<%= APP_AUTH_CALLBACK %>" \ --redirect-uri "<%= APP_SERVER %>/rp.html" \ --post-logout-redirect-uri "<%= APP_SERVER %>/" \ - --post-logout-redirect-uri "<%= APP_SERVER %>/") + --post-logout-redirect-uri "<%= APP_SERVER %>/" > nvl.log 2>&1 + # duplication of --post-logout-redirect-uri see https://github.com/anvilresearch/connect-cli/issues/70 @@ -40,7 +44,7 @@ fi # From OS X: # $ echo "$out" | grep "_id" | grep -o -E '([[:xdigit:]]*-){1,10}[[:xdigit:]]*' # ec8262ae-28d5-4943-8237-d8145042c3e0 -client_id=$(echo "$out" | grep "_id" | grep -o -E '([[:xdigit:]]*-){1,10}[[:xdigit:]]*') +client_id=$(cat nvl.log | grep "_id" | grep -o -E '([[:xdigit:]]*-){1,10}[[:xdigit:]]*') [ -n "$client_id" ] || fail From 52077b21239973bf52c3335f9bdbbee1b09d6dc2 Mon Sep 17 00:00:00 2001 From: Henrich Kraemer Date: Wed, 3 Feb 2016 12:24:31 +0100 Subject: [PATCH 09/14] Improved dev workflow Fixed issues: * npm run watchify would not provide source maps in bundles * live reload not working when config changed --- Gruntfile.js | 3 ++- package.json | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index c752412..7a2bc6b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -51,7 +51,8 @@ module.exports = function (grunt) { files: [ 'app/{,*/}*.html', 'app/styles/{,*/}*.css', - 'app/scripts/{,*/}*.js' + 'app/scripts/{,*/}*.js', + 'app.config/{,*/}*.js' ], tasks: ['newer:copy:browser'] } diff --git a/package.json b/package.json index 4cccadd..72cb193 100644 --- a/package.json +++ b/package.json @@ -46,8 +46,8 @@ "clean": "grunt clean", "clean-all": "grunt clean && rimraf bower_components && rimraf jspm/jspm_packages && rimraf node_modules", "watchify": "npm run watchify-app | npm run watchify-rp | npm run watchify-popup", - "watchify-app": "watchify app/scripts/app.js -o app.browser/scripts/dev-bundle.js --verbose", - "watchify-rp": "watchify app/scripts/rp.js -o app.browser/scripts/rp-dev-bundle.js --verbose", - "watchify-popup": "watchify app/scripts/callback_popup.js -o app.browser/scripts/popup-dev-bundle.js --verbose" + "watchify-app": "watchify app/scripts/app.js --debug -o app.browser/scripts/dev-bundle.js --verbose", + "watchify-rp": "watchify app/scripts/rp.js --debug -o app.browser/scripts/rp-dev-bundle.js --verbose", + "watchify-popup": "watchify app/scripts/callback_popup.js --debug -o app.browser/scripts/popup-dev-bundle.js --verbose" } } From beff011fb8009cab2e292b360f58093c0fd4ee7a Mon Sep 17 00:00:00 2001 From: Henrich Kraemer Date: Wed, 3 Feb 2016 12:26:03 +0100 Subject: [PATCH 10/14] Fixed issue using popup authorization. This requires popup anvil-connect-js version 0.2.4 for refresh to work. --- app.config.templ/anvil-config.js | 3 ++- app/scripts/anvil-connect-angular.js | 17 ++++++++++++++--- app/scripts/app.js | 4 ++-- app/scripts/callback_popup.js | 2 +- package.json | 2 +- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/app.config.templ/anvil-config.js b/app.config.templ/anvil-config.js index bbd8745..b8a0a86 100644 --- a/app.config.templ/anvil-config.js +++ b/app.config.templ/anvil-config.js @@ -2,5 +2,6 @@ module.exports = { issuer: '<%=AUTH_SERVER%>', client_id: '<%=CLIENT_ID%>', app_server: '<%=APP_SERVER%>', - display: '<%=AUTH_DISPLAY%>' + display: '<%=AUTH_DISPLAY%>', + callback: '<%=APP_AUTH_CALLBACK%>' } \ No newline at end of file diff --git a/app/scripts/anvil-connect-angular.js b/app/scripts/anvil-connect-angular.js index 2631a3e..d15c0eb 100644 --- a/app/scripts/anvil-connect-angular.js +++ b/app/scripts/anvil-connect-angular.js @@ -1,7 +1,11 @@ 'use strict' -var Anvil = require('anvil-connect-js').default require('angular') +var bows = require('bows') + +var log = bows('ac-angular') + +var Anvil = require('anvil-connect-js').default function init (providerOptions, $http, $q, $location, $window, $document) { Anvil.init(providerOptions, { @@ -42,9 +46,12 @@ angular.module('anvil', []) function requireAuthentication ($location, Anvil) { if (!Anvil.isAuthenticated()) { - Anvil.promise.authorize() + return Anvil.promise.authorize() + .catch(function (err) { + log.debug('requireAuthentication: authorize() failed', err) + return false + }) } - return Anvil.session } @@ -58,6 +65,10 @@ angular.module('anvil', []) return ['$location', 'Anvil', function requireScope ($location, Anvil) { if (!Anvil.isAuthenticated()) { return Anvil.promise.authorize() + .catch(function (err) { + log.debug('requireScope: authorize() failed', err) + return false + }) } else if (Anvil.session.access_claims.scope.indexOf(scope) === -1) { $location.path(fail) return false diff --git a/app/scripts/app.js b/app/scripts/app.js index 014cba1..baeb9f7 100644 --- a/app/scripts/app.js +++ b/app/scripts/app.js @@ -29,7 +29,7 @@ angular ]) .config(function ($locationProvider, $routeProvider, AnvilProvider) { - var auth_callback_route = '/' + 'callback_' + anvilConfig.display; + var auth_callback_route = '/' + anvilConfig.callback; // CONFIGURE ANVIL CONNECT AnvilProvider.configure( @@ -80,7 +80,7 @@ angular // $location.url( dest || '/'); did not react for me // there may be solutions with scope apply but this seems // to work fine, although this may not be the best solution. - console.log('' + auth_callback_route + ' authorize() succeeded, destination=', dest) + log.debug('' + auth_callback_route + ' authorize() succeeded, destination=', dest) $location.url(dest || '/') }, diff --git a/app/scripts/callback_popup.js b/app/scripts/callback_popup.js index da4c1b3..757f05f 100644 --- a/app/scripts/callback_popup.js +++ b/app/scripts/callback_popup.js @@ -7,7 +7,7 @@ var anvilConfig = require('../../app.config/anvil-config') function copy(dst, src) { for (var prop in src) { if (src.hasOwnProperty(prop)) - dst[prop] = obj[prop]; + dst[prop] = src[prop]; } return dst; } diff --git a/package.json b/package.json index 72cb193..a98b507 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "angular-route": "^1.4.8", "angular-sanitize": "^1.4.8", "angular-touch": "^1.4.8", - "anvil-connect-js": "^0.2.1", + "anvil-connect-js": "^0.2.4", "bootstrap": "^3.3.6", "bows": "^1.4.8", "es5-shim": "^4.5.2", From e093642eaba83d74c0c4c645f66de993398e6889 Mon Sep 17 00:00:00 2001 From: Henrich Kraemer Date: Wed, 3 Feb 2016 15:08:36 +0100 Subject: [PATCH 11/14] Update README to describe some of the recent changes. --- README.md | 106 +++++++++++++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 8080224..31989a8 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,6 @@ $ git checkout webcrypto ## These steps are described here: https://github.com/henrjk/connect-js/tree/webcrypto $ npm install -$ jspm install $ npm run test ``` @@ -44,7 +43,7 @@ $ cd connect-example-angularjs $ git checkout use-npm ``` -No go to the directory where connect-js was cloned to and issue these commands: +Now go to the directory where connect-js was cloned to and issue these commands: ```console connect-js dev (webcrypto)$ npm link @@ -74,22 +73,19 @@ connect-example-angularjs dev (use-npm)$ npm link anvil-connect-js connect-example-angularjs dev (use-npm)$ npm install # this takes a few minutes... - - -connect-example-angularjs dev (use-npm)$ bower install -# this goes much quicker... ``` Now you will have to come up with a authconf.json file in the projects -root folder which matches your server. This is described below although I have -not recently verified these steps. Here is an outline of the steps: +root folder which matches your server. This is described below. These +steps looks still valid but have not been recently tried. +Here is an outline of the steps: * Determine correct `authconf.json` file and place it in connect-example-angularjs root folder. -* grunt build -* login to the server (nvl login) -* execute script generated by grunt build: `./dist/register_with_anvil_connect.sh` +* Generate configuration files based on authconf.json: `grunt config` +* login to the server with `nvl login` +* execute script generated by grunt build: `./app.config/register_with_anvil_connect.sh` * Observe the assigned client ID and update `authconf.json` accordingly. -* grunt serve +* Start the app in dev mode via `grunt serve` This should build the browser app configured correctly for the client registration in folder app.browser, browserify the dependencies and the open a browser @@ -130,14 +126,14 @@ cp authconf.dev.localhost.json authconf.json The Anvil Authentication server recognizes clients by an id which is generated when they are registered. Therefore the starting point is not yet final as the -id in there must be changed. To help with ensure that the client registration +id in there must be changed. To help to ensure that the client registration matches what the generated angular app uses, a registration script is generated -in `dist/register_with_anvil_connect.sh`. +in `app.config/register_with_anvil_connect.sh`. -First generate the script by: +First generate the configuration files based on authconf.json: ```console -grunt build +grunt config ``` Next login to the cli. @@ -152,11 +148,15 @@ Warning: you are communicating over plain text. You have been successfully logged in to localhost:3000 ``` -Use the generated `dist/register_with_anvil_connect.sh` as follows in the root directory of your Anvil Connect Authentication -server: +After the nvl login one can use the generated +`dist/register_with_anvil_connect.sh` to register your app with the client. +However you may want to review the script and change argument values for +--name and perhaps --logo-uri. + +To register the client: ```console -dev$ ./dist/register_with_anvil_connect.sh +dev$ ./config/register_with_anvil_connect.sh Registering this client with localhost-3000 Succeeded. Define CLIENT_ID as follows in authconf.json: @@ -170,55 +170,58 @@ Define CLIENT_ID as follows in authconf.json: The id shown will be unique to your authentication server. Replace the existing `CLIENT_ID` in `authconf.json` with the one you see in your output. +When you build the app the values in authConfig will be used to ensure that the +generated app uses matching configuration values. For js files which +require file app.config/anvil-config.js the config will be incorporated in the +bundles produced by browserify. + ## Run with angular app served by grunt serve -In this scenario we are using a simple build server via grunt. +In this scenario we are using a simple development server via grunt. -TODO: ```console dev$ grunt serve Using authconf.json Running "serve" task +Builds app, starts livereload server and opens browser. +NOTE: also start `npm run watchify` to rebuild the browserify bundles on changes. -Running "clean:server" (clean) task -Cleaning .tmp...OK -Cleaning dist...OK - -Running "concurrent:server" (concurrent) task - - Using authconf.json +Running "build_browser" task +Build app scripts in app.browser folder, matching auth server configuration in authconf.json - Running "copy:styles" (copy) task - Copied 1 files +Running "clean:browser" (clean) task +>> 1 path cleaned. - Done, without errors. +Running "copy:browser" (copy) task +Copied 1 file +Running "browserify:browser" (browserify) task +>> Bundle app.browser/scripts/popup-dev-bundle.js created. +>> Bundle app.browser/scripts/rp-dev-bundle.js created. +>> Bundle app.browser/scripts/dev-bundle.js created. - Execution Time (2015-07-10 19:49:12 UTC) - loading tasks 20ms ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 31% - copy:styles 41ms ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 63% - Total 65ms - - Using authconf.json +Running "connect:livereload" (connect) task +Started connect web server on http://localhost:9000 - Running "copy:dist" (copy) task - Created 5 directories, copied 10 files +Running "watch" task +Waiting... +>> File "app.config/anvil-config.js" changed. +Using authconf.json - Done, without errors. +Running "newer:copy:browser" (newer) task +No newer files to process. +Done, without errors. - Execution Time (2015-07-10 19:49:12 UTC) - loading tasks 18ms ▇▇▇▇▇ 9% - copy:dist 183ms ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 91% - Total 202ms -Running "connect:livereload" (connect) task -Started connect web server on http://localhost:9000 +Execution Time (2016-02-03 11:38:31 UTC) +loading tasks 12ms ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 43% +newer:copy:browser 15ms ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 54% +Total 28ms -Running "watch" task -Waiting... +Completed in 2.858s at Wed Feb 03 2016 12:38:31 GMT+0100 (CET) - Waiting... ``` As a result the browser should open the home page like this: @@ -227,10 +230,17 @@ As a result the browser should open the home page like this: When changes are made to the app this should readily refresh the browser. +However for the Browserify bundles to be rebuild on changes you need to also run: +``` +npm run watchify +``` + These pages should look the same as in the docker use case which shows some more pages except that these are running under `http://localhost:9000` instead of `http://boot2docker:9000`. ## Run with angular app served by docker +**Note:** This section was last updated in July 2015. + In this scenario nginx serves the angular app running inside a docker container. If you have not followed the steps in section **Prerequisites** do this first. From 42307fab200dd9884a718c4fbce859e72b7cef5f Mon Sep 17 00:00:00 2001 From: Henrich Kraemer Date: Wed, 3 Feb 2016 22:48:00 +0100 Subject: [PATCH 12/14] OpenIDConnect session management The changes here use the 'not-authenticated' event introduced in anvil-connect-js 0.2.5 as well as the 'authenticated' event. Also added some rp logging and calling Anvil#checkSession every 5 seconds. --- app/index.html | 4 ++-- app/scripts/anvil-connect-angular.js | 9 +++++++++ app/scripts/rp.js | 10 +++++----- package.json | 2 +- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/app/index.html b/app/index.html index b400606..4716eef 100644 --- a/app/index.html +++ b/app/index.html @@ -43,8 +43,8 @@

Your Logo

- - --> + diff --git a/app/scripts/anvil-connect-angular.js b/app/scripts/anvil-connect-angular.js index d15c0eb..e296ce4 100644 --- a/app/scripts/anvil-connect-angular.js +++ b/app/scripts/anvil-connect-angular.js @@ -98,6 +98,15 @@ angular.module('anvil', []) '$document', '$window', function ($q, $http, $rootScope, $location, $document, $window) { init(null, $http, $q, $location, $window, $document) + Anvil.on('not-authenticated', function(ev) { + log.debug('not-authenticated event: calling $rootScope.$apply()', ev) + $location.url('/') + $rootScope.$apply() + }) + Anvil.on('authenticated', function(ev) { + log.debug('authenticated event: calling $rootScope.$apply()', ev) + $rootScope.$apply() + }) return Anvil }] diff --git a/app/scripts/rp.js b/app/scripts/rp.js index 0ec534b..ad578af 100644 --- a/app/scripts/rp.js +++ b/app/scripts/rp.js @@ -5,7 +5,7 @@ window.Anvil = require('anvil-connect-js').default // window allows access from setTimeout below. var anvilConfig = require('../../app.config/anvil-config') -var log = bows('rp.html') +var log = bows('rp.js') function copy(dst, src) { for (var prop in src) { @@ -36,6 +36,7 @@ Anvil.promise.deserialize().catch(function (err) { var response = (location.hash) ? Anvil.parseFormUrlEncoded(location.hash.substring(1)) : {}; if (location.hash) { + log.debug('Loading rp.js: parsed hash=', response) Anvil.promise.prepareAuthorization().then( function () { Anvil.promise.callback(response).then( function success (session) { @@ -51,13 +52,12 @@ if (location.hash) { }) } -// start checking the session every 30 seconds +// start checking the session every 5 seconds function setTimer() { - var timer = setInterval("Anvil.checkSession('op')", 30*1000); + var timer = setInterval("Anvil.checkSession('op')", 5*1000); } - // listen for changes to OP browser state function receiveMessage(event) { if (event.origin !== Anvil.issuer) { @@ -71,7 +71,7 @@ function receiveMessage(event) { // SESSION STATE IS THE SAME if (event.data === 'unchanged') { - log.info('session state: unchanged'); + ; // do nothing } // SESSION STATE HAS CHANGED diff --git a/package.json b/package.json index a98b507..a01e59f 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "angular-route": "^1.4.8", "angular-sanitize": "^1.4.8", "angular-touch": "^1.4.8", - "anvil-connect-js": "^0.2.4", + "anvil-connect-js": "^0.2.5", "bootstrap": "^3.3.6", "bows": "^1.4.8", "es5-shim": "^4.5.2", From 8fe32d9dacc07e1407e4b98e64dde8f1f9585fab Mon Sep 17 00:00:00 2001 From: Henrich Kraemer Date: Fri, 1 Apr 2016 00:31:22 +0200 Subject: [PATCH 13/14] Update Readme --- README.md | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 31989a8..241f2a5 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,11 @@ To get the webcrypto code for testing the fork is used: ```console $ # create or got to some suited directory then -$ git clone https://github.com/henrjk/connect-js.git +$ git clone https://github.com/anvilresearch/connect-js.git $ cd connect-js -$ git checkout webcrypto +$ git checkout webcrypto-api -## These steps are described here: https://github.com/henrjk/connect-js/tree/webcrypto +## These steps are described here: https://github.com/anvilresearch/connect-js/tree/webcrypto-api $ npm install $ npm run test ``` @@ -38,20 +38,30 @@ in the previous step ```console $ # create or got to some suited directory. This could be the same as above. -$ git clone https://github.com/henrjk/connect-example-angularjs.git +$ git clone https://github.com/anvilresearch/connect-example-angularjs.git $ cd connect-example-angularjs -$ git checkout use-npm +$ git checkout henrjk-use-npm ``` Now go to the directory where connect-js was cloned to and issue these commands: ```console -connect-js dev (webcrypto)$ npm link +connect-js dev (webcrypto-api)$ npm link -> anvil-connect-js@0.2.0 prepublish /Users/dev/code/work/connect-js-fork/connect-js +> anvil-connect-js@0.2.6 postinstall /Users/dev/code/connect-js +> jspm install + + Looking up npm:babel-runtime +... about 40 more lines of output ... +ok Up to date - babel-runtime as npm:babel-runtime@^5.8.24 (5.8.34) +ok Install tree has no forks. + +ok Install complete. + +> anvil-connect-js@0.2.6 prepublish /Users/dev/code/connect-js > npm run build -> anvil-connect-js@0.2.0 build /Users/dev/code/work/connect-js-fork/connect-js +> anvil-connect-js@0.2.6 build /Users/dev/code/connect-js > grunt Running "clean:dist" (clean) task @@ -60,15 +70,15 @@ Running "clean:dist" (clean) task Running "babel:compile" (babel) task Done, without errors. -/Users/dev/.nvm/versions/node/v0.12.6/lib/node_modules/anvil-connect-js -> /Users/dev/code/work/connect-js-fork/connect-js -connect-js dev (webcrypto)$ -``` +/Users/dev/.nvm/versions/node/v0.12.6/lib/node_modules/anvil-connect-js -> /Users/dev/code/connect-js +/connect-js dev (webcrypto-api)$ +``` Then go back to the `connect-example-angularjs` directory and do the following: ```console -connect-js dev (webcrypto)$ cd ../connect-example-angularjs/ -connect-example-angularjs dev (use-npm)$ npm link anvil-connect-js +connect-js dev (webcrypto-api)$ cd ../connect-example-angularjs/ +connect-example-angularjs dev (henrjk-use-npm)$ npm link anvil-connect-js /Users/dev/code/work/connect-js-fork/connect-example-angularjs/node_modules/anvil-connect-js -> /Users/dev/.nvm/versions/node/v0.12.6/lib/node_modules/anvil-connect-js -> /Users/dev/code/work/connect-js-fork/connect-js connect-example-angularjs dev (use-npm)$ npm install From 8f5f0de1865325f0e328b993cb6f89f5131c02bc Mon Sep 17 00:00:00 2001 From: Henrich Kraemer Date: Fri, 1 Apr 2016 00:33:43 +0200 Subject: [PATCH 14/14] package.json script additions and fixes. 1. npm run clean-all was missing rimraf 2. npm run config can now be used to --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index a01e59f..275bf16 100644 --- a/package.json +++ b/package.json @@ -35,11 +35,13 @@ "grunt-processhtml": "^0.3.8", "grunt-shell": "^1.1.2", "load-grunt-tasks": "^3.4.0", + "rimraf": "^2.5.2", "serve-static": "^1.10.0", "time-grunt": "^1.2.2", "watchify": "^3.7.0" }, "scripts": { + "config": "grunt config", "dev": "grunt serve", "build": "grunt build", "grunt-serve-build": "grunt serve_dist",