Skip to content

Commit

Permalink
🐛 Checks for IE11 localStorage
Browse files Browse the repository at this point in the history
Resolves: OKTA-105205
  • Loading branch information
lboyette-okta committed Dec 20, 2016
1 parent 562bf86 commit 0ff509f
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 19 deletions.
26 changes: 14 additions & 12 deletions lib/TokenManager.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
/* eslint complexity:[0,8] max-statements:[0,21] */
var util = require('./util');
var AuthSdkError = require('./errors/AuthSdkError');
var cookies = require('./cookies');
var storageBuilder = require('./storageBuilder');
var storageUtil = require('./storageUtil');
var Q = require('q');
var Emitter = require('tiny-emitter');
var config = require('./config');

// Provides webStorage-like interface for cookies
var cookieStorage = {
getItem: cookies.getCookie,
setItem: function(key, value) {
// Cookie shouldn't expire
cookies.setCookie(key, value, '2038-01-19T03:14:07.000Z');
}
};
var storageBuilder = require('./storageBuilder');

function emitExpired(tokenMgmtRef, key, token) {
tokenMgmtRef.emitter.emit('expired', key, token);
Expand Down Expand Up @@ -144,6 +136,16 @@ function TokenManager(sdk, options) {
options.autoRefresh = true;
}

if (options.storage === 'localStorage' && !storageUtil.browserHasLocalStorage()) {
util.warn('This browser doesn\'t support localStorage. Switching to sessionStorage.');
options.storage = 'sessionStorage';
}

if (options.storage === 'sessionStorage' && !storageUtil.browserHasSessionStorage()) {
util.warn('This browser doesn\'t support sessionStorage. Switching to cookie-based storage.');
options.storage = 'cookie';
}

var storage;
switch(options.storage) {
case 'localStorage':
Expand All @@ -153,7 +155,7 @@ function TokenManager(sdk, options) {
storage = storageBuilder(sessionStorage, config.TOKEN_STORAGE_NAME);
break;
case 'cookie':
storage = storageBuilder(cookieStorage, config.TOKEN_STORAGE_NAME);
storage = storageBuilder(storageUtil.getCookieStorage(), config.TOKEN_STORAGE_NAME);
break;
default:
throw new AuthSdkError('Unrecognized storage option');
Expand Down
7 changes: 3 additions & 4 deletions lib/http.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
/* eslint-disable complexity */
var util = require('./util');
var cookies = require('./cookies');
var storageUtil = require('./storageUtil');
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,
saveAuthnState = options.saveAuthnState,
accessToken = options.accessToken;
accessToken = options.accessToken,
httpCache = storageUtil.getHttpCache();

if (options.cacheResponse) {
var cacheContents = httpCache.getStorage();
Expand Down
5 changes: 2 additions & 3 deletions lib/oauthUtil.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
/* eslint-disable complexity, max-statements */
var http = require('./http');
var util = require('./util');
var storageUtil = require('./storageUtil');
var AuthSdkError = require('./errors/AuthSdkError');
var config = require('./config');
var storageBuilder = require('./storageBuilder');

var httpCache = storageBuilder(localStorage, config.CACHE_STORAGE_NAME);
var httpCache = storageUtil.getHttpCache();

function isToken(obj) {
if (obj &&
Expand Down
63 changes: 63 additions & 0 deletions lib/storageUtil.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
var cookies = require('./cookies');
var storageBuilder = require('./storageBuilder');
var config = require('./config');

// Building this as an object allows us to mock the functions in our tests
var storageUtil = {};

// IE11 bug that Microsoft doesn't plan to fix
// https://connect.microsoft.com/IE/Feedback/Details/1496040
storageUtil.browserHasLocalStorage = function() {
try {
if (storageUtil.getLocalStorage()) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
};

storageUtil.browserHasSessionStorage = function() {
try {
if (storageUtil.getSessionStorage()) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
};

storageUtil.getHttpCache = function() {
if (storageUtil.browserHasLocalStorage()) {
return storageBuilder(storageUtil.getLocalStorage(), config.CACHE_STORAGE_NAME);
} else if (storageUtil.browserHasSessionStorage()) {
return storageBuilder(storageUtil.getSessionStorage(), config.CACHE_STORAGE_NAME);
} else {
return storageBuilder(storageUtil.getCookieStorage(), config.CACHE_STORAGE_NAME);
}
};

storageUtil.getLocalStorage = function() {
return localStorage;
};

storageUtil.getSessionStorage = function() {
return sessionStorage;
};

// Provides webStorage-like interface for cookies
storageUtil.getCookieStorage = function() {
return {
getItem: cookies.getCookie,
setItem: function(key, value) {
// Cookie shouldn't expire
cookies.setCookie(key, value, '2038-01-19T03:14:07.000Z');
}
};
};

module.exports = storageUtil;
7 changes: 7 additions & 0 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ function getLink(obj, linkName, altName) {
}
}

function warn(text) {
/* eslint-disable no-console */
console.log('[okta-auth-sdk] WARN: ' + text);
/* eslint-enable */
}

function deprecate(text) {
/* eslint-disable no-console */
console.log('[okta-auth-sdk] DEPRECATION: ' + text);
Expand Down Expand Up @@ -236,6 +242,7 @@ module.exports = {
omit: omit,
find: find,
getLink: getLink,
warn: warn,
deprecate: deprecate,
deprecateWrap: deprecateWrap,
removeTrailingSlash: removeTrailingSlash
Expand Down
65 changes: 65 additions & 0 deletions test/spec/oauthUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,71 @@ define(function(require) {
}));
}
});
util.itMakesCorrectRequestResponse({
title: 'caches response in sessionStorage if localStorage isn\'t available',
setup: {
beforeClient: function() {
oauthUtilHelpers.mockLocalStorageError();
},
calls: [
{
request: {
method: 'get',
uri: '/.well-known/openid-configuration'
},
response: 'well-known'
}
],
time: 1449699929
},
execute: function(test) {
sessionStorage.clear();
return oauthUtil.getWellKnown(test.oa)
.then(function() {
return oauthUtil.getWellKnown(test.oa);
});
},
expectations: function() {
var cache = sessionStorage.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: 'caches response in cookie if neither localStorage nor sessionStorage is available',
setup: {
beforeClient: function() {
oauthUtilHelpers.mockLocalStorageError();
oauthUtilHelpers.mockSessionStorageError();
},
calls: [
{
request: {
method: 'get',
uri: '/.well-known/openid-configuration'
},
response: 'well-known'
}
],
time: 1449699929
},
execute: function(test) {
test.setCookieMock = util.mockSetCookie();
return oauthUtil.getWellKnown(test.oa);
},
expectations: function(test) {
expect(test.setCookieMock).toHaveBeenCalledWith('okta-cache-storage=' + JSON.stringify({
'https://auth-js-test.okta.com/.well-known/openid-configuration': {
expiresAt: 1449786329,
response: wellKnown.response
}
}) + '; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT;');
}
});
});

describe('getKey', function() {
Expand Down
18 changes: 18 additions & 0 deletions test/spec/tokenManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@ define(function(require) {
'test-idToken': tokens.standardIdTokenParsed
});
});
it('defaults to sessionStorage if localStorage isn\'t available', function() {
oauthUtil.mockLocalStorageError();
var client = setupSync();
client.tokenManager.add('test-idToken', tokens.standardIdTokenParsed);
oauthUtil.expectTokenStorageToEqual(sessionStorage, {
'test-idToken': tokens.standardIdTokenParsed
});
});
it('defaults to cookieStorage if localStorage nor sessionStorage is available', function() {
oauthUtil.mockLocalStorageError();
oauthUtil.mockSessionStorageError();
var client = setupSync();
var setCookieMock = util.mockSetCookie();
client.tokenManager.add('test-idToken', tokens.standardIdTokenParsed);
expect(setCookieMock).toHaveBeenCalledWith('okta-token-storage=' + JSON.stringify({
'test-idToken': tokens.standardIdTokenParsed
}) + '; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT;');
});
});

describe('add', function() {
Expand Down
13 changes: 13 additions & 0 deletions test/util/oauthUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ define(function(require) {
var wellKnown = require('../xhr/well-known');
var wellKnownSharedResource = require('../xhr/well-known-shared-resource');
var keys = require('../xhr/keys');
var storageUtil = require('../../lib/storageUtil');

var oauthUtil = {};

Expand All @@ -26,6 +27,18 @@ define(function(require) {
});
};

oauthUtil.mockLocalStorageError = function() {
spyOn(storageUtil, 'getLocalStorage').and.callFake(function() {
throw 'This function is not supported on this system.';
});
};

oauthUtil.mockSessionStorageError = function() {
spyOn(storageUtil, 'getSessionStorage').and.callFake(function() {
throw 'This function is not supported on this system.';
});
};

oauthUtil.loadWellKnownCache = function() {
localStorage.setItem('okta-cache-storage', JSON.stringify({
'https://auth-js-test.okta.com/.well-known/openid-configuration': {
Expand Down
3 changes: 3 additions & 0 deletions test/util/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ define(function(require) {
}
})
.then(function() {
if (options.beforeClient) {
options.beforeClient();
}

// 2. Setup OktaAuth
oa = new OktaAuth({
Expand Down

0 comments on commit 0ff509f

Please sign in to comment.