Skip to content

Commit

Permalink
Issue #503 - Add optional client_id to OAuth2
Browse files Browse the repository at this point in the history
  • Loading branch information
bbooth authored and marcoow committed Oct 16, 2015
1 parent 0114dc1 commit cfccc17
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 7 deletions.
36 changes: 31 additions & 5 deletions addon/authenticators/oauth2.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import Ember from 'ember';
import Base from './base';
import Configuration from './../configuration';
import Base64 from './../utils/base64';

/**
Authenticator that conforms to OAuth 2
Expand Down Expand Up @@ -31,6 +32,19 @@ export default Base.extend({
@public
*/

/**
The client_id to be sent to the authorization server
This value can be configured via
[`SimpleAuth.Configuration.OAuth2#clientId`](#SimpleAuth-Configuration-OAuth2-clientId).
@property clientId
@type String
@default null
@public
*/
clientId: null,

/**
The endpoint on the server the authenticator acquires the access token
from.
Expand Down Expand Up @@ -83,6 +97,7 @@ export default Base.extend({
@private
*/
init() {
this.clientId = Configuration.oauth2.clientId;
this.serverTokenEndpoint = Configuration.oauth2.serverTokenEndpoint;
this.serverTokenRevocationEndpoint = Configuration.oauth2.serverTokenRevocationEndpoint;
this.refreshAccessTokens = Configuration.oauth2.refreshAccessTokens;
Expand Down Expand Up @@ -223,13 +238,24 @@ export default Base.extend({
@public
*/
makeRequest(url, data) {
return Ember.$.ajax({
let options = {
url,
type: 'POST',
type: 'POST',
data,
dataType: 'json',
contentType: 'application/x-www-form-urlencoded'
});
dataType: 'json',
contentType: 'application/x-www-form-urlencoded'
};

if (!Ember.isEmpty(this.clientId)) {
let base64ClientId = Base64.encode(this.clientId.concat(':'));
Ember.merge(options, {
headers: {
Authorization: 'Basic '.concat(base64ClientId)
}
});
}

return Ember.$.ajax(options);
},

/**
Expand Down
15 changes: 14 additions & 1 deletion addon/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ const defaults = {
oauth2: {
serverTokenEndpoint: '/token',
serverTokenRevocationEndpoint: null,
refreshAccessTokens: true
refreshAccessTokens: true,
clientId: null
}
};

Expand Down Expand Up @@ -285,6 +286,18 @@ export default {
},

oauth2: {
/**
The client_id sent to the server
@property client_id
@readOnly
@static
@type String
@default null
@public
*/
clientId: defaults.oauth2.clientId,

/**
The endpoint on the server the authenticator acquires the access token
from.
Expand Down
32 changes: 32 additions & 0 deletions addon/utils/base64.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*global InvalidCharacterError*/

/**
Adapted from https://github.com/davidchambers/Base64.js
@public
*/

const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';

export default {
encode(input) {
let str = String(input);
let output = '';
for (
// initialize result and counter
let block, charCode, idx = 0, map = chars;
// if the next str index does not exist:
// change the mapping table to "="
// check if d has no fractional digits
str.charAt(idx | 0) || (map = '=', idx % 1);
// "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8
output += map.charAt(63 & block >> 8 - idx % 1 * 8)
) {
charCode = str.charCodeAt(idx += 3 / 4);
if (charCode > 0xFF) {
throw new InvalidCharacterError("'base64Encode' failed: The string to be encoded contains characters outside of the Latin1 range.");
}
block = block << 8 | charCode;
}
return output;
}
};
25 changes: 24 additions & 1 deletion tests/unit/authenticators/oauth2-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ describe('OAuth2', () => {
expect(OAuth2.create().refreshAccessTokens).to.be.false;
});

it('assigns clientId from the configuration object', () => {
Configuration.oauth2.clientId = 'test-client';

expect(OAuth2.create().clientId).to.be.eq('test-client');
});

afterEach(() => {
Configuration.load({});
});
Expand Down Expand Up @@ -142,7 +148,24 @@ describe('OAuth2', () => {
});
});

it('sends a single OAuth scope to the token endpoint', (done) => {
it('sends an AJAX request to the token endpoint with client_id Basic Auth header', function(done) {
authenticator.set('clientId', 'test-client');
authenticator.authenticate({ identification: 'username', password: 'password' });

Ember.run.next(() => {
expect(Ember.$.ajax.getCall(0).args[0]).to.eql({
url: '/token',
type: 'POST',
data: { 'grant_type': 'password', username: 'username', password: 'password' },
dataType: 'json',
contentType: 'application/x-www-form-urlencoded',
headers: { Authorization: 'Basic dGVzdC1jbGllbnQ6' }
});
done();
});
});

it('sends a single OAuth scope to the token endpoint', function(done) {
authenticator.authenticate({ identification: 'username', password: 'password', scope: 'public' });

Ember.run.next(() => {
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/configuration-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,12 @@ describe('Configuration', () => {
expect(Configuration.devise.tokenAttributeName).to.eql('tokenAttributeName');
});

it('sets clientId correctly', () => {
Configuration.load({ oauth2: { clientId: 'clientId' } });

expect(Configuration.oauth2.clientId).to.eql('clientId');
});

it('sets serverTokenEndpoint correctly', () => {
Configuration.load({ oauth2: { serverTokenEndpoint: 'serverTokenEndpoint' } });

Expand Down

0 comments on commit cfccc17

Please sign in to comment.