Skip to content
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

🐛 Checks for IE11 localStorage #54

Merged
merged 2 commits into from
Dec 20, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
62 changes: 62 additions & 0 deletions test/spec/oauthUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,68 @@ 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);
},
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 localStorage and sessionStorage are not 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 cookie-based storage if localStorage and sessionStorage are not 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