Skip to content

Commit

Permalink
Merge pull request lelylan#9 from fdeandao/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
andreareginato committed Jan 16, 2014
2 parents d8876bd + 255d3eb commit 5be0e71
Show file tree
Hide file tree
Showing 13 changed files with 187 additions and 40 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ node_modules

# Docs
docs/

.idea/
*.iml
4 changes: 3 additions & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
Copyright (c) 2013 Lelylan
Copyright (c) 2013 lelylan, [lelylan.com](http://lelylan.com)

This project is released under the [MIT License](http://opensource.org/licenses/MIT).
90 changes: 85 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Simple OAuth2 supports the following flows.

* Authorization Code Flow (for apps with servers that can store persistent information).
* Password Credentials (when previous flow can't be used or during development).
* [Client Credentials Flow](http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.4) (the client can request an access token using only its client credentials)

## Requirements

Expand All @@ -30,6 +31,57 @@ Install the client library using git:

## Getting started

### Express and Github example

```javascript
var express = require('express'),
app = express();

var OAuth2 = require('simple-oauth2')({
clientID: CLIENT_ID,
clientSecret: CLIENT_SECRET,
site: 'https://github.com/login',
tokenPath: '/oauth/access_token'
});

// Authorization uri definition
var authorization_uri = OAuth2.AuthCode.authorizeURL({
redirect_uri: 'http://localhost:3000/callback',
scope: 'notifications',
state: '3(#0/!~'
});

// Initial page redirecting to Github
app.get('/auth', function (req, res) {
res.redirect(authorization_uri);
});

// Callback service parsing the authorization token and asking for the access token
app.get('/callback', function (req, res) {
var code = req.query.code;
console.log('/callback');
OAuth2.AuthCode.getToken({
code: code,
redirect_uri: 'http://localhost:3000/callback'
}, saveToken);

function saveToken(error, result) {
if (error) { console.log('Access Token Error', error.message); }
token = OAuth2.AccessToken.create(result);
}
});

app.get('/', function (req, res) {
res.send('Hello World');
});

app.listen(3000);

console.log('Express server started on port 3000');
```

Credits to [@lazybean](https://github.com/lazybean)

### Authorization Code flow

The Authorization Code flow is made up from two parts. At first your application asks to
Expand Down Expand Up @@ -95,6 +147,32 @@ function saveToken(error, result) {
});
```

### Client Credentials Flow

This flow is suitable when client is requesting access to the protected resources under its control.

```javascript
// Get the access token object.
var token;
var credentials = {
clientID: '<client-id>',
clientSecret: '<client-secret>',
site: 'https://api.oauth.com'
};

// Initialize the OAuth2 Library
var OAuth2 = require('simple-oauth2')(credentials);

// Get the access token object for the client
OAuth2.Client.getToken(saveToken);

// Save the access token
function saveToken(error, result) {
if (error) { console.log('Access Token Error', error.message); }
token = OAuth2.AccessToken.create(result);
});
```

### Access Token object

When a token expires we need to refresh it. Simple OAuth2 offers the
Expand Down Expand Up @@ -145,10 +223,11 @@ Simple OAuth2 accepts an object with the following valid params.
* `clientID` - Required registered Client ID.
* `clientSecret` - Required registered Client secret.
* `site` - Required OAuth2 server site.
* `authorizationPath` - Authorization path for the OAuth2 server.
Simple OAuth2 uses `/oauth/authorize` as default
* `tokenPath` - Access token path for the OAuth2 server.
Simple OAuth2 uses `/oauth/token` as default.
* `authorizationPath` - Authorization path for the OAuth2 server. Defaults to `/oauth/authorize`.
* `tokenPath` - Access token path for the OAuth2 server. Defaults to `/oauth/token`.
* `useBasicAuthorizationHeader` - Whether or not the `Authorization: Basic ...` header is set on the request.
Defaults to `true`.
* `clientSecretParameterName` - Parameter name for the client secret. Defaults to `client_secret`.

```javascript
// Set the configuration settings
Expand Down Expand Up @@ -215,4 +294,5 @@ See [CHANGELOG](https://github.com/andreareginato/simple-oauth2/blob/master/CHAN
## Copyright

Copyright (c) 2013 [Lelylan](http://lelylan.com).
See [LICENSE](https://github.com/andreareginato/simple-oauth2/blob/master/LICENSE.md) for details.

This project is released under the [MIT License](http://opensource.org/licenses/MIT).
39 changes: 18 additions & 21 deletions lib/client/access-token.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,46 @@
module.exports = function(config) {

var core = require('./../core')(config);
require('date-utils');
require('date-utils');

//
// ### Creates an OAuth2.AccessToken instance.
//
// ### Creates an OAuth2.AccessToken instance.
//
// * `token` - An object containing the token object returned from the OAuth2 server.
//
//
function create(token) {
this.token = token;
this.token.expires_at = (new Date).addSeconds(token.expires_in);
return this;
}


//
//
// ### Check if the access token is expired or not.
//
function expired() {
return (Date.compare(this.token.expires_at, new Date) == -1) ? true : false
}

//
function expired() {
return (Date.compare(this.token.expires_at, new Date) == -1) ? true : false
}

//
//
// ### Refresh the access token
//
//
// * `callback` - The callback function returning the results.
// An error object is passed as first argument and the new OAuth2.AccessToken
// An error object is passed as first argument and the new OAuth2.AccessToken
// as last.
//
function refresh(callback) {
var params = { grant_type: 'refresh_token', refresh_token: this.token.refresh_token };
//
function refresh(callback) {
var params = { grant_type: 'refresh_token', refresh_token: this.token.refresh_token };
var that = this;
core.api('POST', config.tokenPath, params, function(error, result){
if (result) { result = that.create(result); }
callback(error, result);
});
}


return {
'create' : create,
'token' : this.token,
'expired': expired,
'refresh': refresh
'token' : this.token,
'expired': expired,
'refresh': refresh
}
};
24 changes: 24 additions & 0 deletions lib/client/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// ### Client credentials flow implementation
//
module.exports = function(config) {

var core = require('./../core')(config);

//
// ### Returns the Access Token object.
//
// * `params.scope` - A String that represents the application privileges.
// * `callback` - The callback function returning the results.
// An error object is passed as first argument and the result as last.
//
function getToken(params, callback) {
params.grant_type = 'client_credentials';
core.api('POST', config.tokenPath, params, callback);
}


return {
'getToken' : getToken
}
};
6 changes: 4 additions & 2 deletions lib/config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
module.exports = {
'authorizationPath' : '/oauth/authorize',
'tokenPath' : '/oauth/token',
'clientID': null,
'clientID': null,
'clientSecret': null,
'site': null
'site': null,
'useBasicAuthorizationHeader': true,
'clientSecretParameterName': 'client_secret'
}
11 changes: 8 additions & 3 deletions lib/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ module.exports = function(config) {
if (!config.clientID || !config.clientSecret || !config.site)
throw new Error('Configuration missing. You need to specify the client id, the client secret and the oauth2 server');

if (config.clientID)
if (config.useBasicAuthorizationHeader && config.clientID)
options.headers = { 'Authorization': 'Basic ' + new Buffer(config.clientID + ':' + config.clientSecret).toString('base64') }
else
options.headers = {}

if (isEmpty(params)) params = null;
if (method != 'GET') options.form = params;
Expand All @@ -42,7 +44,7 @@ module.exports = function(config) {
// Enable the system to send authorization params in the body (for example github does not require to be in the header)
if (method != 'GET' && options.form && !params.password) {
options.form.client_id = config.clientID;
options.form.secret = config.clientSecret;
options.form[config.clientSecretParameterName] = config.clientSecret;
}

if (process.env.DEBUG) console.log('Simple OAuth2: Making the HTTP request', options)
Expand All @@ -53,7 +55,10 @@ module.exports = function(config) {
// Extract the data from the request response
function data(error, response, body, callback) {

if (error) throw new Error('Simple OAuth2: something went worng during the request');
if (error) {
return callback(error);
}

if (process.env.DEBUG) console.log('Simple OAuth2: checking response body', body);

try { body = JSON.parse(body); }
Expand Down
1 change: 1 addition & 0 deletions lib/simple-oauth2.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ module.exports = function(config) {
return {
'AuthCode': require('./client/auth-code')(config),
'Password': require('./client/password')(config),
'Client': require('./client/client')(config),
'AccessToken': require('./client/access-token')(config)
}
};
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "simple-oauth2",
"version": "0.1.7",
"version": "0.1.8",
"description": "Node.js client for OAuth2",
"author": "Andrea Reginato <[email protected]>",
"homepage": "http://github.com/andreareginato/simple-oauth2",
Expand Down
8 changes: 4 additions & 4 deletions test/access_token.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var request, result, token, error;
describe('OAuth2.AccessToken',function() {

beforeEach(function(done) {
var params = { 'code': 'code', 'redirect_uri': 'http://callback.com', 'grant_type': 'authorization_code', 'client_id': 'client-id', 'secret': 'client-secret' };
var params = { 'code': 'code', 'redirect_uri': 'http://callback.com', 'grant_type': 'authorization_code', 'client_id': 'client-id', 'client_secret': 'client-secret' };
request = nock('https://example.org:443').post('/oauth/token', qs.stringify(params)).replyWithFile(200, __dirname + '/fixtures/access_token.json');
done();
})
Expand Down Expand Up @@ -51,11 +51,11 @@ describe('OAuth2.AccessToken',function() {
it('returns false',function() {
token.expired().should.be.true
});

});
describe('when refreses token', function() {

beforeEach(function(done) {
var params = { 'grant_type': 'refresh_token', refresh_token: 'ec1a59d298', 'client_id': 'client-id', 'secret': 'client-secret' };
var params = { 'grant_type': 'refresh_token', refresh_token: 'ec1a59d298', 'client_id': 'client-id', 'client_secret': 'client-secret' };
request = nock('https://example.org:443').post('/oauth/token', qs.stringify(params)).replyWithFile(200, __dirname + '/fixtures/access_token.json');
done();
});
Expand All @@ -75,5 +75,5 @@ describe('OAuth2.AccessToken',function() {
result.token.should.have.property('access_token');
});
})
});

});
2 changes: 1 addition & 1 deletion test/auth-code.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('OAuth2.AuthCode',function() {
describe('#getToken',function() {

beforeEach(function(done) {
var params = { 'code': 'code', 'redirect_uri': 'http://callback.com', 'grant_type': 'authorization_code', 'client_id': 'client-id', 'secret': 'client-secret' };
var params = { 'code': 'code', 'redirect_uri': 'http://callback.com', 'grant_type': 'authorization_code', 'client_id': 'client-id', 'client_secret': 'client-secret' };
request = nock('https://example.org').post('/oauth/token', qs.stringify(params)).replyWithFile(200, __dirname + '/fixtures/access_token.json');
done();
})
Expand Down
33 changes: 33 additions & 0 deletions test/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
var credentials = { clientID: 'client-id', clientSecret: 'client-secret', site: 'https://example.org', form: false },
OAuth2 = require('./../lib/simple-oauth2.js')(credentials),
qs = require('querystring'),
nock = require('nock');

var request, result, error;

describe('OAuth2.Client',function() {

describe('#getToken',function() {

beforeEach(function(done) {
var params = { 'grant_type': 'client_credentials', client_id: 'client-id', client_secret: 'client-secret' };
request = nock('https://example.org:443').post('/oauth/token', qs.stringify(params)).replyWithFile(200, __dirname + '/fixtures/access_token.json');
done();
})

beforeEach(function(done) {
var params = {};
OAuth2.Client.getToken(params, function(e, r) {
error = e; result = r; done();
})
})

it('makes the HTTP request', function() {
request.isDone();
});

it('returns an access token',function() {
result.should.have.property('access_token');
});
});
});
4 changes: 2 additions & 2 deletions test/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe('Simple OAuth2 Error',function() {
describe('with status code 401',function() {

beforeEach(function(done) {
var params = { 'code': 'code', 'redirect_uri': 'http://callback.com', 'grant_type': 'authorization_code', 'client_id': 'client-id', 'secret': 'client-secret' };
var params = { 'code': 'code', 'redirect_uri': 'http://callback.com', 'grant_type': 'authorization_code', 'client_id': 'client-id', 'client_secret': 'client-secret' };
request = nock('https://example.org:443').post('/oauth/token', qs.stringify(params)).reply(401, 'Unauthorized');
done();
});
Expand All @@ -34,7 +34,7 @@ describe('Simple OAuth2 Error',function() {
describe('with status code 500',function() {

beforeEach(function(done) {
var params = { 'code': 'code', 'redirect_uri': 'http://callback.com', 'grant_type': 'authorization_code', 'client_id': 'client-id', 'secret': 'client-secret' };
var params = { 'code': 'code', 'redirect_uri': 'http://callback.com', 'grant_type': 'authorization_code', 'client_id': 'client-id', 'client_secret': 'client-secret' };
request = nock('https://example.org:443').post('/oauth/token', qs.stringify(params)).reply(500, 'Server Error');
done();
});
Expand Down

0 comments on commit 5be0e71

Please sign in to comment.