Skip to content

Commit

Permalink
Add authorize method
Browse files Browse the repository at this point in the history
  • Loading branch information
pgom committed Jan 18, 2016
1 parent 6519e27 commit eaa5f24
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 2 deletions.
45 changes: 45 additions & 0 deletions src/providers/oauth-provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import angular from 'angular';
import queryString from 'query-string';

var defaults = {
authorizePath: '/oauth2/authorize',
baseUrl: null,
clientId: null,
clientSecret: null,
Expand All @@ -15,6 +16,7 @@ var defaults = {
};

var requiredKeys = [
'authorizePath',
'baseUrl',
'clientId',
'grantPath',
Expand Down Expand Up @@ -60,6 +62,11 @@ function OAuthProvider() {
config.baseUrl = config.baseUrl.slice(0, -1);
}

// Add `authorizePath` facing slash.
if('/' !== config.authorizePath[0]) {
config.authorizePath = `/${config.authorizePath}`;
}

// Add `grantPath` facing slash.
if('/' !== config.grantPath[0]) {
config.grantPath = `/${config.grantPath}`;
Expand Down Expand Up @@ -92,6 +99,44 @@ function OAuthProvider() {
}
}

/**
* Requests a authorization for an application based on clientId, scope and state
*
* @param {string} clientId - Application `clientId`
* @param {string} scope - Scope(s) defined for the application
* @param {string} state - Randomly generated `state` string
* @return {promise} A response promise.
*/

authorize(clientId, scope, state) {
// Check if `clientId` is defined.
if (!clientId) {
throw new Error('You must provide an application `clientId`');
}

const data = {
client_id: clientId,
response_type: 'code'
};

if (scope) {
data.scope = scope;
}

if (state) {
data.state = state;
}

const qs = queryString.stringify(data);
const url = `${config.baseUrl}${config.authorizePath}?${qs}`;

const options = {
headers: { 'Content-Type': 'application/json' }
};

return $http.get(url, options);
}

/**
* Verifies if the `user` is authenticated or not based on the `token`
* cookie.
Expand Down
113 changes: 111 additions & 2 deletions test/unit/providers/oauth-provider.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@

describe('OAuthProvider', function() {
var defaults = {
authorizePath: '/oauth2/authorize',
baseUrl: 'https://api.website.com',
clientId: 'CLIENT_ID',
clientSecret: 'CLIENT_SECRET',
grantPath: '/oauth2/token',
revokePath: '/oauth2/revoke',
clientSecret: 'CLIENT_SECRET'
redirectUrl: 'https://website.com',
revokePath: '/oauth2/revoke'
};

describe('configure()', function() {
Expand Down Expand Up @@ -48,6 +50,25 @@ describe('OAuthProvider', function() {
}
});

it('should throw an error if `authorizePath` param is empty', function() {
try {
provider.configure(_.defaults({ authorizePath: null }, defaults));

should.fail();
} catch(e) {
e.should.be.an.instanceOf(Error);
e.message.should.match(/authorizePath/);
}
});

it('should add facing slash from `authorizePath`', function() {
var config = provider.configure(_.defaults({
authorizePath: 'oauth2/authorize'
}, defaults));

config.authorizePath.should.equal('/oauth2/authorize');
});

it('should throw an error if `baseUrl` param is empty', function() {
try {
provider.configure(_.omit(defaults, 'baseUrl'));
Expand Down Expand Up @@ -137,6 +158,94 @@ describe('OAuthProvider', function() {
OAuthToken.removeToken();
}));

describe('authorize()', function() {
var data = {
client_id: defaults.clientId,
response_type: 'code',
scope: 'foo:bar',
state: 'state_hash'
};

it('should throw an error if `clientId` is missing', inject(function(OAuth) {
try {
OAuth.authorize();

should.fail();
} catch(e) {
e.should.be.an.instanceOf(Error);
e.message.should.match(/clientId/);
}
}));

it('should call `queryString.stringify`', inject(function(OAuth) {
sinon.spy(queryString, 'stringify');

OAuth.authorize(data.client_id, data.scope, data.state);

queryString.stringify.callCount.should.equal(1);
queryString.stringify.firstCall.args.should.have.lengthOf(1);
queryString.stringify.firstCall.args[0].should.eql({
client_id: data.client_id,
response_type: 'code',
scope: data.scope,
state: data.state
});

queryString.stringify.restore();
}));

it('should return an error if request response doesn\'t contain a `redirectUri` attribute', inject(function($httpBackend, OAuth) {
$httpBackend.expectGET(`${defaults.baseUrl}${defaults.authorizePath}?${queryString.stringify(data)}`)
.respond(200, { redirectUri: `${defaults.redirectUrl}` });

OAuth.authorize(data.client_id, data.scope, data.state).then(function(response) {
response.data.should.have.property('redirectUri');
});

$httpBackend.flush();

$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
}));

it('should return an `error` and `error_description` parameters if scope is invalid ', inject(function($httpBackend, OAuth) {
$httpBackend.expectGET(`${defaults.baseUrl}${defaults.authorizePath}?${queryString.stringify(data)}`)
.respond(200, { redirectUri: `${defaults.redirectUrl}?error=invalid_scope&error_description=The%20requested%20scope%20is%20invalid` });

OAuth.authorize(data.client_id, data.scope, data.state).then(function(response) {
response.data.should.have.property('redirectUri');
response.data.redirectUri.should.match(/error=/);
response.data.redirectUri.should.match(/error_description=/);
});

$httpBackend.flush();

$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
}));

it('should return an `code` and `state` parameters if scope is valid', inject(function($httpBackend, OAuth) {
var redirectUri = `${defaults.redirectUrl}?code=foo&state=${data.state}`;
_.merge(data, { scope: 'foobar' });

$httpBackend.expectGET(`${defaults.baseUrl}${defaults.authorizePath}?${queryString.stringify(data)}`)
.respond(200, { redirectUri: redirectUri });

OAuth.authorize(data.client_id, data.scope, data.state).then(function(response) {
response.data.should.have.property('redirectUri');
response.data.redirectUri.should.match(/code=/);
response.data.redirectUri.should.match(/state=/);
response.data.redirectUri.should.match(new RegExp(`state=${data.state}`));
});

$httpBackend.flush();

$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
}));

});

describe('isAuthenticated()', function() {
it('should be true when there is a stored `token` cookie', inject(function(OAuth, OAuthToken) {
OAuthToken.setToken({ token_type: 'bearer', access_token: 'foo', expires_in: 3600, refresh_token: 'bar' });
Expand Down

0 comments on commit eaa5f24

Please sign in to comment.