Commit 375b5743 authored by Robert Knight's avatar Robert Knight

Reload profile information when API tokens are changed by another client.

With this change, logins are automatically synced across different tabs
in the same browser session.
parent a23437ec
......@@ -15,6 +15,11 @@ module.exports = {
GROUPS_CHANGED: 'groupsChanged',
/** The logged-in user changed */
USER_CHANGED: 'userChanged',
/**
* API tokens were fetched and saved to local storage by another client
* instance.
*/
OAUTH_TOKENS_CHANGED: 'oauthTokensChanged',
// UI state changes
......
......@@ -2,6 +2,7 @@
var queryString = require('query-string');
var events = require('./events');
var resolve = require('./util/url-util').resolve;
var serviceConfig = require('./service-config');
......@@ -27,7 +28,7 @@ var serviceConfig = require('./service-config');
* an opaque access token.
*/
// @ngInject
function auth($http, $window, flash, localStorage, random, settings) {
function auth($http, $rootScope, $window, flash, localStorage, random, settings) {
/**
* Authorization code from auth popup window.
......@@ -214,6 +215,7 @@ function auth($http, $window, flash, localStorage, random, settings) {
// Reset cached token information. Tokens will be reloaded from storage
// on the next call to `tokenGetter()`.
tokenInfoPromise = null;
$rootScope.$broadcast(events.OAUTH_TOKENS_CHANGED);
}
});
}
......
......@@ -258,6 +258,10 @@ function session($http, $q, $resource, $rootScope, analytics, annotationUI, auth
return resource.load();
}
$rootScope.$on(events.OAUTH_TOKENS_CHANGED, () => {
reload();
});
return {
dismissSidebarTutorial: dismissSidebarTutorial,
load: resource.load,
......
......@@ -3,6 +3,8 @@
var angular = require('angular');
var { stringify } = require('query-string');
var events = require('../events');
var DEFAULT_TOKEN_EXPIRES_IN_SECS = 1000;
var TOKEN_KEY = 'hypothesis.oauth.hypothes%2Eis.token';
......@@ -51,6 +53,7 @@ class FakeWindow {
describe('sidebar.oauth-auth', function () {
var $rootScope;
var auth;
var nowStub;
var fakeHttp;
......@@ -137,8 +140,9 @@ describe('sidebar.oauth-auth', function () {
settings: fakeSettings,
});
angular.mock.inject((_auth_) => {
angular.mock.inject((_auth_, _$rootScope_) => {
auth = _auth_;
$rootScope = _$rootScope_;
});
});
......@@ -445,12 +449,8 @@ describe('sidebar.oauth-auth', function () {
});
});
it('reloads tokens when refreshed by another client instance', () => {
return login().then(() => {
return auth.tokenGetter();
}).then(token => {
assert.equal(token, 'firstAccessToken');
context('when another client instance saves new tokens', () => {
function notifyStoredTokenChange() {
// Trigger "storage" event as if another client refreshed the token.
var storageEvent = new Event('storage');
storageEvent.key = TOKEN_KEY;
......@@ -462,10 +462,29 @@ describe('sidebar.oauth-auth', function () {
});
fakeWindow.trigger(storageEvent);
}
return auth.tokenGetter();
}).then(token => {
assert.equal(token, 'storedAccessToken');
it('reloads tokens from storage', () => {
return login().then(() => {
return auth.tokenGetter();
}).then(token => {
assert.equal(token, 'firstAccessToken');
notifyStoredTokenChange();
return auth.tokenGetter();
}).then(token => {
assert.equal(token, 'storedAccessToken');
});
});
it('notifies other services about the change', () => {
var onTokenChange = sinon.stub();
$rootScope.$on(events.OAUTH_TOKENS_CHANGED, onTokenChange);
notifyStoredTokenChange();
assert.called(onTokenChange);
});
});
});
......
......@@ -6,7 +6,7 @@ var events = require('../events');
var mock = angular.mock;
describe('session', function () {
describe('sidebar.session', function () {
var $httpBackend;
var $rootScope;
......@@ -426,4 +426,25 @@ describe('session', function () {
});
});
});
context('when another client changes the current login', () => {
it('reloads the profile', () => {
fakeAuth.login = sinon.stub().returns(Promise.resolve());
fakeStore.profile.read.returns(Promise.resolve({
userid: 'acct:initial_user@hypothes.is',
}));
return session.load().then(() => {
// Simulate login change happening in a different tab.
fakeStore.profile.read.returns(Promise.resolve({
userid: 'acct:different_user@hypothes.is',
}));
$rootScope.$broadcast(events.OAUTH_TOKENS_CHANGED);
}).then(() => {
assert.equal(session.state.userid, 'acct:different_user@hypothes.is');
});
});
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment