Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Netflix OAuth Module #25

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ So far, `everyauth` enables you to login via:
- `dropbox`                                    (Credits [Torgeir](https://github.com/torgeir))
- `justin.tv`                                 (Credits [slickplaid](https://github.com/slickplaid))
- `vimeo`                                 (Credits [slickplaid](https://github.com/slickplaid))
- `netflix`                                 (Credits [slickplaid](https://github.com/slickplaid))
- OAuth2
- `facebook`
- `github`
Expand Down Expand Up @@ -1018,6 +1019,66 @@ To see all parameters that are configurable, the following will return an object
everyauth.justintv.configurable();
```

## Setting up Netflix OAuth

You will first need to sign up for a [developer application](http://developer.netflix.com/) to get the [application name, consumer key and secret](http://developer.netflix.com/apps/mykeys).

```javascript
var everyauth = require('everyauth')
, connect = require('connect');

everyauth.netflix
.consumerKey('YOUR CONSUMER KEY HERE')
.consumerSecret('YOUR CONSUMER SECRET HERE')
.moreAuthQueryParams({
oauth_consumer_key: 'YOUR CONSUMER KEY HERE'
, application_name: 'APPLICATION NAME HERE'
})
.findOrCreateUser( function (sess, accessToken, accessSecret, user) {
// find or create user logic goes here
//
// e.g.,
// return usersByNetflixId[user.id] || (usersByNetflixId[user.id] = user);
})
.redirectPath('/');

var routes = function (app) {
// Define your routes here
};

connect(
connect.bodyParser()
, connect.cookieParser()
, connect.session({secret: 'whodunnit'})
, everyauth.middleware()
, connect.router(routes);
).listen(3000);
```

You can also configure more parameters (most are set to defaults) via
the same chainable API:

```javascript
everyauth.netflix
.entryPath('/auth/netflix')
.callbackPath('/auth/netflix/callback');
```

If you want to see what the current value of a
configured parameter is, you can do so via:

```javascript
everyauth.netflix.callbackPath(); // '/auth/netflix/callback'
everyauth.netflix.entryPath(); // '/auth/netflix'
```

To see all parameters that are configurable, the following will return an
object whose parameter name keys map to description values:

```javascript
everyauth.netflix.configurable();
```

## Setting up Vimeo OAuth

You will first need to sign up for a [developer application](http://vimeo.com/api/applications) to get the consumer key and secret.
Expand Down Expand Up @@ -1546,6 +1607,7 @@ Thanks to the following contributors for the following modules:
- [slickplaid](https://github.com/slickplaid)
- Justin.tv
- Vimeo
- Netflix

### MIT License
Copyright (c) 2011 by Brian Noguchi
Expand Down
5 changes: 5 additions & 0 deletions example/conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ module.exports = {
consumerKey: 'Enter your consumer key here'
, consumerSecret: 'Enter your consumer secret here'
}
, netflix: {
consumerKey: 'Enter your consumer key here'
, consumerSecret: 'Enter your consumer secret here'
, application_name: 'Enter your application name here'
}
, box: {
apiKey: '5hl66lbfy0quj8qhhzcn57dflb55y4rg'
}
Expand Down
14 changes: 14 additions & 0 deletions example/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ var express = require('express')

everyauth.debug = true;

var usersByNetflixId = {};
var usersByVimeoId = {};
var usersByJustintvId = {};
var usersByDropboxId = {};
Expand Down Expand Up @@ -224,6 +225,19 @@ everyauth.justintv
})
.redirectPath('/')

everyauth.netflix
.consumerKey(conf.netflix.consumerKey)
.consumerSecret(conf.netflix.consumerSecret)
.moreAuthQueryParams({
oauth_consumer_key: conf.netflix.consumerKey
, application_name: conf.netflix.application_name
})
.findOrCreateUser( function (sess, accessToken, accessSecret, netflixUser) {
return usersByJustintvId[justintvUser.id] ||
(usersByNetflixId[netflixUser.id] = netflixUser);
})
.redirectPath('/')

everyauth.box
.apiKey(conf.box.apiKey)
.findOrCreateUser( function (sess, authToken, boxUser) {
Expand Down
3 changes: 3 additions & 0 deletions example/views/home.jade
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
#justintv-login
a(href='/auth/justintv', style='border: 0px')
img(src='http://s.jtvnw.net/jtv_user_pictures/hosted_images/new_logo_148_40_black.png')
#netflix-login
a(href='/auth/netflix', style='border: 0px')
img(src='http://developer.netflix.com/public/Mashery/images/clients/netflix/logo.gif')
#box-login
a(href='/auth/box', style='border: 0px')
img(src='http://sites.box.net/apps/web/simpleshare/img/logo.png')
Expand Down
47 changes: 47 additions & 0 deletions lib/modules/netflix.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
var oauthModule = require('./oauth')
, querystring = require('querystring')
, xml2js = require('xml2js');

var netflix = module.exports =
oauthModule.submodule('netflix')
.apiHost('http://api.netflix.com')
.oauthHost('http://api.netflix.com')

// abnormal authorize URI
.authorizePath('https://api-user.netflix.com/oauth/login')

.entryPath('/auth/netflix')
.callbackPath('/auth/netflix/callback')

.getAccessToken( function (reqToken, reqTokenSecret, verifier) {
var promise = this.Promise()
, extraParams = {
consumer_key: this._consumerKey
, consumer_secret: this._consumerSecret
};
this.oauth._performSecureRequest(reqToken, reqTokenSecret, "POST", this._apiHost + this._accessTokenPath, extraParams, null, null, function(error,data,res){
if( error ) promise.fail(error);
else {
var results= querystring.parse( data );
var oauth_access_token= results["oauth_token"];
delete results["oauth_token"];
var oauth_access_token_secret= results["oauth_token_secret"];
delete results["oauth_token_secret"];
promise.fulfill(oauth_access_token, oauth_access_token_secret, results)
}
});
return promise;
})

.fetchOAuthUser( function (accessToken, accessTokenSecret, params) {
var promise = this.Promise()
, parser = new xml2js.Parser();
parser.addListener('end', function(data){
return promise.fulfill(data);
});
this.oauth.get(this.apiHost() + '/users/'+params.user_id, accessToken, accessTokenSecret, function (err, data) {
if (err) return promise.fail(err);
return parser.parseString(data);
});
return promise;
});
51 changes: 47 additions & 4 deletions lib/modules/oauth.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var everyModule = require('./everymodule')
, OAuth = require('oauth').OAuth
, url = require('url')
, querystring = require('querystring')
, extractHostname = require('../utils').extractHostname;

var oauth = module.exports =
Expand All @@ -10,7 +11,8 @@ everyModule.submodule('oauth')
, oauthHost: 'the host for the OAuth provider'
, requestTokenPath: "the path on the OAuth provider's domain where we request the request token, e.g., /oauth/request_token"
, accessTokenPath: "the path on the OAuth provider's domain where we request the access token, e.g., /oauth/access_token"
, authorizePath: 'the path on the OAuth provider where you direct a visitor to login, e.g., /oauth/authorize'
, authorizePath: 'the path or full uri on the OAuth provider where you direct a visitor to login, e.g., /oauth/authorize or http://api-user.netflix.com/'
, moreAuthQueryParams: 'Additional querystrings to provide during the authorize step. e.g., { application_name: "testapp" }'
, consumerKey: 'the api key provided by the OAuth provider'
, consumerSecret: 'the api secret provided by the OAuth provider'
, myHostname: 'e.g., http://localhost:3000 . Notice no trailing slash'
Expand Down Expand Up @@ -81,7 +83,7 @@ everyModule.submodule('oauth')
}

var p = this.Promise();
this.oauth.getOAuthRequestToken( function (err, token, tokenSecret, authUrl, params) {
this.oauth.getOAuthRequestToken( function (err, token, tokenSecret, /* authUrl, */ params) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what the authUrl variable is doing here. In the corresponding function, it doesn't have that variable passed to this function. I needed to use the params variable so that's why it's been corrected. I left it in commented just so you can see what I did in case it breaks something for some reason.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In commit 7cb7aa078613242d62a3, I have a conflict.

authUrl variable doesn't seem to be in the parent oauth module. The callback on line 460 of node_modules/oauth/lib/oauth.js is callback(null, oauth_token, oauth_token_secret, results ); which doesn't include authUrl

Just wanted to get your clarification on this.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that shouldn't be there. I just removed it in a commit.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the vigilant eyes.

if (err) return p.fail(err);
p.fulfill(token, tokenSecret);
});
Expand All @@ -95,12 +97,53 @@ everyModule.submodule('oauth')
_provider.tokenSecret = tokenSecret;
})
.redirectToProviderAuth( function (res, token) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Start of fix for #23

var url = (/^http/.test(this._authorizePath))
? this._authorizePath
: this._oauthHost + this._authorizePath
, params = {
oauth_token: token
, oauth_callback: this._myHostname + this._callbackPath
}
, additionalParams = this._moreAuthQueryParams
, param
, authUri;

if (additionalParams) for (var k in additionalParams) {
param = additionalParams[k];
// Leaving this in in case needed in the future.
// Caused problems because req object wasn't passed to this function.
//if ('function' === typeof param) {
// // e.g., for facebook module, param could be
// // function () {
// // return this._scope && this.scope();
// // }
// additionalParams[k] = // cache the function call
// param = param.call(this);
//}
//if ('function' === typeof param) {
// // this.scope() itself could be a function
// // to allow for dynamic scope determination - e.g.,
// // function (req, res) {
// // return req.session.onboardingPhase; // => "email"
// // }
// param = param.call(this, req, res);
//}
params[k] = param;
}

authUri = url + '?' + querystring.stringify(params);
res.writeHead(303, {'Location': authUri});
res.end();

// Build URI from host + authorizePath if authorizeURI is absent (default behavior)
// Note: Not all oauth modules need oauth_callback as a uri query parameter. As far as I know, only readability's
// module needs it as a uri query parameter. However, in cases such as twitter, it allows you to over-ride
// the callback url settings at dev.twitter.com from one place, your app code, rather than in two places -- i.e.,
// your app code + dev.twitter.com app settings.
res.writeHead(303, { 'Location': this._oauthHost + this._authorizePath + '?oauth_token=' + token + '&oauth_callback=' + this._myHostname + this._callbackPath });
res.end();
//res.writeHead(303, { 'Location': this._oauthHost + this._authorizePath + '?oauth_token=' + token + '&oauth_callback=' + this._myHostname + this._callbackPath });
//res.end();

})

// Steps for GET `callbackPath`
Expand Down