Skip to content

Commit

Permalink
Reimplement the AuthService
Browse files Browse the repository at this point in the history
Use a local http server for the OAuth redirect instead of the
NW.js based app:// protocol.

Fixes #209
  • Loading branch information
bastimeyer committed Feb 6, 2016
1 parent de7085c commit 46cac0b
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 143 deletions.
25 changes: 14 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
19 changes: 12 additions & 7 deletions src/app/controllers/UserAuthController.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -134,6 +134,11 @@ define([
self.retryTransition();
}
});
},

// abort sign in with username + password
"abort": function() {
get( this, "auth" ).abortSignin();
}
}
});
Expand Down
35 changes: 0 additions & 35 deletions src/app/nwjs/redirect.js

This file was deleted.

5 changes: 1 addition & 4 deletions src/app/routes/UserAuthRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Expand Down
117 changes: 61 additions & 56 deletions src/app/services/AuthService.js
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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() {
Expand Down Expand Up @@ -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 );
},

/**
Expand All @@ -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();
},
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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;
}, {} );
}
});

Expand Down
51 changes: 51 additions & 0 deletions src/oauth-redirect.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
<title>Authentication</title>
<style>html{width:100%;background:#fff;color:#222}body{width:28em;margin:5em auto}h2,p{text-align:center}body>p{display:none}em{padding:0 .2em;background:#f2f2f2}</style>
</head>
<body>
<noscript>
<h2>JavaScript required!</h2>
<p>Alternative way:</p>
<ol>
<li>Copy the value of the <em>access_token</em> parameter from the URL</li>
<li>Enable advanced settings at the top of the settings menu</li>
<li>Go to the login menu and click the <em>Use access token</em> button</li>
<li>Enter the access token into the input field and log in</li>
</ol>
</noscript>
<p id="validating">Validating... Please wait!</p>
<p id="success">Authentication successful!</p>
<p id="failure">Authentication failed!</p>
<p id="close">This window or tab can be closed now!</p>
<script>
(function() {
function display( id, mode ) {
document.getElementById( id ).style.display = mode ? "block" : "none";
}

var params = String( location.hash ).replace( /^#/, "" );
if ( /\berror=access_denied\b/.test( location.search ) ) {
params = "";
} else if ( !params || /^(success|failure)$/.test( params ) ) {
return display( "close", true );
}
display( "validating", true );

var req = new XMLHttpRequest();
req.open( "GET", "/token?" + params, true );
req.addEventListener( "readystatechange", function() {
if ( req.readyState !== XMLHttpRequest.DONE ) { return; }
var str = req.status === 200 ? "success" : "failure";
location.replace( location.origin + location.pathname + "#" + str );
display( "validating", false );
display( str, true );
display( "close", true );
}, false );
req.send();
})();
</script>
</body>
</html>
16 changes: 0 additions & 16 deletions src/oauth.html

This file was deleted.

14 changes: 0 additions & 14 deletions src/oauth.json

This file was deleted.

Loading

0 comments on commit 46cac0b

Please sign in to comment.