diff --git a/README.md b/README.md
index ce8e5406..642bfcdd 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,7 @@ So far, `everyauth` enables you to login via:
Evernote | Danny Amey
@@ -1056,6 +1057,62 @@ object whose parameter name keys map to description values:
everyauth.linkedin.configurable();
```
+## Setting up Windows Live OAuth
+
+```javascript
+var everyauth = require('everyauth')
+ , connect = require('connect');
+
+everyauth.windowsLive
+ .appId('YOUR CLIENT ID HERE')
+ .appSecret('YOUR CLIENT SECRET HERE')
+ .findOrCreateUser( function (session, accessToken, accessTokenExtra, liveUserMetadata) {
+ // find or create user logic goes here
+ })
+ .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.windowsLive
+ .entryPath('/auth/windowslive')
+ .callbackPath('/auth/windowslive/callback');
+```
+
+If you want to see what the current value of a
+configured parameter is, you can do so via:
+
+```javascript
+everyauth.windowsLive.callbackPath(); // '/auth/windowslive/callback'
+everyauth.windowsLive.entryPath(); // '/auth/windowslive'
+```
+
+To see all parameters that are configurable, the following will return an
+object whose parameter name keys map to description values:
+
+```javascript
+everyauth.windowsLive.configurable();
+```
+
+To run the Windows Live sample please run access the server through the url local.hosti:3000,
+since Windows Live limits the apps by one app per domain and local.host was taken.
+
+## Setting up Google OAuth2
+=======
### Google OAuth2
```javascript
@@ -2670,6 +2727,8 @@ Thanks to the following contributors for the following modules:
- Mail.ru
- [Rodolphe Stoclin](https://github.com/srod)
- Skyrock
+- [Or Kaplan](https://github.com/orkaplan)
+ - Windows Live
- [Danny Amey](https://github.com/dannyamey)
- 500px
- Evernote
diff --git a/example/conf.js b/example/conf.js
index 6a28044a..15d68748 100644
--- a/example/conf.js
+++ b/example/conf.js
@@ -27,6 +27,10 @@ module.exports = {
apiKey: 'pv6AWspODUeHIPNZfA531OYcFyB1v23u3y-KIADJdpyw54BXh-ciiQnduWf6FNRH'
, apiSecret: 'Pdx7DCoJRdAk0ai3joXsslZvK1DPCQwsLn-T17Opkae22ZYDP5R7gmAoFes9TNHy'
}
+ , windowsLive: {
+ apiKey: '0000000048088A51'
+ , apiSecret: 'z5zxAbjIgFMlT4lyeuu5HFJodwku5XGs'
+ }
, google: {
clientId: '3335216477.apps.googleusercontent.com'
, clientSecret: 'PJMW_uP39nogdu0WpBuqMhtB'
diff --git a/example/server.js b/example/server.js
index bd6d0870..e8bfa0bd 100644
--- a/example/server.js
+++ b/example/server.js
@@ -33,6 +33,7 @@ var usersByInstagramId = {};
var usersByFoursquareId = {};
var usersByGowallaId = {};
var usersByLinkedinId = {};
+var usersByWindowsLiveId = {};
var usersByGoogleId = {};
var usersByAngelListId = {};
var usersByYahooId = {};
@@ -208,6 +209,14 @@ everyauth.linkedin
return usersByLinkedinId[linkedinUser.id] || (usersByLinkedinId[linkedinUser.id] = addUser('linkedin', linkedinUser));
})
.redirectPath('/');
+
+ everyauth.windowsLive
+ .appId(conf.windowsLive.apiKey)
+ .appSecret(conf.windowsLive.apiSecret)
+ .findOrCreateUser( function (sess, accessToken, accessSecret, windowsLiveUser) {
+ return usersByWindowsLiveId[windowsLiveUser.id] || (usersByWindowsLiveId[windowsLiveUser.id] = addUser('windowsLive', windowsLiveUser));
+ })
+ .redirectPath('/');
everyauth.google
.appId(conf.google.clientId)
@@ -432,5 +441,6 @@ app.get('/', function (req, res) {
app.listen(3000);
console.log('Go to http://local.host:3000');
+console.log('For Windows Live Go to http://local.hosti:3000');
-module.exports = app;
+module.exports = app;
\ No newline at end of file
diff --git a/example/views/home.jade b/example/views/home.jade
index 4b39cd68..928c9ca1 100644
--- a/example/views/home.jade
+++ b/example/views/home.jade
@@ -29,6 +29,9 @@
#linkedin-login
a(href='/auth/linkedin', style='border: 0px')
img(style='border: 0px', src='http://press.linkedin.com/sites/all/themes/presslinkedin/images/LinkedIn_WebLogo_LowResExample.jpg')
+ #windows-live-login
+ a(href='/auth/windowslive', style='border: 0px')
+ img(style='border: 0px', src='http://upload.wikimedia.org/wikipedia/en/thumb/5/50/Windows_Live_logo.svg/300px-Windows_Live_logo.svg.png')
#google-login
a(href='/auth/google', style='border: 0px')
img(style='border: 0px', src='https://www.google.com/favicon.ico')
@@ -117,6 +120,9 @@
- if (everyauth.linkedin)
h3 LinkedIn User Data
p= JSON.stringify(everyauth.linkedin.user)
+ - if (everyauth.windowsLive)
+ h3 Windows Live User Data
+ p= JSON.stringify(everyauth.windowsLive.user)
- if (everyauth.google)
h3 Google User Data
p= JSON.stringify(everyauth.google.user)
diff --git a/lib/modules/oauth2.js b/lib/modules/oauth2.js
index f313afae..20b0b9b1 100644
--- a/lib/modules/oauth2.js
+++ b/lib/modules/oauth2.js
@@ -55,7 +55,7 @@ everyModule.submodule('oauth2')
.promises('code')
.canBreakTo('authCallbackErrorSteps')
.step('getAccessToken')
- .accepts('code')
+ .accepts('req code')
.promises('accessToken extra')
.step('fetchOAuthUser')
.accepts('accessToken')
@@ -136,7 +136,11 @@ everyModule.submodule('oauth2')
}
return parsedUrl.query && parsedUrl.query.code;
})
- .getAccessToken( function (code) {
+ .getAccessToken( function (req, code) {
+ // Automatic hostname detection + assignment
+ if (!this._myHostname || this._alwaysDetectHostname) {
+ this.myHostname(extractHostname(req));
+ }
var p = this.Promise()
, params = {
client_id: this._appId
diff --git a/lib/modules/windowsLive.js b/lib/modules/windowsLive.js
new file mode 100644
index 00000000..5dd468cc
--- /dev/null
+++ b/lib/modules/windowsLive.js
@@ -0,0 +1,77 @@
+var oauthModule = require('./oauth2')
+ , url = require('url');
+
+var fb = module.exports =
+ oauthModule.submodule('windowsLive')
+ .configurable({
+ scope: 'specify types of access: http://msdn.microsoft.com/en-us/library/hh243646(en-us).aspx',
+ display: 'The display type to be used for the authorization page. Valid values are "popup", "touch", "page", or "none".',
+ locale: 'Optional. A market string that determines how the consent UI is localized. If the value of this parameter is missing or is not valid, a market value is determined by using an internal algorithm.'
+ })
+
+ .apiHost('https://apis.live.net/v5.0')
+ .oauthHost('https://oauth.live.com')
+
+ .authPath('https://oauth.live.com/authorize')
+
+ .entryPath('/auth/windowslive')
+ .accessTokenHttpMethod('get')
+ .accessTokenPath('/token')
+ .callbackPath('/auth/windowslive/callback')
+
+ .scope('wl.signin')
+ .display('page')
+
+ .authQueryParam('scope', function () {
+ return this._scope && this.scope();
+ })
+
+ .authQueryParam('response_type', function () {
+ return 'code';
+ })
+
+ .accessTokenParam('grant_type', function () {
+ return 'authorization_code';
+ })
+
+ .authQueryParam('display', function () {
+ return this._display && this.display();
+ })
+
+ .authCallbackDidErr( function (req) {
+ var parsedUrl = url.parse(req.url, true);
+ return parsedUrl.query && !!parsedUrl.query.error;
+ })
+
+ .handleAuthCallbackError( function (req, res) {
+ var parsedUrl = url.parse(req.url, true)
+ , errorDesc = parsedUrl.query.error_description;
+ if (res.render) {
+ res.render(__dirname + '/../views/auth-fail.jade', {
+ errorDescription: errorDesc
+ });
+ } else {
+ // TODO Replace this with a nice fallback
+ throw new Error("You must configure handleAuthCallbackError if you are not using express");
+ }
+ })
+
+ .fetchOAuthUser( function (accessToken) {
+ var p = this.Promise();
+ this.oauth.get(this.apiHost() + '/me', accessToken, function (err, data) {
+ if (err)
+ return p.fail(err);
+ var oauthUser = JSON.parse(data);
+ p.fulfill(oauthUser);
+ })
+ return p;
+ })
+
+ .convertErr( function (data) {
+ if (typeof data == 'string')
+ return new Error(JSON.parse(data.data).error.message);
+ if (data)
+ return new Error(data.error + ' - ' + data.error_description);
+ else
+ return new Error('unknown error');
+ });
diff --git a/lib/step.js b/lib/step.js
index c1503e58..ebd60889 100644
--- a/lib/step.js
+++ b/lib/step.js
@@ -78,8 +78,9 @@ Step.prototype = {
new Error('Step ' + this.name + ' of `' + _module.name +
'` is promising: ' + promises.join(', ') +
' ; however, the step returns nothing. ' +
- 'Fix the step by returning the expected values OR ' +
- 'by returning a Promise that promises said values.')
+ 'Fix the step by returning the expected values OR ' +
+ 'by returning a Promise that promises said values.'),
+ seq.values
);
}
// Convert return value into a Promise
@@ -109,13 +110,13 @@ Step.prototype = {
} else if ('string' === typeof err) {
err = new Error(err);
}
- return oldFn.call(this, err);
+ return oldFn.call(this, err, scope);
};
return oldErrback.call(this, fn, scope);
};
}
- ret.errback(errorCallback);
+ ret.errback(errorCallback, seq.values);
ret.callback( function () {
// Store the returned values
diff --git a/media/windows_live.ico b/media/windows_live.ico
new file mode 100644
index 00000000..2376bb8b
Binary files /dev/null and b/media/windows_live.ico differ
|