Skip to content
This repository has been archived by the owner on Dec 21, 2020. It is now read-only.

Commit

Permalink
feat: lock an auth token for a few minutes
Browse files Browse the repository at this point in the history
  • Loading branch information
Benjamin Coe committed Oct 14, 2016
1 parent 630e1c4 commit c807b99
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 4 deletions.
17 changes: 13 additions & 4 deletions lib/authorizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,20 @@ AuthorizerOAuth2.prototype.authorize = AuthorizerOAuth2.prototype.whoami = funct
if (err) return cb(err)
else if (!user.accessToken) return _this.session.oauthURL(cb, token)
else {
_this._checkToken(user.accessToken, function (err) {
if (err) {
return _this.session.oauthURL(cb, token)
} else {
// we hold a lock in redis for a few minutes, to prevent
// a thundering herd of auth requests.
_this.session.checkLock(token, function (locked) {
if (locked) {
return cb(null, user)
} else {
_this._checkToken(user.accessToken, function (err) {
if (err) {
return _this.session.oauthURL(cb, token)
} else {
_this.session.lock(token)
return cb(null, user)
}
})
}
})
}
Expand Down
29 changes: 29 additions & 0 deletions lib/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ var redis = require('redis')
function SessionOAuth2 (opts) {
_.extend(this, {
sessionLookupPrefix: 'user-',
sessionLockPrefix: 'user-token-lock-',
lockTime: 300,
client: redis.createClient(process.env.LOGIN_CACHE_REDIS),
clientId: process.env.OAUTH2_CLIENT_ID,
clientSecret: process.env.OAUTH2_CLIENT_SECRET,
Expand Down Expand Up @@ -41,6 +43,17 @@ SessionOAuth2.prototype.get = function (key, cb) {
})
}

SessionOAuth2.prototype.checkLock = function (key, cb) {
var _this = this

key = normalizeKey(key)

this.client.get(_this.sessionLockPrefix + key, function (_err, lock) {
if (lock) return cb(true)
else return cb(false)
})
}

SessionOAuth2.prototype.set = function (key, user, cb) {
var _this = this

Expand All @@ -49,6 +62,22 @@ SessionOAuth2.prototype.set = function (key, user, cb) {
_this.client.set(this.sessionLookupPrefix + key, JSON.stringify(user), cb)
}

SessionOAuth2.prototype.lock = function (key) {
var _this = this

key = normalizeKey(key)

_this.client.setex(this.sessionLockPrefix + key, this.lockTime, 'locked')
}

SessionOAuth2.prototype.unlock = function (key) {
var _this = this

key = normalizeKey(key)

_this.client.del(this.sessionLockPrefix + key)
}

SessionOAuth2.prototype.delete = function (key, cb) {
key = normalizeKey(key)

Expand Down
27 changes: 27 additions & 0 deletions test/authorizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ tap.test('it responds with session object if SSO dance is complete', function (t
.get('/user')
.reply(200)

session.unlock('ben@example.com-abc123')

session.set('ben@example.com-abc123', userComplete, function (err) {
t.equal(err, null)
authorizer.authorize({
Expand All @@ -25,6 +27,7 @@ tap.test('it responds with session object if SSO dance is complete', function (t
}
}, function (err, user) {
authorizer.end()
session.unlock('ben@example.com-abc123')
session.delete('ben@example.com-abc123')

profile.done()
Expand All @@ -35,6 +38,28 @@ tap.test('it responds with session object if SSO dance is complete', function (t
})
})

tap.test('does not check github if request is within timeout window', function (t) {
var authorizer = new Authorizer()

session.lock('ben@example.com-abc123')
session.set('ben@example.com-abc123', userComplete, function (err) {
t.equal(err, null)
authorizer.authorize({
headers: {
authorization: 'Bearer ben@example.com-abc123'
}
}, function (err, user) {
authorizer.end()
session.delete('ben@example.com-abc123')
session.unlock('ben@example.com-abc123')

t.equal(err, null)
t.equal(user.email, 'ben@example.com')
t.end()
})
})
})

tap.test('it returns error with login url if access token is no longer valid', function (t) {
var authorizer = new Authorizer()
var profile = nock('https://api.github.com')
Expand All @@ -50,6 +75,7 @@ tap.test('it returns error with login url if access token is no longer valid', f
}, function (err, user) {
authorizer.end()
session.delete('ben@example.com-abc123')
session.unlock('ben@example.com-abc123')

profile.done()
t.ok(err.message.indexOf('visit https://auth.example.com') !== -1)
Expand All @@ -69,6 +95,7 @@ tap.test('it returns error with login url if SSO dance is not complete', functio
}, function (err, user) {
authorizer.end()
session.delete('ben@example.com-abc123')
session.unlock('ben@example.com-abc123')

t.ok(err.message.indexOf('visit https://auth.example.com') !== -1)
t.end()
Expand Down

0 comments on commit c807b99

Please sign in to comment.