Skip to content
This repository has been archived by the owner on Mar 19, 2019. It is now read-only.

Commit

Permalink
Bug 988847 — Tell when the generated url expires
Browse files Browse the repository at this point in the history
r=Natim
  • Loading branch information
almet committed Mar 28, 2014
1 parent 64b02b0 commit 6244745
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 24 deletions.
7 changes: 5 additions & 2 deletions loop/index.js
Expand Up @@ -226,9 +226,12 @@ app.post('/call-url', sessions.requireSession, sessions.attachSession,
if (expiresIn !== undefined) {
tokenPayload.expires = (Date.now() / tokenlib.ONE_HOUR) + expiresIn;
}
var token = tokenManager.encode(tokenPayload);
var tokenWrapper = tokenManager.encode(tokenPayload);
var host = req.protocol + "://" + req.get('host');
res.json(200, {call_url: host + "/calls/" + token});
res.json(200, {
call_url: host + "/calls/" + tokenWrapper.token,
expiresAt: tokenWrapper.payload.expires
});
});

/**
Expand Down
19 changes: 17 additions & 2 deletions loop/tokenlib.js
Expand Up @@ -50,6 +50,15 @@ function TokenManager(options) {
}

TokenManager.prototype = {

/**
* Encode the given data.
*
* Returns an object with two keys:
* - token, the encoded token;
* - payload, the data contained inside the token (it may contain additional
data than the one passed in the argument)
*/
encode: function(data) {
var payload, mac, hmac, cipher, encipheredPayload;
var expires = data.expires || (Date.now() / ONE_HOUR) + this.timeout;
Expand Down Expand Up @@ -77,7 +86,10 @@ TokenManager.prototype = {
// keep the first `macSize` bytes only, so we avoid huge MAC
mac = hmac.read().slice(0, this.macSize);

return base64.encode(Buffer.concat([IV, encipheredPayload, mac]));
return {
payload: data,
token: base64.encode(Buffer.concat([IV, encipheredPayload, mac]))
};
},

decode: function(token) {
Expand Down Expand Up @@ -124,4 +136,7 @@ TokenManager.prototype = {
};


module.exports = {TokenManager: TokenManager};
module.exports = {
TokenManager: TokenManager,
ONE_HOUR: ONE_HOUR
};
25 changes: 20 additions & 5 deletions test/functional_test.js
Expand Up @@ -321,7 +321,7 @@ describe("HTTP API exposed by the server", function() {
clock.restore();
});

it.only("should accept an expiresIn parameter", function(done) {
it("should accept an expiresIn parameter", function(done) {
jsonReq
.set('Cookie', sessionCookie)
.expect(200)
Expand Down Expand Up @@ -359,6 +359,18 @@ describe("HTTP API exposed by the server", function() {
done(err);
});
});

it("should return the expiration date of the call-url", function(done) {
jsonReq
.set('Cookie', sessionCookie)
.expect(200)
.send({callerId: callerId})
.end(function(err, res) {
var expiresAt = res.body && res.body.expiresAt;
expect(expiresAt).eql(387830);
done();
});
});
});
});

Expand Down Expand Up @@ -392,6 +404,9 @@ describe("HTTP API exposed by the server", function() {
.send({}) // XXX sending nothing fails here, investigate
.expect(400)
.end(function(err, res) {
if (err) {
throw err;
}
expectFormatedError(res.body, "body", "simple_push_url");
done();
});
Expand Down Expand Up @@ -521,7 +536,7 @@ describe("HTTP API exposed by the server", function() {
uuid: uuid,
user: user,
callerId: callerId
});
}).token;
supertest(app)
.get('/calls/' + token)
.expect("Location", conf.get("webAppUrl").replace("{token}", token))
Expand All @@ -546,7 +561,7 @@ describe("HTTP API exposed by the server", function() {
token = tokenManager.encode({
uuid: uuid,
user: user
});
}).token;
req = supertest(app)
.del('/call-url/' + token)
.set('Authorization', 'BrowserID ' + expectedAssertion)
Expand Down Expand Up @@ -579,7 +594,7 @@ describe("HTTP API exposed by the server", function() {
var token = tokenManager.encode({
uuid: "1234",
user: "h4x0r"
});
}).token;
req = supertest(app)
.del('/call-url/' + token)
.set('Authorization', 'BrowserID ' + expectedAssertion)
Expand Down Expand Up @@ -708,7 +723,7 @@ describe("HTTP API exposed by the server", function() {
uuid: uuid,
user: user,
callerId: callerId
});
}).token;

sandbox.stub(request, "put", function(options) {
requests.push(options);
Expand Down
6 changes: 3 additions & 3 deletions test/index_test.js
Expand Up @@ -14,7 +14,7 @@ var urlsRevocationStore = require("../loop").urlsRevocationStore;
var validateToken = require("../loop").validateToken;
var requireParams = require("../loop").requireParams;

describe("index", function() {
describe("index.js", function() {
var jsonReq;

beforeEach(function() {
Expand Down Expand Up @@ -80,7 +80,7 @@ describe("index", function() {
var token = tokenManager.encode({
uuid: "1234",
user: "natim"
});
}).token;
urlsRevocationStore.add({uuid: "1234"}, function(err) {
if (err) {
throw err;
Expand All @@ -102,7 +102,7 @@ describe("index", function() {
uuid: "1234",
user: "natim",
callerId: "alexis"
});
}).token;

jsonReq
.get('/validateToken/' + token)
Expand Down
40 changes: 28 additions & 12 deletions test/tokenlib_test.js
Expand Up @@ -105,34 +105,48 @@ describe("TokenManager", function() {
macSecret: macSecret,
timeout: timeout
});
var token = tokenManager.encode({some: "data"});
var tokenWrapper = tokenManager.encode({some: "data"});

expect(tokenManager.decode(token)).to.deep.equal({
expect(tokenManager.decode(tokenWrapper.token)).to.deep.equal({
some: "data",
expires: fakeNow + timeout
});
expect(tokenWrapper.payload.expires).eql(fakeNow + timeout);
});
});

describe("#encode", function() {
it("should return a token string", function() {
var token = tokenManager.encode({some: "data"});
var token = tokenManager.encode({some: "data"}).token;
expect(token.constructor.name).to.be.equal("String");
});

it("should return a payload argument", function() {
var payload = tokenManager.encode({some: "data"}).payload;
expect(payload.constructor.name).to.be.equal("Object");
});

it("should return a payload with the decoded data", function() {
var payload = tokenManager.encode({some: "data"}).payload;
expect(payload).to.deep.eql({
some: "data",
expires: fakeNow + tokenManager.timeout
});
});

it("should return an base64 url safe value", function() {
var token = tokenManager.encode({some: "data"});
var token = tokenManager.encode({some: "data"}).token;
expect(base64.validate(token)).to.equal(true);
});

it("should parametrize the size of the token according to a given " +
"macSize parameter", function() {
var token1 = tokenManager.encode({some: "data"});
var token1 = tokenManager.encode({some: "data"}).token;
var token2 = (new tokenlib.TokenManager({
encryptionSecret: encryptionSecret,
macSecret: macSecret,
macSize: 128/8
})).encode({some: "data"});
})).encode({some: "data"}).token;
expect(token1.length < token2.length).to.equal(true);
});

Expand All @@ -149,7 +163,7 @@ describe("TokenManager", function() {
});

it("should add a default `expires` time", function() {
var token = tokenManager.encode({some: "data"});
var token = tokenManager.encode({some: "data"}).token;

expect(tokenManager.decode(token)).to.deep.equal({
some: "data",
Expand All @@ -161,17 +175,19 @@ describe("TokenManager", function() {
var expires = fakeNow + 10000; // now + 2 months (expired)
var token = tokenManager.encode({some: "data", expires: expires});

expect(tokenManager.decode(token)).to.deep.equal({
expect(tokenManager.decode(token.token)).to.deep.equal({
some: "data",
expires: expires
});

expect(token.payload.expires).to.deep.equal(expires);
});
});

describe("#decode", function() {
it("should decode a valid encoded token", function() {
var data = {some: "data"};
var token = tokenManager.encode(data);
var token = tokenManager.encode(data).token;

// XXX: here, the decoded data carries more than just the `some`
// property but also as an `expires` property. Despite this
Expand All @@ -198,7 +214,7 @@ describe("TokenManager", function() {
encryptionSecret: encryptionSecret,
macSecret: invalidMacSecret
});
var token = tokenForger.encode(data);
var token = tokenForger.encode(data).token;
var failure = function() {
tokenManager.decode(token);
};
Expand All @@ -211,7 +227,7 @@ describe("TokenManager", function() {
encryptionSecret: invalidEncryptionSecret,
macSecret: macSecret
});
var token = tokenForger.encode(data);
var token = tokenForger.encode(data).token;
var failure = function() {
tokenManager.decode(token);
};
Expand All @@ -221,7 +237,7 @@ describe("TokenManager", function() {
it("should throw an error if the token expired", function() {
var tenHoursAgo = (Date.now() / ONE_HOUR) - 10;
var data = {some: "data", expires: tenHoursAgo};
var token = tokenManager.encode(data);
var token = tokenManager.encode(data).token;
var failure = function() {
tokenManager.decode(token);
};
Expand Down

0 comments on commit 6244745

Please sign in to comment.