-
Notifications
You must be signed in to change notification settings - Fork 271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added caching to well-known configuration #48
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,15 +4,26 @@ var cookies = require('./cookies'); | |
var Q = require('q'); | ||
var AuthApiError = require('./errors/AuthApiError'); | ||
var config = require('./config'); | ||
var storageBuilder = require('./storageBuilder'); | ||
|
||
var httpCache = storageBuilder(localStorage, config.CACHE_STORAGE_NAME); | ||
|
||
function httpRequest(sdk, options) { | ||
options = options || {}; | ||
var url = options.url, | ||
method = options.method, | ||
args = options.args, | ||
dontSaveResponse = options.dontSaveResponse, | ||
cacheState = options.cacheState, | ||
accessToken = options.accessToken; | ||
|
||
if (options.responseCacheDuration) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we actually need to pass in responseCacheDuration, or can we just get it from the config? I.e. are we planning on exposing this to the user later on down the line? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It could change depending on the response you're trying to cache (in theory we could store the keys indefinitely and only request new ones when key validation fails). I guess I'm defaulting to 2 weeks for everything now, so I'll just change that here. |
||
var cacheContents = httpCache.getStorage(); | ||
var cachedResponse = cacheContents[url]; | ||
if (cachedResponse && Date.now()/1000 < cachedResponse.expiresAt) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we simplify and remove the math (i.e. just use Date.now() and work in ms) since we control both ends and don't plan on exposing this outwards? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had it that way originally, but thought this way was easier to read (and easier to understand when debugging the cache) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm also curious how we're planning on handling the keys call, specifically:
I.e. it doesn't look like we expose whether we retrieved the response from the cache or not - is that going to be a problem when we need to have logic like "see what we have, looks like it's not there, make a request to get the new info". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Code from the next PR
|
||
return Q.resolve(cachedResponse.response); | ||
} | ||
} | ||
|
||
var headers = { | ||
'Accept': 'application/json', | ||
'Content-Type': 'application/json', | ||
|
@@ -37,7 +48,7 @@ function httpRequest(sdk, options) { | |
res = JSON.parse(res); | ||
} | ||
|
||
if (!dontSaveResponse) { | ||
if (cacheState) { | ||
if (!res.stateToken) { | ||
cookies.deleteCookie(config.STATE_TOKEN_COOKIE_NAME); | ||
} | ||
|
@@ -47,6 +58,15 @@ function httpRequest(sdk, options) { | |
cookies.setCookie(config.STATE_TOKEN_COOKIE_NAME, res.stateToken, res.expiresAt); | ||
} | ||
|
||
if (res && options.responseCacheDuration) { | ||
var cache = httpCache.getStorage(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be nice to provide a method for "updateStorage" - namely, underneath the hood it's going to get, write, and set, but we can just set a new url with the value here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. makes sense to me |
||
cache[url] = { | ||
expiresAt: Math.floor(Date.now()/1000) + options.responseCacheDuration, | ||
response: res | ||
}; | ||
httpCache.setStorage(cache); | ||
} | ||
|
||
return res; | ||
}) | ||
.fail(function(resp) { | ||
|
@@ -79,23 +99,26 @@ function httpRequest(sdk, options) { | |
}); | ||
} | ||
|
||
function get(sdk, url, saveResponse) { | ||
function get(sdk, url, options) { | ||
url = util.isAbsoluteUrl(url) ? url : sdk.options.url + url; | ||
return httpRequest(sdk, { | ||
var getOptions = { | ||
url: url, | ||
method: 'GET', | ||
dontSaveResponse: !saveResponse | ||
}); | ||
method: 'GET' | ||
}; | ||
util.extend(getOptions, options); | ||
return httpRequest(sdk, getOptions); | ||
} | ||
|
||
function post(sdk, url, args, dontSaveResponse) { | ||
function post(sdk, url, args, options) { | ||
url = util.isAbsoluteUrl(url) ? url : sdk.options.url + url; | ||
return httpRequest(sdk, { | ||
var postOptions = { | ||
url: url, | ||
method: 'POST', | ||
args: args, | ||
dontSaveResponse: dontSaveResponse | ||
}); | ||
cacheState: true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, why is cacheState true for post? When would we actually cache a post response? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I think I see what's going on here - the "dontSaveResponse" from before was the idea of remembering the previous response in the authn flow. I think "cacheState" is a confusing variable name now that we have a cache that persists past the current auth flow. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cacheAuthnState? |
||
}; | ||
util.extend(postOptions, options); | ||
return httpRequest(sdk, postOptions); | ||
} | ||
|
||
module.exports = { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
var http = require('./http'); | ||
var util = require('./util'); | ||
var AuthSdkError = require('./errors/AuthSdkError'); | ||
var config = require('./config'); | ||
|
||
function isToken(obj) { | ||
if (obj && | ||
|
@@ -45,7 +46,9 @@ function loadPopup(src, options) { | |
|
||
function getWellKnown(sdk) { | ||
// TODO: Use the issuer when known (usually from the id_token) | ||
return http.get(sdk, sdk.options.url + '/.well-known/openid-configuration'); | ||
return http.get(sdk, sdk.options.url + '/.well-known/openid-configuration', { | ||
responseCacheDuration: config.DEFAULT_CACHE_DURATION | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Related to other comment, but seems like it'd be nicer to make the a boolean "cacheResponse" and just load the time from the config. Or do you foresee having different cache timeouts for different requests? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm fine with changing everything to use DEFAULT_CACHE_DURATION for now. |
||
}); | ||
} | ||
|
||
function validateClaims(sdk, claims, aud, iss) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
var AuthSdkError = require('./errors/AuthSdkError'); | ||
|
||
// storage must have getItem and setItem | ||
function storageBuilder(webstorage, storageName) { | ||
function getStorage() { | ||
var storageString = webstorage.getItem(storageName); | ||
storageString = storageString || '{}'; | ||
try { | ||
return JSON.parse(storageString); | ||
} catch(e) { | ||
throw new AuthSdkError('Unable to parse storage string: ' + storageName); | ||
} | ||
} | ||
|
||
function setStorage(storage) { | ||
try { | ||
var storageString = JSON.stringify(storage); | ||
webstorage.setItem(storageName, storageString); | ||
} catch(e) { | ||
throw new AuthSdkError('Unable to set storage: ' + storageName); | ||
} | ||
} | ||
|
||
function clearStorage() { | ||
setStorage({}); | ||
} | ||
|
||
return { | ||
getStorage: getStorage, | ||
setStorage: setStorage, | ||
clearStorage: clearStorage | ||
}; | ||
} | ||
|
||
module.exports = storageBuilder; |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,54 @@ | ||
define(function(require) { | ||
var OktaAuth = require('OktaAuth'); | ||
var oauthUtil = require('../../lib/oauthUtil'); | ||
var util = require('../util/util'); | ||
var wellKnown = require('../xhr/well-known'); | ||
|
||
describe('getWellKnown', function() { | ||
util.itMakesCorrectRequestResponse({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here it might be better to just test that "we get well known, and use the cached version when it's stored", i.e. the end-to-end where we can verify that we got the same response both times, but only made a request once There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, it's easy enough to add an extra getWellKnown request There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It also seems like we probably need a test for not using the cached item if it exceeds the time when it expired (1 day) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yup, will add |
||
title: 'caches response', | ||
setup: { | ||
calls: [ | ||
{ | ||
request: { | ||
method: 'get', | ||
uri: '/.well-known/openid-configuration' | ||
}, | ||
response: 'well-known' | ||
} | ||
], | ||
time: 1449699929 | ||
}, | ||
execute: function(test) { | ||
localStorage.clear(); | ||
return oauthUtil.getWellKnown(test.oa); | ||
}, | ||
expectations: function() { | ||
var cache = localStorage.getItem('okta-cache-storage'); | ||
expect(cache).toEqual(JSON.stringify({ | ||
'https://auth-js-test.okta.com/.well-known/openid-configuration': { | ||
expiresAt: 1449786329, | ||
response: wellKnown.response | ||
} | ||
})); | ||
} | ||
}); | ||
util.itMakesCorrectRequestResponse({ | ||
title: 'uses cached response', | ||
setup: { | ||
time: 1449699929 | ||
}, | ||
execute: function(test) { | ||
localStorage.setItem('okta-cache-storage', JSON.stringify({ | ||
'https://auth-js-test.okta.com/.well-known/openid-configuration': { | ||
expiresAt: 1449786329, | ||
response: wellKnown.response | ||
} | ||
})); | ||
return oauthUtil.getWellKnown(test.oa); | ||
} | ||
}); | ||
}); | ||
|
||
describe('getOAuthUrls', function() { | ||
function setupOAuthUrls(options) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something that we might want to consider for the future, but it seems like overkill to have a separate instance of storage for every storage key (as opposed to having 1 localStorage, and 1 cookieStorage for example that each know how to lookup the val given a key).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fair enough