Skip to content

Commit 6c4c001

Browse files
committed
updates to 6-4 (clean copy of 6-1)
1 parent 0690a0a commit 6c4c001

File tree

6 files changed

+103
-88
lines changed

6 files changed

+103
-88
lines changed

exercises/ch-6-ex-1/client.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ var server = app.listen(9000, 'localhost', function () {
1515
});
1616

1717
app.get("/*", function(req, res){
18-
res.sendFile(path.join(__dirname, "files/client/callback.html"));
18+
res.sendFile(path.join(__dirname, "files/client/index.html"));
1919
});
File renamed without changes.

exercises/ch-6-ex-4/authorizationServer.js

Lines changed: 100 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var randomstring = require("randomstring");
55
var cons = require('consolidate');
66
var nosql = require('nosql').load('database.nosql');
77
var querystring = require('querystring');
8+
var qs = require('qs');
89
var __ = require('underscore');
910
__.string = require('underscore.string');
1011

@@ -26,6 +27,7 @@ var authServer = {
2627

2728
// client information
2829
var clients = [
30+
2931
{
3032
"client_id": "oauth-client-1",
3133
"client_secret": "oauth-client-secret-1",
@@ -48,11 +50,6 @@ app.get('/', function(req, res) {
4850

4951
app.get("/authorize", function(req, res){
5052

51-
/*
52-
* Process the request, validate the client, and send the user to the approval page
53-
*/
54-
55-
5653
var client = getClient(req.query.client_id);
5754

5855
if (!client) {
@@ -68,15 +65,13 @@ app.get("/authorize", function(req, res){
6865
var rscope = req.query.scope ? req.query.scope.split(' ') : undefined;
6966
var cscope = client.scope ? client.scope.split(' ') : undefined;
7067
if (__.difference(rscope, cscope).length > 0) {
71-
// client asked for a scope it couldn't have
72-
var urlParsed = url.parse(req.query.redirect_uri);
73-
delete urlParsed.search; // this is a weird behavior of the URL library
74-
urlParsed.query = urlParsed.query || {};
75-
urlParsed.query.error = 'invalid_scope';
76-
res.redirect(url.format(urlParsed));
68+
var urlParsed = buildUrl(req.query.redirect_uri, {
69+
error: 'invalid_scope'
70+
});
71+
res.redirect(urlParsed);
7772
return;
7873
}
79-
74+
8075
var reqid = randomstring.generate(8);
8176

8277
requests[reqid] = req.query;
@@ -102,90 +97,85 @@ app.post('/approve', function(req, res) {
10297
if (req.body.approve) {
10398
if (query.response_type == 'code') {
10499
// user approved access
105-
var code = randomstring.generate(8);
106-
107-
var scope = __.filter(__.keys(req.body), function(s) { return __.string.startsWith(s, 'scope_'); })
108-
.map(function(s) { return s.slice('scope_'.length); });
100+
101+
var rscope = getScopesFromForm(req.body);
109102
var client = getClient(query.client_id);
110103
var cscope = client.scope ? client.scope.split(' ') : undefined;
111-
if (__.difference(scope, cscope).length > 0) {
112-
// client asked for a scope it couldn't have
113-
var urlParsed = url.parse(query.redirect_uri);
114-
delete urlParsed.search; // this is a weird behavior of the URL library
115-
urlParsed.query = urlParsed.query || {};
116-
urlParsed.query.error = 'invalid_scope';
117-
res.redirect(url.format(urlParsed));
104+
if (__.difference(rscope, cscope).length > 0) {
105+
var urlParsed = buildUrl(query.redirect_uri, {
106+
error: 'invalid_scope'
107+
});
108+
res.redirect(urlParsed);
118109
return;
119110
}
120111

112+
var code = randomstring.generate(8);
113+
121114
// save the code and request for later
122-
codes[code] = { authorizationEndpointRequest: query, scope: scope };
115+
116+
codes[code] = { request: query, scope: rscope };
123117

124-
var urlParsed = url.parse(query.redirect_uri);
125-
delete urlParsed.search; // this is a weird behavior of the URL library
126-
urlParsed.query = urlParsed.query || {};
127-
urlParsed.query.code = code;
128-
urlParsed.query.state = query.state;
129-
res.redirect(url.format(urlParsed));
118+
var urlParsed = buildUrl(query.redirect_uri, {
119+
code: code,
120+
state: query.state
121+
});
122+
res.redirect(urlParsed);
130123
return;
124+
} else if (query.response_type == 'token') {
131125

132-
} else if (query.response_type == 'token') {
133-
var scope = __.filter(__.keys(req.body), function(s) { return __.string.startsWith(s, 'scope_'); })
134-
.map(function(s) { return s.slice('scope_'.length); });
126+
var rscope = getScopesFromForm(req.body);
135127
var client = getClient(query.client_id);
136128
var cscope = client.scope ? client.scope.split(' ') : undefined;
137-
if (__.difference(scope, cscope).length > 0) {
138-
var urlParsed = url.parse(query.redirect_uri);
139-
delete urlParsed.search; // this is a weird behavior of the URL library
140-
urlParsed.hash = 'error=invalid_scope';
141-
res.redirect(url.format(urlParsed));
129+
if (__.difference(rscope, cscope).length > 0) {
130+
var urlParsed = buildUrl(query.redirect_uri,
131+
{},
132+
qs.stringify({error: 'invalid_scope'})
133+
);
134+
res.redirect(urlParsed);
142135
return;
143136
}
144137
var access_token = randomstring.generate();
145-
nosql.insert({ access_token: access_token, client_id: clientId, scope: scope });
146-
var cscope = null;
147-
if (scope) {
148-
cscope = scope.join(' ');
149-
}
138+
nosql.insert({ access_token: access_token, client_id: client.client_id, scope: rscope });
150139

151-
var token_response = { access_token: access_token, token_type: 'Bearer', scope: cscope };
152-
var urlParsed = url.parse(query.redirect_uri);
153-
delete urlParsed.search; // this is a weird behavior of the URL library
140+
var token_response = { access_token: access_token, token_type: 'Bearer', scope: rscope.join(' ') };
154141
if (query.state) {
155142
token_response.state = query.state;
156-
}
157-
urlParsed.hash = qs.stringify(token_response);
158-
res.redirect(url.format(urlParsed));
143+
}
144+
145+
var urlParsed = buildUrl(query.redirect_uri,
146+
{},
147+
qs.stringify(token_response)
148+
);
149+
res.redirect(urlParsed);
159150
return;
160151
} else {
161152
// we got a response type we don't understand
162-
var urlParsed = url.parse(query.redirect_uri);
163-
delete urlParsed.search; // this is a weird behavior of the URL library
164-
urlParsed.query = urlParsed.query || {};
165-
urlParsed.query.error = 'unsupported_response_type';
166-
res.redirect(url.format(urlParsed));
153+
var urlParsed = buildUrl(query.redirect_uri, {
154+
error: 'unsupported_response_type'
155+
});
156+
res.redirect(urlParsed);
167157
return;
168158
}
159+
169160
} else {
170161
// user denied access
171-
var urlParsed = url.parse(query.redirect_uri);
172-
delete urlParsed.search; // this is a weird behavior of the URL library
173-
urlParsed.query = urlParsed.query || {};
174-
urlParsed.query.error = 'access_denied';
175-
res.redirect(url.format(urlParsed));
162+
var urlParsed = buildUrl(query.redirect_uri, {
163+
error: 'access_denied'
164+
});
165+
res.redirect(urlParsed);
176166
return;
177167
}
178-
168+
179169
});
180170

181171
app.post("/token", function(req, res){
182-
172+
183173
var auth = req.headers['authorization'];
184174
if (auth) {
185175
// check the auth header
186-
var clientCredentials = new Buffer(auth.slice('basic '.length), 'base64').toString().split(':');
187-
var clientId = querystring.unescape(clientCredentials[0]);
188-
var clientSecret = querystring.unescape(clientCredentials[1]);
176+
var clientCredentials = decodeClientCredentials(auth);
177+
var clientId = clientCredentials.id;
178+
var clientSecret = clientCredentials.secret;
189179
}
190180

191181
// otherwise, check the post body
@@ -220,62 +210,92 @@ app.post("/token", function(req, res){
220210

221211
if (code) {
222212
delete codes[req.body.code]; // burn our code, it's been used
223-
if (code.authorizationEndpointRequest.client_id == clientId) {
213+
if (code.request.client_id == clientId) {
224214

225215
var access_token = randomstring.generate();
226216
var refresh_token = randomstring.generate();
227217

228218
nosql.insert({ access_token: access_token, client_id: clientId, scope: code.scope });
229219
nosql.insert({ refresh_token: refresh_token, client_id: clientId, scope: code.scope });
230220

221+
console.log('Issuing access token %s', access_token);
222+
231223
var token_response = { access_token: access_token, token_type: 'Bearer', refresh_token: refresh_token, scope: code.scope.join(' ') };
232224

233225
res.status(200).json(token_response);
234226
console.log('Issued tokens for code %s', req.body.code);
235227

236228
return;
237229
} else {
238-
console.log('Client mismatch, expected %s got %s', code.authorizationEndpointRequest.client_id, clientId);
230+
console.log('Client mismatch, expected %s got %s', code.request.client_id, clientId);
239231
res.status(400).json({error: 'invalid_grant'});
240232
return;
241233
}
234+
235+
242236
} else {
243237
console.log('Unknown code, %s', req.body.code);
244238
res.status(400).json({error: 'invalid_grant'});
245239
return;
246240
}
247241
} else if (req.body.grant_type == 'refresh_token') {
248-
nosql.all(function(token) {
249-
return (token.refresh_token == req.body.refresh_token);
250-
}, function(err, tokens) {
251-
if (tokens.length == 1) {
252-
var token = tokens[0];
242+
nosql.one(function(token) {
243+
if (token.refresh_token == req.body.refresh_token) {
244+
return token;
245+
}
246+
}, function(err, token) {
247+
if (token) {
248+
console.log("We found a matching refresh token: %s", req.body.refresh_token);
253249
if (token.client_id != clientId) {
254-
console.log('Invalid client using a refresh token, expected %s got %s', token.client_id, clientId);
255250
nosql.remove(function(found) { return (found == token); }, function () {} );
256-
res.status(400).end();
257-
return
251+
res.status(400).json({error: 'invalid_grant'});
252+
return;
258253
}
259-
console.log("We found a matching token: %s", req.body.refresh_token);
260254
var access_token = randomstring.generate();
261-
var token_response = { access_token: access_token, token_type: 'Bearer', refresh_token: req.body.refresh_token };
262255
nosql.insert({ access_token: access_token, client_id: clientId });
263-
console.log('Issuing access token %s for refresh token %s', access_token, req.body.refresh_token);
256+
var token_response = { access_token: access_token, token_type: 'Bearer', refresh_token: token.refresh_token };
264257
res.status(200).json(token_response);
265258
return;
266259
} else {
267260
console.log('No matching token was found.');
268-
res.status(401).end();
261+
res.status(400).json({error: 'invalid_grant'});
262+
return;
269263
}
270264
});
271265
} else {
272266
console.log('Unknown grant type %s', req.body.grant_type);
273267
res.status(400).json({error: 'unsupported_grant_type'});
274-
return;
275268
}
276-
277269
});
278270

271+
var buildUrl = function(base, options, hash) {
272+
var newUrl = url.parse(base, true);
273+
delete newUrl.search;
274+
if (!newUrl.query) {
275+
newUrl.query = {};
276+
}
277+
__.each(options, function(value, key, list) {
278+
newUrl.query[key] = value;
279+
});
280+
if (hash) {
281+
newUrl.hash = hash;
282+
}
283+
284+
return url.format(newUrl);
285+
};
286+
287+
var decodeClientCredentials = function(auth) {
288+
var clientCredentials = new Buffer(auth.slice('basic '.length), 'base64').toString().split(':');
289+
var clientId = querystring.unescape(clientCredentials[0]);
290+
var clientSecret = querystring.unescape(clientCredentials[1]);
291+
return { id: clientId, secret: clientSecret };
292+
};
293+
294+
var getScopesFromForm = function(body) {
295+
return __.filter(__.keys(body), function(s) { return __.string.startsWith(s, 'scope_'); })
296+
.map(function(s) { return s.slice('scope_'.length); });
297+
};
298+
279299
app.use('/', express.static('files/authorizationServer'));
280300

281301
// clear the database

exercises/ch-6-ex-4/client.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ var server = app.listen(9000, 'localhost', function () {
1515
});
1616

1717
app.get("/*", function(req, res){
18-
res.sendFile(path.join(__dirname, "files/client/callback.html"));
18+
res.sendFile(path.join(__dirname, "files/client/index.html"));
1919
});

exercises/ch-6-ex-4/files/authorizationServer/approve.html

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,6 @@ <h2>Approve this client?</h2>
5050
<% } %>
5151

5252
<form class="form" action="/approve" method="POST">
53-
<label>Select user:</label>
54-
<select name="user">
55-
<option value="alice">Alice</option>
56-
<option value="bob">Bob</option>
57-
</select>
5853
<input type="hidden" name="reqid" value="<%- reqid %>">
5954
<% if (scope) { %>
6055
<p>The client is requesting access to the following:</p>

exercises/ch-6-ex-4/files/client/callback.html renamed to exercises/ch-6-ex-4/files/client/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ <h2>Data from protected resource:</h2>
6464
var client = {
6565
'client_id': 'oauth-client-1',
6666
'redirect_uris': ['http://localhost:9000/callback'],
67-
'scope': 'openid profile email address phone'
67+
'scope': 'foo bar'
6868
};
6969

7070
// authorization server information

0 commit comments

Comments
 (0)