Skip to content

Commit 36d10ce

Browse files
committed
Add authorize method
1 parent 6519e27 commit 36d10ce

File tree

2 files changed

+129
-2
lines changed

2 files changed

+129
-2
lines changed

src/providers/oauth-provider.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import angular from 'angular';
77
import queryString from 'query-string';
88

99
var defaults = {
10+
authorizePath: '/oauth2/authorize',
1011
baseUrl: null,
1112
clientId: null,
1213
clientSecret: null,
@@ -15,6 +16,7 @@ var defaults = {
1516
};
1617

1718
var requiredKeys = [
19+
'authorizePath',
1820
'baseUrl',
1921
'clientId',
2022
'grantPath',
@@ -60,6 +62,11 @@ function OAuthProvider() {
6062
config.baseUrl = config.baseUrl.slice(0, -1);
6163
}
6264

65+
// Add `authorizePath` facing slash.
66+
if('/' !== config.authorizePath[0]) {
67+
config.authorizePath = `/${config.authorizePath}`;
68+
}
69+
6370
// Add `grantPath` facing slash.
6471
if('/' !== config.grantPath[0]) {
6572
config.grantPath = `/${config.grantPath}`;
@@ -92,6 +99,40 @@ function OAuthProvider() {
9299
}
93100
}
94101

102+
/**
103+
* Requests a authorization for an application based on clientId, scope and state
104+
*
105+
* @param {string} clientId - Application `clientId`
106+
* @param {string} scope - Scope(s) defined for the application
107+
* @param {string} state - Randomly generated `state` string
108+
* @return {promise} A response promise.
109+
*/
110+
111+
authorize(clientId, scope, state) {
112+
// Check if `clientId` is defined.
113+
if (!clientId) {
114+
throw new Error('Missing parameter: clientId.');
115+
}
116+
117+
const data = {
118+
client_id: clientId,
119+
response_type: 'code'
120+
};
121+
122+
if (scope) {
123+
data.scope = scope;
124+
}
125+
126+
if (state) {
127+
data.state = state;
128+
}
129+
130+
const qs = queryString.stringify(data);
131+
const url = `${config.baseUrl}${config.authorizePath}?${qs}`;
132+
133+
return $http.get(url);
134+
}
135+
95136
/**
96137
* Verifies if the `user` is authenticated or not based on the `token`
97138
* cookie.

test/unit/providers/oauth-provider.spec.js

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55

66
describe('OAuthProvider', function() {
77
var defaults = {
8+
authorizePath: '/oauth2/authorize',
89
baseUrl: 'https://api.website.com',
910
clientId: 'CLIENT_ID',
11+
clientSecret: 'CLIENT_SECRET',
1012
grantPath: '/oauth2/token',
11-
revokePath: '/oauth2/revoke',
12-
clientSecret: 'CLIENT_SECRET'
13+
redirectUrl: 'https://website.com',
14+
revokePath: '/oauth2/revoke'
1315
};
1416

1517
describe('configure()', function() {
@@ -48,6 +50,25 @@ describe('OAuthProvider', function() {
4850
}
4951
});
5052

53+
it('should throw an error if `authorizePath` param is empty', function() {
54+
try {
55+
provider.configure(_.defaults({ authorizePath: null }, defaults));
56+
57+
should.fail();
58+
} catch(e) {
59+
e.should.be.an.instanceOf(Error);
60+
e.message.should.match(/authorizePath/);
61+
}
62+
});
63+
64+
it('should add facing slash from `authorizePath`', function() {
65+
var config = provider.configure(_.defaults({
66+
authorizePath: 'oauth2/authorize'
67+
}, defaults));
68+
69+
config.authorizePath.should.equal('/oauth2/authorize');
70+
});
71+
5172
it('should throw an error if `baseUrl` param is empty', function() {
5273
try {
5374
provider.configure(_.omit(defaults, 'baseUrl'));
@@ -137,6 +158,71 @@ describe('OAuthProvider', function() {
137158
OAuthToken.removeToken();
138159
}));
139160

161+
describe('authorize()', function() {
162+
var data = {
163+
client_id: defaults.clientId,
164+
response_type: 'code',
165+
scope: 'foo:bar',
166+
state: 'state_hash'
167+
};
168+
169+
it('should throw an error if `clientId` is missing', inject(function(OAuth) {
170+
try {
171+
OAuth.authorize();
172+
173+
should.fail();
174+
} catch(e) {
175+
e.should.be.an.instanceOf(Error);
176+
e.message.should.match(/clientId/);
177+
}
178+
}));
179+
180+
it('should call `queryString.stringify` with default `data` if `state` and `scope` are not provided', inject(function(OAuth) {
181+
sinon.spy(queryString, 'stringify');
182+
183+
OAuth.authorize(data.client_id);
184+
185+
queryString.stringify.callCount.should.equal(1);
186+
queryString.stringify.firstCall.args.should.have.lengthOf(1);
187+
queryString.stringify.firstCall.args[0].should.eql({
188+
client_id: data.client_id,
189+
response_type: 'code'
190+
});
191+
192+
queryString.stringify.restore();
193+
}));
194+
195+
it('should call `queryString.stringify` with provided `state` and `scope`', inject(function(OAuth) {
196+
sinon.spy(queryString, 'stringify');
197+
198+
OAuth.authorize(data.client_id, data.scope, data.state);
199+
200+
queryString.stringify.callCount.should.equal(1);
201+
queryString.stringify.firstCall.args.should.have.lengthOf(1);
202+
queryString.stringify.firstCall.args[0].should.eql({
203+
client_id: data.client_id,
204+
response_type: 'code',
205+
scope: data.scope,
206+
state: data.state
207+
});
208+
209+
queryString.stringify.restore();
210+
}));
211+
212+
it('should call `$http.get` with url containing the stringified `data`', inject(function($httpBackend, OAuth) {
213+
const qs = queryString.stringify(data);
214+
215+
$httpBackend.expectGET(`${defaults.baseUrl}${defaults.authorizePath}?${qs}`).respond(200, 'foobar');
216+
217+
OAuth.authorize(data.client_id, data.scope, data.state);
218+
219+
$httpBackend.flush();
220+
221+
$httpBackend.verifyNoOutstandingExpectation();
222+
$httpBackend.verifyNoOutstandingRequest();
223+
}));
224+
});
225+
140226
describe('isAuthenticated()', function() {
141227
it('should be true when there is a stored `token` cookie', inject(function(OAuth, OAuthToken) {
142228
OAuthToken.setToken({ token_type: 'bearer', access_token: 'foo', expires_in: 3600, refresh_token: 'bar' });

0 commit comments

Comments
 (0)