From 46cac0bc8cf685560276c1dcefa903a7603deb57 Mon Sep 17 00:00:00 2001 From: bastimeyer Date: Sat, 6 Feb 2016 14:00:17 +0100 Subject: [PATCH] Reimplement the AuthService Use a local http server for the OAuth redirect instead of the NW.js based app:// protocol. Fixes #209 --- package.json | 25 +++-- src/app/controllers/UserAuthController.js | 19 ++-- src/app/nwjs/redirect.js | 35 ------- src/app/routes/UserAuthRoute.js | 5 +- src/app/services/AuthService.js | 117 +++++++++++----------- src/oauth-redirect.html | 51 ++++++++++ src/oauth.html | 16 --- src/oauth.json | 14 --- src/templates/user/UserAuth.hbs | 4 + 9 files changed, 143 insertions(+), 143 deletions(-) delete mode 100644 src/app/nwjs/redirect.js create mode 100644 src/oauth-redirect.html delete mode 100644 src/oauth.html delete mode 100644 src/oauth.json diff --git a/package.json b/package.json index e33cf8af2e..c5e5c8e7c1 100644 --- a/package.json +++ b/package.json @@ -59,17 +59,20 @@ "twitch-subscribe-url": "http://www.twitch.tv/{channel}/subscribe", "twitch-subscribe-edit": "http://www.twitch.tv/products/{channel}/ticket/edit", "twitch-subscribe-cancel": "http://www.twitch.tv/products/{channel}/ticket/edit?cancel_and_refund=true", - "twitch-oauth-base-uri": "https://api.twitch.tv/kraken/oauth2/authorize?response_type=token&client_id={client-id}&redirect_uri={redirect-uri}&scope={scope}", - "twitch-oauth-client-id": "phiay4sq36lfv9zu7cbqwz2ndnesfd8", - "twitch-oauth-redirect-uri": "app://livestreamer-twitch-gui/oauth.html", - "twitch-oauth-scope": [ - "user_read", - "user_blocks_read", - "user_blocks_edit", - "user_follows_edit", - "user_subscriptions", - "chat_login" - ], + "twitch-oauth": { + "server-port": 65432, + "base-uri": "https://api.twitch.tv/kraken/oauth2/authorize?response_type=token&client_id={client-id}&redirect_uri={redirect-uri}&scope={scope}", + "client-id": "phiay4sq36lfv9zu7cbqwz2ndnesfd8", + "redirect-uri": "http://localhost:{server-port}/redirect", + "scope": [ + "user_read", + "user_blocks_read", + "user_blocks_edit", + "user_follows_edit", + "user_subscriptions", + "chat_login" + ] + }, "enable-smoothscroll": true, "image-expiration-time": 60, "language_codes": { diff --git a/src/app/controllers/UserAuthController.js b/src/app/controllers/UserAuthController.js index 623db39659..a3bda495cf 100644 --- a/src/app/controllers/UserAuthController.js +++ b/src/app/controllers/UserAuthController.js @@ -23,12 +23,12 @@ define([ token: "", scope: function() { - return get( this, "auth.scope" ).join( ", " ); - }.property( "auth.scope" ), + return get( this, "auth.oauth.scope" ).join( ", " ); + }.property( "auth.oauth.scope" ), /** * 0 000: start - * 1 001: auth - login (window opened) + * 1 001: auth - login (server started) * 2 010: auth - failure * 3 011: auth - success (not used) * 4 100: token - show form @@ -64,10 +64,10 @@ define([ }.property( "loginStatus" ), - windowObserver: function() { - var authWindow = get( this, "auth.window" ); - set( this, "loginStatus", authWindow ? 1 : 0 ); - }.observes( "auth.window" ), + serverObserver: function() { + var authServer = get( this, "auth.server" ); + set( this, "loginStatus", authServer ? 1 : 0 ); + }.observes( "auth.server" ), resetProperties: function() { @@ -134,6 +134,11 @@ define([ self.retryTransition(); } }); + }, + + // abort sign in with username + password + "abort": function() { + get( this, "auth" ).abortSignin(); } } }); diff --git a/src/app/nwjs/redirect.js b/src/app/nwjs/redirect.js deleted file mode 100644 index d050ac2bdf..0000000000 --- a/src/app/nwjs/redirect.js +++ /dev/null @@ -1,35 +0,0 @@ -define( [ "nwjs/nwGui" ], function( nwGui ) { - - var reURI = /^([a-z]+):\/\/([\w-]+(?:\.[\w-]+)*)\/?/; - - var enabled = {}; - - /** - * @param {string} from - * @param {string} to - */ - function enable( from, to ) { - var src = reURI.exec( from ); - var dst = reURI.exec( to ); - - if ( !src || !dst ) { - throw new Error( "Invalid parameters" ); - } - - if ( enabled.hasOwnProperty( from ) ) { - if ( enabled[ from ].hasOwnProperty( to ) ) { - return; - } - } else { - enabled[ from ] = {}; - } - enabled[ from ][ to ] = true; - - nwGui.App.addOriginAccessWhitelistEntry( src[0], dst[1], dst[2], true ); - } - - return { - enable: enable - }; - -}); diff --git a/src/app/routes/UserAuthRoute.js b/src/app/routes/UserAuthRoute.js index 39390314a9..f2d000d7b3 100644 --- a/src/app/routes/UserAuthRoute.js +++ b/src/app/routes/UserAuthRoute.js @@ -17,10 +17,7 @@ define( [ "Ember" ], function( Ember ) { actions: { willTransition: function() { - var win = get( this, "auth.window" ); - if ( win ) { - win.close(); - } + this.controller.send( "abort" ); this.controller.resetProperties(); } } diff --git a/src/app/services/AuthService.js b/src/app/services/AuthService.js index 105ec417ad..8f4c97ca50 100644 --- a/src/app/services/AuthService.js +++ b/src/app/services/AuthService.js @@ -1,17 +1,17 @@ define([ "Ember", "nwjs/nwGui", - "nwjs/redirect", - "nwjs/cookies", + "nwjs/nwWindow", "utils/contains", - "json!root/oauth" + "utils/node/http/Server", + "file!root/oauth-redirect.html" ], function( Ember, nwGui, - redirect, - cookies, + nwWindow, contains, - OAuth + HttpServer, + OAuthResponseRedirect ) { var get = Ember.get; @@ -26,23 +26,26 @@ define([ store : Ember.inject.service(), config: alias( "metadata.config" ), - scope : alias( "config.twitch-oauth-scope" ), + oauth : alias( "config.twitch-oauth" ), session: null, - window: null, + server: null, url: function() { - var baseuri = get( this, "config.twitch-oauth-base-uri" ); - var clientid = get( this, "config.twitch-oauth-client-id" ); - var redirecturi = get( this, "config.twitch-oauth-redirect-uri" ); - var scope = get( this, "scope" ); + var baseuri = get( this, "oauth.base-uri" ); + var clientid = get( this, "oauth.client-id" ); + var serverport = get( this, "oauth.server-port" ); + var redirecturi = get( this, "oauth.redirect-uri" ); + var scope = get( this, "oauth.scope" ); + + redirecturi = redirecturi.replace( "{server-port}", String( serverport ) ); return baseuri .replace( "{client-id}", clientid ) .replace( "{redirect-uri}", encodeURIComponent( redirecturi ) ) .replace( "{scope}", scope.join( "+" ) ); - }.property( "config", "scope" ), + }.property( "config", "oauth.scope" ), init: function() { @@ -78,52 +81,63 @@ define([ }, /** - * Open OAuth window + * Open OAuth url in browser * @returns {Promise} */ signin: function() { - if ( get( this, "window" ) ) { + if ( get( this, "server" ) ) { return Promise.reject(); } var self = this; var defer = Promise.defer(); - function callback( hash ) { - var params = self.parseParams( hash ); - - self.validateOAuthResponse( params[ "access_token" ], params[ "scope" ] ) - .then( defer.resolve, defer.reject ) - .then(function() { - self.window.close(); - }); - } + var port = get( this, "oauth.server-port" ); + var server = new HttpServer( port, 1000 ); + set( self, "server", server ); - function onClosed() { - set( self, "window", null ); - delete window.OAUTH_CALLBACK; - cookies.removeAll(); - defer.reject(); - } + server.onRequest( "GET", "/redirect", function( req, res ) { + res.end( OAuthResponseRedirect ); + }); - // enable the redirect from https://api.twitch.tv to app://livestreamer-twitch-gui - redirect.enable( - get( self, "config.twitch-oauth-base-uri" ), - get( self, "config.twitch-oauth-redirect-uri" ) - ); - cookies.removeAll(); + server.onRequest( "GET", "/token", function( req, res ) { + var token = req.url.query.access_token; + var scope = req.url.query.scope; + + self.validateOAuthResponse( token, scope ) + .then(function( data ) { + res.end(); + defer.resolve( data ); + }, function( err ) { + res.statusCode = 500; + res.end(); + defer.reject( err ); + }); - window.OAUTH_CALLBACK = callback; + return true; + }); - // open window - var win = nwGui.Window.open( - get( self, "url" ), - OAuth.window - ); - win.on( "closed", onClosed ); - set( self, "window", win ); + // open auth url in web browser + var url = get( self, "url" ); + nwGui.Shell.openExternal( url ); + + return defer.promise + .then(function( data ) { + self.abortSignin(); + nwWindow.focus(); + return data; + }, function( err ) { + self.abortSignin(); + nwWindow.focus(); + return Promise.reject( err ); + }); + }, - return defer.promise; + abortSignin: function() { + var server = get( this, "server" ); + if ( !server ) { return; } + server.close(); + set( this, "server", null ); }, /** @@ -138,7 +152,7 @@ define([ && token.length > 0 && scope && scope.length > 0 - && this.validateScope( scope.split( "+" ) ) + && this.validateScope( scope.split( " " ) ) ? this.login( token, false ) : Promise.reject(); }, @@ -226,7 +240,7 @@ define([ * @returns {boolean} */ validateScope: function( scope ) { - var expected = get( this, "scope" ); + var expected = get( this, "oauth.scope" ); return scope instanceof Array && contains.all.apply( scope, expected ); @@ -272,15 +286,6 @@ define([ } set( adapter, "access_token", token ); - }, - - parseParams: function( str ) { - return String( str || "" ).split( "&" ) - .reduce(function( obj, elem ) { - var split = elem.split( "=" ); - obj[ split.splice( 0, 1 ) ] = split.join( "=" ); - return obj; - }, {} ); } }); diff --git a/src/oauth-redirect.html b/src/oauth-redirect.html new file mode 100644 index 0000000000..0ff5e8c0ae --- /dev/null +++ b/src/oauth-redirect.html @@ -0,0 +1,51 @@ + + + + +Authentication + + + + +

Validating... Please wait!

+

Authentication successful!

+

Authentication failed!

+

This window or tab can be closed now!

+ + + \ No newline at end of file diff --git a/src/oauth.html b/src/oauth.html deleted file mode 100644 index 0b1a7e90d5..0000000000 --- a/src/oauth.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - -Authentication - - - - - \ No newline at end of file diff --git a/src/oauth.json b/src/oauth.json deleted file mode 100644 index 74b624d6b4..0000000000 --- a/src/oauth.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "window": { - "nodejs": false, - "title": "Authenticate", - "icon": "img/icon-256.png", - "position": "center", - "frame": true, - "toolbar": false, - "resizable": false, - "focus": true, - "width": 450, - "height": 600 - } -} diff --git a/src/templates/user/UserAuth.hbs b/src/templates/user/UserAuth.hbs index 57a301f898..d767973173 100644 --- a/src/templates/user/UserAuth.hbs +++ b/src/templates/user/UserAuth.hbs @@ -1,6 +1,10 @@
+{{#if isLoggingIn}} +{{#form-button action="abort" class="btn-danger" icon="fa-times"}}Abort{{/form-button}} +{{else}} {{#form-button action="signin" class="btn-signin" icon="fa-sign-in" disabled=hasUserInteraction}}Sign in{{/form-button}} +{{/if}}