diff --git a/__tests__/src/components/App.test.js b/__tests__/src/components/App.test.js
index e67b7edf3203f1aa1304b2293d192969d9a743c8..5046765124baa2018ddb8769162a0b42a03b0fd9 100644
--- a/__tests__/src/components/App.test.js
+++ b/__tests__/src/components/App.test.js
@@ -2,8 +2,6 @@ import React from 'react';
 import { shallow } from 'enzyme';
 import PluginProvider from '../../../src/extend/PluginProvider';
 import AppProviders from '../../../src/containers/AppProviders';
-import AccessTokenSender from '../../../src/containers/AccessTokenSender';
-import AuthenticationSender from '../../../src/containers/AuthenticationSender';
 import { App } from '../../../src/components/App';
 
 /** */
@@ -21,7 +19,5 @@ describe('App', () => {
     expect(wrapper.find(PluginProvider).length).toBe(1);
     expect(wrapper.find(AppProviders).length).toBe(1);
     expect(wrapper.find('Suspense').length).toBe(1);
-    expect(wrapper.find(AuthenticationSender).length).toBe(1);
-    expect(wrapper.find(AccessTokenSender).length).toBe(1);
   });
 });
diff --git a/__tests__/src/components/AuthenticationLogout.test.js b/__tests__/src/components/AuthenticationLogout.test.js
deleted file mode 100644
index e120d6e9188e17a7cbcacfb18993dad313de7de4..0000000000000000000000000000000000000000
--- a/__tests__/src/components/AuthenticationLogout.test.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from 'react';
-import { shallow } from 'enzyme';
-import Fab from '@material-ui/core/Fab';
-import { AuthenticationLogout } from '../../../src/components/AuthenticationLogout';
-
-/**
- * Helper function to create a shallow wrapper around AuthenticationLogout
- */
-function createWrapper(props) {
-  return shallow(
-    <AuthenticationLogout
-      authServiceId="http://example.com/auth"
-      label="Log out now!"
-      logoutServiceId="http://example.com/logout"
-      status="ok"
-      t={key => key}
-      windowId="w"
-      {...props}
-    />,
-  );
-}
-
-describe('AuthenticationLogout', () => {
-  it('when status is not ok, render fragment', () => {
-    const wrapper = createWrapper({ status: 'fail' });
-    expect(wrapper.matchesElement(<></>)).toBe(true);
-  });
-  it('renders Fab with logout label', () => {
-    const wrapper = createWrapper();
-    expect(wrapper.find(Fab).length).toBe(1);
-    expect(wrapper.find(Fab).text()).toBe('Log out now!');
-  });
-  it('click opens a new window to logout and resets state', () => {
-    const mockWindow = {};
-    const open = jest.fn(() => mockWindow);
-    const reset = jest.fn(() => {});
-    const wrapper = createWrapper({ depWindow: { open }, resetAuthenticationState: reset });
-    expect(wrapper.find(Fab).props().onClick());
-    expect(open).toHaveBeenCalledWith('http://example.com/logout');
-    expect(reset).toHaveBeenCalledWith({ authServiceId: 'http://example.com/auth' });
-  });
-});
diff --git a/__tests__/src/components/AuthenticationSender.test.js b/__tests__/src/components/AuthenticationSender.test.js
deleted file mode 100644
index 4b18215e28673b93297a1e61bc9791b392c941a9..0000000000000000000000000000000000000000
--- a/__tests__/src/components/AuthenticationSender.test.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import React from 'react';
-import { shallow } from 'enzyme';
-import { NewWindow } from '../../../src/components/NewWindow';
-import { AuthenticationSender } from '../../../src/components/AuthenticationSender';
-
-/**
- * Helper function to create a shallow wrapper around ErrorDialog
- */
-function createWrapper(props) {
-  return shallow(
-    <AuthenticationSender
-      t={key => key}
-      handleInteraction={() => {}}
-      {...props}
-    />,
-  );
-}
-
-describe('AuthenticationSender', () => {
-  let wrapper;
-
-  it('renders nothing if there is no url', () => {
-    wrapper = createWrapper({});
-    expect(wrapper.matchesElement(<></>)).toBe(true);
-  });
-
-  it('renders properly', () => {
-    Object.defineProperty(window, 'origin', {
-      value: 'http://localhost',
-      writable: true,
-    });
-    wrapper = createWrapper({ url: 'http://example.com' });
-    expect(wrapper.find(NewWindow).length).toBe(1);
-    expect(wrapper.find(NewWindow).props().url).toBe('http://example.com?origin=http://localhost');
-  });
-
-  it('triggers an action when the window is unloaded', () => {
-    const handleInteraction = jest.fn();
-    wrapper = createWrapper({ handleInteraction, url: 'http://example.com' });
-    wrapper.find(NewWindow).simulate('close');
-
-    expect(handleInteraction).toHaveBeenCalledWith('http://example.com');
-  });
-});
diff --git a/__tests__/src/components/IIIFAuthentication.test.js b/__tests__/src/components/IIIFAuthentication.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..c43d762cfe973be06165903c5b2472dad96d069c
--- /dev/null
+++ b/__tests__/src/components/IIIFAuthentication.test.js
@@ -0,0 +1,95 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import WindowAuthenticationBar from '../../../src/containers/WindowAuthenticationBar';
+import { NewWindow } from '../../../src/components/NewWindow';
+import { AccessTokenSender } from '../../../src/components/AccessTokenSender';
+import { IIIFAuthentication } from '../../../src/components/IIIFAuthentication';
+
+/**
+ * Helper function to create a shallow wrapper around IIIFAuthentication
+ */
+function createWrapper(props) {
+  return shallow(
+    <IIIFAuthentication
+      accessTokenServiceId="http://example.com/token"
+      authServiceId="http://example.com/auth"
+      failureDescription="... and this is why."
+      failureHeader="Login failed"
+      handleAuthInteraction={() => {}}
+      isInteractive
+      logoutServiceId="http://example.com/logout"
+      resetAuthenticationState={() => {}}
+      resolveAccessTokenRequest={() => {}}
+      resolveAuthenticationRequest={() => {}}
+      t={key => key}
+      windowId="w"
+      {...props}
+    />,
+  );
+}
+
+describe('IIIFAuthentication', () => {
+  describe('without an auth service', () => {
+    it('renders nothing', () => {
+      const wrapper = createWrapper({ authServiceId: null });
+      expect(wrapper.isEmptyRender()).toBe(true);
+    });
+  });
+  describe('with an available auth service', () => {
+    it('renders a login bar', () => {
+      const handleAuthInteraction = jest.fn();
+      const wrapper = createWrapper({ handleAuthInteraction });
+      expect(wrapper.find(WindowAuthenticationBar).length).toBe(1);
+      expect(wrapper.find(WindowAuthenticationBar).simulate('confirm'));
+      expect(handleAuthInteraction).toHaveBeenCalledWith('w', 'http://example.com/auth');
+    });
+    it('renders nothing for a non-interactive login', () => {
+      const wrapper = createWrapper({ isInteractive: false });
+      expect(wrapper.isEmptyRender()).toBe(true);
+    });
+  });
+  describe('with a failed authentication', () => {
+    it('renders with an error message', () => {
+      const handleAuthInteraction = jest.fn();
+      const wrapper = createWrapper({ handleAuthInteraction, status: 'failed' });
+      expect(wrapper.find(WindowAuthenticationBar).length).toBe(1);
+      expect(wrapper.find(WindowAuthenticationBar).prop('confirmButton')).toEqual('retry');
+      expect(wrapper.find(WindowAuthenticationBar).prop('status')).toEqual('failed');
+      expect(wrapper.find(WindowAuthenticationBar).prop('header')).toEqual('Login failed');
+      expect(wrapper.find(WindowAuthenticationBar).prop('description')).toEqual('... and this is why.');
+      expect(wrapper.find(WindowAuthenticationBar).simulate('confirm'));
+      expect(handleAuthInteraction).toHaveBeenCalledWith('w', 'http://example.com/auth');
+    });
+  });
+  describe('in the middle of authenicating', () => {
+    it('does the IIIF access cookie behavior', () => {
+      const wrapper = createWrapper({ status: 'cookie' });
+      expect(wrapper.find(WindowAuthenticationBar).length).toBe(1);
+      expect(wrapper.find(NewWindow).length).toBe(1);
+      expect(wrapper.find(NewWindow).prop('url')).toContain('http://example.com/auth?origin=');
+    });
+    it('does the IIIF access token behavior', () => {
+      const wrapper = createWrapper({ status: 'token' });
+      expect(wrapper.find(WindowAuthenticationBar).length).toBe(1);
+      expect(wrapper.find(AccessTokenSender).length).toBe(1);
+      expect(wrapper.find(AccessTokenSender).prop('url')).toEqual('http://example.com/token');
+    });
+  });
+  describe('when logged in', () => {
+    it('renders a logout button', () => {
+      const openWindow = jest.fn();
+      const resetAuthenticationState = jest.fn();
+      const wrapper = createWrapper({ openWindow, resetAuthenticationState, status: 'ok' });
+      expect(wrapper.find(WindowAuthenticationBar).length).toBe(1);
+      expect(wrapper.find(WindowAuthenticationBar).prop('confirmButton')).toEqual('logout');
+      expect(wrapper.find(WindowAuthenticationBar).prop('hasLogoutService')).toEqual(true);
+
+      wrapper.find(WindowAuthenticationBar).simulate('confirm');
+
+      expect(openWindow).toHaveBeenCalledWith('http://example.com/logout', 'centerscreen');
+      expect(resetAuthenticationState).toHaveBeenCalledWith({
+        authServiceId: 'http://example.com/auth', tokenServiceId: 'http://example.com/token',
+      });
+    });
+  });
+});
diff --git a/__tests__/src/components/Window.test.js b/__tests__/src/components/Window.test.js
index e21220fa6b7efbf35eb33b892a992e702016db7a..c16b361fef230d10765295efaccef0786e4ec971 100644
--- a/__tests__/src/components/Window.test.js
+++ b/__tests__/src/components/Window.test.js
@@ -3,7 +3,7 @@ import { shallow } from 'enzyme';
 import { Window } from '../../../src/components/Window';
 import WindowTopBar from '../../../src/containers/WindowTopBar';
 import PrimaryWindow from '../../../src/containers/PrimaryWindow';
-import WindowAuthenticationControl from '../../../src/containers/WindowAuthenticationControl';
+import IIIFAuthentication from '../../../src/containers/IIIFAuthentication';
 import ErrorContent from '../../../src/containers/ErrorContent';
 
 /** create wrapper */
@@ -34,9 +34,9 @@ describe('Window', () => {
     wrapper = createWrapper();
     expect(wrapper.find(PrimaryWindow)).toHaveLength(1);
   });
-  it('renders <WindowAuthenticationControl>', () => {
+  it('renders <WindowAuthenticationBar>', () => {
     wrapper = createWrapper();
-    expect(wrapper.find(WindowAuthenticationControl)).toHaveLength(1);
+    expect(wrapper.find(IIIFAuthentication)).toHaveLength(1);
   });
   it('renders manifest error', () => {
     wrapper = createWrapper({ manifestError: 'Invalid JSON' });
diff --git a/__tests__/src/components/WindowAuthenticationBar.test.js b/__tests__/src/components/WindowAuthenticationBar.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..72e9b029922cac5e935d21f347103759ec7e3d80
--- /dev/null
+++ b/__tests__/src/components/WindowAuthenticationBar.test.js
@@ -0,0 +1,73 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import Button from '@material-ui/core/Button';
+import Collapse from '@material-ui/core/Collapse';
+import DialogActions from '@material-ui/core/DialogActions';
+import SanitizedHtml from '../../../src/containers/SanitizedHtml';
+import { WindowAuthenticationBar } from '../../../src/components/WindowAuthenticationBar';
+
+/**
+ * Helper function to create a shallow wrapper around AuthenticationLogout
+ */
+function createWrapper(props) {
+  return shallow(
+    <WindowAuthenticationBar
+      classes={{}}
+      hasLogoutService
+      confirmButton="Click here"
+      label="Log in to see more"
+      onConfirm={() => {}}
+      status="ok"
+      t={key => key}
+      windowId="w"
+      {...props}
+    />,
+  );
+}
+
+describe('AuthenticationControl', () => {
+  it('renders nothing if the user is logged in and there is no logout service', () => {
+    const wrapper = createWrapper({ hasLogoutService: false });
+    expect(wrapper.isEmptyRender()).toBe(true);
+  });
+
+  it('renders a non-collapsing version if there is no description', () => {
+    const wrapper = createWrapper({ description: undefined, header: undefined });
+    expect(wrapper.find(SanitizedHtml).at(0).props().htmlString).toEqual('Log in to see more');
+    expect(wrapper.find(Button).children().text()).toEqual('Click here');
+  });
+
+  it('renders a collapsable version if there is a description', () => {
+    const onConfirm = jest.fn();
+    const wrapper = createWrapper({ description: 'long description', header: 'header', onConfirm });
+    expect(wrapper.find(SanitizedHtml).at(0).props().htmlString).toEqual('Log in to see more');
+    expect(wrapper.find(Button).at(0).find('span').text()).toEqual('continue');
+    // is expandable
+    expect(wrapper.find(Collapse).prop('in')).toEqual(false);
+    wrapper.find(Button).at(0).simulate('click');
+    expect(wrapper.find(Collapse).prop('in')).toEqual(true);
+
+    // has more information
+    expect(wrapper.find(Collapse).find(SanitizedHtml).at(0).props().htmlString).toEqual('header');
+    expect(wrapper.find(Collapse).find(SanitizedHtml).at(1).props().htmlString).toEqual('long description');
+
+    // is recollapsable
+    wrapper.find(DialogActions).find(Button).at(0).simulate('click');
+    expect(wrapper.find(Collapse).prop('in')).toEqual(false);
+    wrapper.find(Button).at(0).simulate('click');
+
+    // starts the auth process
+    wrapper.find(DialogActions).find(Button).at(1).simulate('click');
+    expect(onConfirm).toHaveBeenCalled();
+  });
+
+  it('triggers an action when the confirm button is clicked', () => {
+    const onConfirm = jest.fn();
+    const wrapper = createWrapper({
+      onConfirm,
+    });
+
+    wrapper.find(Button).simulate('click');
+    expect(onConfirm).toHaveBeenCalled();
+  });
+});
diff --git a/__tests__/src/components/WindowAuthenticationControl.test.js b/__tests__/src/components/WindowAuthenticationControl.test.js
deleted file mode 100644
index ffa31741da69f6c345cc8ee1de67ac0ba56e12d3..0000000000000000000000000000000000000000
--- a/__tests__/src/components/WindowAuthenticationControl.test.js
+++ /dev/null
@@ -1,122 +0,0 @@
-import React from 'react';
-import { shallow } from 'enzyme';
-import Button from '@material-ui/core/Button';
-import Collapse from '@material-ui/core/Collapse';
-import DialogActions from '@material-ui/core/DialogActions';
-import SanitizedHtml from '../../../src/containers/SanitizedHtml';
-import { WindowAuthenticationControl } from '../../../src/components/WindowAuthenticationControl';
-import AuthenticationLogout from '../../../src/containers/AuthenticationLogout';
-
-/**
- * Helper function to create a shallow wrapper around WindowAuthenticationControl
- */
-function createWrapper(props) {
-  return shallow(
-    <WindowAuthenticationControl
-      t={key => key}
-      classes={{}}
-      degraded
-      handleAuthInteraction={() => {}}
-      label="authenticate"
-      windowId="w"
-      profile="http://iiif.io/api/auth/1/login"
-      {...props}
-    />,
-  );
-}
-
-describe('WindowAuthenticationControl', () => {
-  let wrapper;
-
-  it('renders AuthenticationLogout if it is not degraded', () => {
-    wrapper = createWrapper({ degraded: false });
-    expect(wrapper.find(AuthenticationLogout).length).toBe(1);
-  });
-
-  describe('with a non-interactive login', () => {
-    it('renders failure messages', () => {
-      wrapper = createWrapper({
-        degraded: true,
-        failureDescription: 'failure description',
-        failureHeader: 'failure header',
-        profile: 'http://iiif.io/api/auth/1/external',
-        status: 'failed',
-      });
-      expect(wrapper.find(SanitizedHtml).at(0).props().htmlString).toEqual('failure header');
-      expect(wrapper.find(SanitizedHtml).at(1).props().htmlString).toEqual('failure description');
-      expect(wrapper.find(DialogActions)).toHaveLength(1);
-      expect(wrapper.find(DialogActions).find(Button)).toHaveLength(2);
-      expect(wrapper.find(DialogActions).find(Button).at(1).children()
-        .text()).toEqual('retry');
-    });
-  });
-
-  it('renders properly', () => {
-    wrapper = createWrapper({ confirmLabel: 'some confirm label', description: 'some description' });
-    expect(wrapper.find(SanitizedHtml).at(2).props().htmlString).toEqual('some description');
-    expect(wrapper.find(DialogActions).find(Button)).toHaveLength(2);
-    expect(wrapper.find(DialogActions).find(Button).at(1).children()
-      .text()).toEqual('some confirm label');
-  });
-
-  it('hides the cancel button if there is nothing to collapose', () => {
-    wrapper = createWrapper({ classes: { topBar: 'topBar' }, confirmLabel: 'some confirm label' });
-    expect(wrapper.find('.topBar').children().find(Button).at(0)
-      .children()
-      .text()).toEqual('some confirm label');
-
-    expect(wrapper.find(DialogActions).find(Button)).toHaveLength(0);
-  });
-
-  it('shows the auth dialog when the login button is clicked', () => {
-    wrapper = createWrapper({ classes: { topBar: 'topBar' }, description: 'some description' });
-    wrapper.find('.topBar').props().onClick();
-    expect(wrapper.find(Collapse).props().in).toEqual(true);
-  });
-
-  it('triggers an action when the confirm button is clicked', () => {
-    const handleAuthInteraction = jest.fn();
-    wrapper = createWrapper({
-      confirmLabel: 'some confirm label',
-      description: 'some description',
-      handleAuthInteraction,
-      infoId: 'i',
-      serviceId: 's',
-    });
-    wrapper.instance().setState({ open: true });
-    expect(wrapper.find(Collapse).props().in).toEqual(true);
-
-    wrapper.find(DialogActions).find(Button).at(1).simulate('click');
-    expect(handleAuthInteraction).toHaveBeenCalledWith('w', 'i', 's');
-  });
-
-  it('displays a failure message if the login has failed', () => {
-    wrapper = createWrapper({
-      failureDescription: 'failure description',
-      failureHeader: 'failure header',
-      status: 'failed',
-    });
-
-    expect(wrapper.find(SanitizedHtml).at(0).props().htmlString).toEqual('failure header');
-    expect(wrapper.find(SanitizedHtml).at(1).props().htmlString).toEqual('failure description');
-
-    expect(wrapper.find(DialogActions).find(Button)).toHaveLength(2);
-    expect(wrapper.find(DialogActions).find(Button).at(1).children()
-      .text()).toEqual('login');
-  });
-
-  it('displays the login messages if the user dismisses the failure messages', () => {
-    wrapper = createWrapper({
-      failureDescription: 'failure description',
-      failureHeader: 'failure header',
-      status: 'failed',
-    });
-
-    expect(wrapper.find(SanitizedHtml).at(0).props().htmlString).toEqual('failure header');
-    expect(wrapper.find(SanitizedHtml).at(1).props().htmlString).toEqual('failure description');
-
-    wrapper.find(DialogActions).find(Button).at(0).simulate('click');
-
-    expect(wrapper.find(SanitizedHtml).at(0).props().htmlString).toEqual('authenticate');
-  });
-});
diff --git a/src/components/App.js b/src/components/App.js
index a415d6ff823e2b900aa84345f993ccf874858530..ed42b7aabf312e6b8df56865f8bb23311b8d06c8 100644
--- a/src/components/App.js
+++ b/src/components/App.js
@@ -2,8 +2,6 @@ import React, { Component, lazy, Suspense } from 'react';
 import PropTypes from 'prop-types';
 import PluginProvider from '../extend/PluginProvider';
 import AppProviders from '../containers/AppProviders';
-import AuthenticationSender from '../containers/AuthenticationSender';
-import AccessTokenSender from '../containers/AccessTokenSender';
 
 const WorkspaceArea = lazy(() => import('../containers/WorkspaceArea'));
 
@@ -22,8 +20,6 @@ export class App extends Component {
     return (
       <PluginProvider plugins={plugins}>
         <AppProviders dndManager={dndManager}>
-          <AuthenticationSender />
-          <AccessTokenSender />
           <Suspense
             fallback={<div />}
           >
diff --git a/src/components/AuthenticationLogout.js b/src/components/AuthenticationLogout.js
deleted file mode 100644
index 82c37de15f562c0053fa00a740d31ada12b1bfbb..0000000000000000000000000000000000000000
--- a/src/components/AuthenticationLogout.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import Fab from '@material-ui/core/Fab';
-
-/**
- *
- */
-export class AuthenticationLogout extends Component {
-  /** */
-  constructor(props) {
-    super(props);
-
-    this.handleLogout = this.handleLogout.bind(this);
-  }
-
-  /** */
-  handleLogout() {
-    const {
-      authServiceId, depWindow, logoutServiceId, resetAuthenticationState,
-    } = this.props;
-    (depWindow || window).open(logoutServiceId);
-
-    resetAuthenticationState({ authServiceId });
-  }
-
-  /** */
-  render() {
-    const {
-      label, status, t,
-    } = this.props;
-    if (status !== 'ok') return <></>;
-    return (
-      <Fab color="primary" variant="extended" onClick={this.handleLogout}>
-        {label || t('logout')}
-      </Fab>
-    );
-  }
-}
-
-AuthenticationLogout.propTypes = {
-  authServiceId: PropTypes.string,
-  depWindow: PropTypes.object, // eslint-disable-line react/forbid-prop-types
-  label: PropTypes.string,
-  logoutServiceId: PropTypes.string,
-  resetAuthenticationState: PropTypes.func.isRequired,
-  status: PropTypes.string,
-  t: PropTypes.func,
-};
-
-AuthenticationLogout.defaultProps = {
-  authServiceId: undefined,
-  depWindow: undefined,
-  label: undefined,
-  logoutServiceId: undefined,
-  status: undefined,
-  t: () => {},
-};
diff --git a/src/components/AuthenticationSender.js b/src/components/AuthenticationSender.js
deleted file mode 100644
index e277838069e5f7a88ed2d34f0609a4edec046962..0000000000000000000000000000000000000000
--- a/src/components/AuthenticationSender.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { NewWindow } from './NewWindow';
-
-/**
- * Opens a new window for click
- */
-export class AuthenticationSender extends Component {
-  /** */
-  constructor(props) {
-    super(props);
-
-    this.onClose = this.onClose.bind(this);
-  }
-
-  /** @private */
-  onClose() {
-    const { handleInteraction, url } = this.props;
-
-    handleInteraction(url);
-  }
-
-  /** */
-  render() {
-    const { url } = this.props;
-
-    if (!url) return <></>;
-
-    return <NewWindow name="IiifAuthenticationSender" url={`${url}?origin=${window.origin}`} features="centerscreen" onClose={this.onClose} />;
-  }
-}
-
-AuthenticationSender.propTypes = {
-  handleInteraction: PropTypes.func.isRequired,
-  url: PropTypes.string,
-};
-
-AuthenticationSender.defaultProps = {
-  url: undefined,
-};
diff --git a/src/components/IIIFAuthentication.js b/src/components/IIIFAuthentication.js
new file mode 100644
index 0000000000000000000000000000000000000000..098b6ca4a32a179879258e4968d1679df80a7633
--- /dev/null
+++ b/src/components/IIIFAuthentication.js
@@ -0,0 +1,190 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { AccessTokenSender } from './AccessTokenSender';
+import { NewWindow } from './NewWindow';
+import WindowAuthenticationBar from '../containers/WindowAuthenticationBar';
+
+/**
+ * Opens a new window for click
+ */
+export class IIIFAuthentication extends Component {
+  /** */
+  constructor(props) {
+    super(props);
+
+    this.performLogout = this.performLogout.bind(this);
+    this.onReceiveAccessTokenMessage = this.onReceiveAccessTokenMessage.bind(this);
+  }
+
+  /** */
+  onReceiveAccessTokenMessage(payload) {
+    const {
+      authServiceId, accessTokenServiceId, resolveAccessTokenRequest,
+    } = this.props;
+
+    resolveAccessTokenRequest(authServiceId, accessTokenServiceId, payload);
+  }
+
+  /** */
+  defaultAuthBarProps() {
+    const {
+      authServiceId, windowId, status, logoutServiceId,
+    } = this.props;
+
+    return {
+      authServiceId,
+      hasLogoutService: !!logoutServiceId,
+      status,
+      windowId,
+    };
+  }
+
+  /** handle the IIIF logout workflow */
+  performLogout() {
+    const {
+      accessTokenServiceId, authServiceId, features,
+      logoutServiceId, resetAuthenticationState, openWindow,
+    } = this.props;
+    openWindow(logoutServiceId, features);
+
+    resetAuthenticationState({ authServiceId, tokenServiceId: accessTokenServiceId });
+  }
+
+  /** Render the auth bar for logged in users */
+  renderLoggedIn() {
+    const {
+      isInteractive, t,
+    } = this.props;
+
+    if (!isInteractive) return null;
+
+    return (
+      <WindowAuthenticationBar
+        confirmButton={t('logout')}
+        onConfirm={this.performLogout}
+        {...this.defaultAuthBarProps()}
+      />
+    );
+  }
+
+  /** Render whatever shows up after the interactive login attempt fails */
+  renderFailure() {
+    const {
+      handleAuthInteraction, failureHeader: header, failureDescription: description, t,
+      authServiceId, windowId,
+    } = this.props;
+
+    return (
+      <WindowAuthenticationBar
+        header={header}
+        description={description}
+        confirmButton={t('retry')}
+        onConfirm={() => handleAuthInteraction(windowId, authServiceId)}
+        {...this.defaultAuthBarProps()}
+      />
+    );
+  }
+
+  /** Render the login bar after we're logging in */
+  renderLoggingInCookie() {
+    const {
+      accessTokenServiceId, authServiceId, resolveAuthenticationRequest, features,
+    } = this.props;
+
+    return (
+      <>
+        {this.renderLogin()}
+        <NewWindow name="IiifLoginSender" url={`${authServiceId}?origin=${window.origin}`} features={features} onClose={() => resolveAuthenticationRequest(authServiceId, accessTokenServiceId)} />
+      </>
+    );
+  }
+
+  /** Render the login bar after we're logging in */
+  renderLoggingInToken() {
+    const {
+      accessTokenServiceId,
+    } = this.props;
+
+    return (
+      <>
+        {this.renderLogin()}
+        <AccessTokenSender
+          handleAccessTokenMessage={this.onReceiveAccessTokenMessage}
+          url={accessTokenServiceId}
+        />
+      </>
+    );
+  }
+
+  /** Render a login bar */
+  renderLogin() {
+    const {
+      confirm, description, handleAuthInteraction, header, isInteractive, label,
+      authServiceId, windowId,
+    } = this.props;
+    if (!isInteractive) return null;
+
+    return (
+      <WindowAuthenticationBar
+        header={header}
+        description={description}
+        label={label}
+        confirmButton={confirm}
+        onConfirm={() => handleAuthInteraction(windowId, authServiceId)}
+        {...this.defaultAuthBarProps()}
+      />
+    );
+  }
+
+  /** */
+  render() {
+    const { authServiceId, status } = this.props;
+
+    if (!authServiceId) return null;
+
+    if (status === null) return this.renderLogin();
+    if (status === 'cookie') return this.renderLoggingInCookie();
+    if (status === 'token') return this.renderLoggingInToken();
+    if (status === 'failed') return this.renderFailure();
+    if (status === 'ok') return this.renderLoggedIn();
+
+    return null;
+  }
+}
+
+IIIFAuthentication.propTypes = {
+  accessTokenServiceId: PropTypes.string.isRequired,
+  authServiceId: PropTypes.string.isRequired,
+  confirm: PropTypes.string,
+  description: PropTypes.string,
+  failureDescription: PropTypes.string,
+  failureHeader: PropTypes.string,
+  features: PropTypes.string,
+  handleAuthInteraction: PropTypes.func.isRequired,
+  header: PropTypes.string,
+  isInteractive: PropTypes.bool,
+  label: PropTypes.string,
+  logoutServiceId: PropTypes.string,
+  openWindow: PropTypes.func,
+  resetAuthenticationState: PropTypes.func.isRequired,
+  resolveAccessTokenRequest: PropTypes.func.isRequired,
+  resolveAuthenticationRequest: PropTypes.func.isRequired,
+  status: PropTypes.oneOf(['logout', 'ok', 'token', 'cookie', 'failed', null]),
+  t: PropTypes.func,
+  windowId: PropTypes.string.isRequired,
+};
+
+IIIFAuthentication.defaultProps = {
+  confirm: undefined,
+  description: undefined,
+  failureDescription: undefined,
+  failureHeader: undefined,
+  features: 'centerscreen',
+  header: undefined,
+  isInteractive: true,
+  label: undefined,
+  logoutServiceId: undefined,
+  openWindow: window.open,
+  status: null,
+  t: k => k,
+};
diff --git a/src/components/Window.js b/src/components/Window.js
index 62466dd06d74ee869da7f31cbf7d73acd6e39f50..56d3bd45e6c893b89638f30836c3aaccf1c771ed 100644
--- a/src/components/Window.js
+++ b/src/components/Window.js
@@ -9,7 +9,7 @@ import PrimaryWindow from '../containers/PrimaryWindow';
 import CompanionArea from '../containers/CompanionArea';
 import MinimalWindow from '../containers/MinimalWindow';
 import ErrorContent from '../containers/ErrorContent';
-import WindowAuthenticationControl from '../containers/WindowAuthenticationControl';
+import IIIFAuthentication from '../containers/IIIFAuthentication';
 import { PluginHook } from './PluginHook';
 
 /**
@@ -44,7 +44,7 @@ export class Window extends Component {
           windowId={windowId}
           windowDraggable={windowDraggable}
         />
-        <WindowAuthenticationControl key="auth" windowId={windowId} />
+        <IIIFAuthentication windowId={windowId} />
       </div>
     );
     if (workspaceType === 'mosaic' && windowDraggable) {
diff --git a/src/components/WindowAuthenticationBar.js b/src/components/WindowAuthenticationBar.js
new file mode 100644
index 0000000000000000000000000000000000000000..c0765eacf06705cbc911c718bd24be80f866a3e9
--- /dev/null
+++ b/src/components/WindowAuthenticationBar.js
@@ -0,0 +1,133 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import Button from '@material-ui/core/Button';
+import Paper from '@material-ui/core/Paper';
+import Collapse from '@material-ui/core/Collapse';
+import DialogActions from '@material-ui/core/DialogActions';
+import Typography from '@material-ui/core/Typography';
+import LockIcon from '@material-ui/icons/LockSharp';
+import SanitizedHtml from '../containers/SanitizedHtml';
+import { PluginHook } from './PluginHook';
+
+/** */
+export class WindowAuthenticationBar extends Component {
+  /** */
+  constructor(props) {
+    super(props);
+
+    this.state = { open: false };
+    this.setOpen = this.setOpen.bind(this);
+    this.onSubmit = this.onSubmit.bind(this);
+  }
+
+  /** */
+  onSubmit() {
+    const { onConfirm } = this.props;
+    this.setOpen(false);
+    onConfirm();
+  }
+
+  /** Toggle the full description */
+  setOpen(open) {
+    this.setState(state => ({ ...state, open }));
+  }
+
+  /** */
+  render() {
+    const {
+      classes, confirmButton, continueLabel,
+      header, description, icon, label, t,
+      ruleSet, hasLogoutService, status, ConfirmProps,
+    } = this.props;
+
+    if (status === 'ok' && !hasLogoutService) return null;
+
+    const { open } = this.state;
+
+    const button = (
+      <Button onClick={this.onSubmit} className={classes.buttonInvert} autoFocus color="secondary" {...ConfirmProps}>
+        {confirmButton || t('login')}
+      </Button>
+    );
+
+    if (!description && !header) {
+      return (
+        <Paper square elevation={4} color="secondary" classes={{ root: classes.paper }}>
+          <div className={classes.topBar}>
+            { icon || <LockIcon className={classes.icon} /> }
+            <Typography className={classes.label} component="h3" variant="body1" color="inherit">
+              <SanitizedHtml htmlString={label} ruleSet={ruleSet} />
+            </Typography>
+            <PluginHook {...this.props} />
+            { button }
+          </div>
+        </Paper>
+      );
+    }
+
+    return (
+      <Paper square elevation={4} color="secondary" classes={{ root: classes.paper }}>
+        <Button fullWidth className={classes.topBar} onClick={() => this.setOpen(true)} component="div" color="inherit">
+          { icon || <LockIcon className={classes.icon} /> }
+          <Typography className={classes.label} component="h3" variant="body1" color="inherit">
+            <SanitizedHtml htmlString={label} ruleSet="iiif" />
+          </Typography>
+          <PluginHook {...this.props} />
+          <span className={classes.fauxButton}>
+            { !open && (
+              <Typography variant="button" color="inherit">
+                { continueLabel || t('continue') }
+              </Typography>
+            )}
+          </span>
+        </Button>
+        <Collapse
+          in={open}
+          onClose={() => this.setOpen(false)}
+        >
+          <Typography variant="body1" color="inherit" className={classes.expanded}>
+            <SanitizedHtml htmlString={header} ruleSet={ruleSet} />
+            { header && description ? ': ' : '' }
+            <SanitizedHtml htmlString={description} ruleSet={ruleSet} />
+          </Typography>
+          <DialogActions>
+            <Button onClick={() => this.setOpen(false)} color="inherit">
+              { t('cancel') }
+            </Button>
+
+            { button }
+          </DialogActions>
+        </Collapse>
+      </Paper>
+    );
+  }
+}
+
+WindowAuthenticationBar.propTypes = {
+  classes: PropTypes.objectOf(PropTypes.string).isRequired,
+  confirmButton: PropTypes.string,
+  ConfirmProps: PropTypes.object, // eslint-disable-line react/forbid-prop-types
+  continueLabel: PropTypes.string,
+  description: PropTypes.node,
+  hasLogoutService: PropTypes.bool,
+  header: PropTypes.node,
+  icon: PropTypes.node,
+  label: PropTypes.node.isRequired,
+  onConfirm: PropTypes.func.isRequired,
+  ruleSet: PropTypes.string,
+  status: PropTypes.string,
+  t: PropTypes.func,
+};
+
+WindowAuthenticationBar.defaultProps = {
+  confirmButton: undefined,
+  ConfirmProps: {},
+  continueLabel: undefined,
+  description: undefined,
+  hasLogoutService: true,
+  header: undefined,
+  icon: undefined,
+  ruleSet: 'iiif',
+  status: undefined,
+  t: k => k,
+};
diff --git a/src/components/WindowAuthenticationControl.js b/src/components/WindowAuthenticationControl.js
deleted file mode 100644
index 32b3cbef3856c4fffdf3adcb6af8c9cb050c6d5b..0000000000000000000000000000000000000000
--- a/src/components/WindowAuthenticationControl.js
+++ /dev/null
@@ -1,173 +0,0 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import Button from '@material-ui/core/Button';
-import Paper from '@material-ui/core/Paper';
-import Collapse from '@material-ui/core/Collapse';
-import DialogActions from '@material-ui/core/DialogActions';
-import Typography from '@material-ui/core/Typography';
-import LockIcon from '@material-ui/icons/LockSharp';
-import SanitizedHtml from '../containers/SanitizedHtml';
-import AuthenticationLogout from '../containers/AuthenticationLogout';
-
-/**
- */
-export class WindowAuthenticationControl extends Component {
-  /** */
-  constructor(props) {
-    super(props);
-
-    this.state = {
-      open: false,
-      showFailureMessage: true,
-    };
-
-    this.handleClickOpen = this.handleClickOpen.bind(this);
-    this.handleClose = this.handleClose.bind(this);
-    this.handleConfirm = this.handleConfirm.bind(this);
-  }
-
-  /** */
-  handleClickOpen() {
-    this.setState({ open: true });
-  }
-
-  /** */
-  handleClose() {
-    this.setState({ open: false, showFailureMessage: false });
-  }
-
-  /** */
-  handleConfirm() {
-    const {
-      handleAuthInteraction, infoId, serviceId, windowId,
-    } = this.props;
-    handleAuthInteraction(windowId, infoId, serviceId);
-    this.setState({ showFailureMessage: true });
-  }
-
-  /** */
-  isInteractive() {
-    const {
-      profile,
-    } = this.props;
-
-    return profile === 'http://iiif.io/api/auth/1/clickthrough' || profile === 'http://iiif.io/api/auth/1/login';
-  }
-
-  /** */
-  render() {
-    const {
-      classes,
-      confirmLabel,
-      degraded,
-      description,
-      failureDescription,
-      failureHeader,
-      header,
-      label,
-      profile,
-      status,
-      t,
-      windowId,
-    } = this.props;
-
-    const failed = status === 'failed';
-    if ((!degraded || !profile) && status !== 'fetching') return <AuthenticationLogout windowId={windowId} />;
-    if (!this.isInteractive() && !failed) return <></>;
-
-    const { showFailureMessage, open } = this.state;
-
-    const isInFailureState = showFailureMessage && failed;
-
-    const hasCollapsedContent = isInFailureState
-      ? failureDescription
-      : header || description;
-
-    const confirmButton = (
-      <Button onClick={this.handleConfirm} className={classes.buttonInvert} autoFocus color="secondary">
-        {confirmLabel || (this.isInteractive() ? t('login') : t('retry')) }
-      </Button>
-    );
-
-    return (
-      <Paper square elevation={4} color="secondary" classes={{ root: classes.paper }}>
-        <Button fullWidth className={classes.topBar} onClick={hasCollapsedContent ? this.handleClickOpen : this.handleConfirm} component="div" color="inherit">
-          <LockIcon className={classes.icon} />
-          <Typography className={classes.label} component="h3" variant="body1" color="inherit">
-            <SanitizedHtml htmlString={(isInFailureState ? failureHeader : label) || t('authenticationRequired')} ruleSet="iiif" />
-          </Typography>
-          <span className={classes.fauxButton}>
-            { hasCollapsedContent
-              ? !open && (
-              <Typography variant="button" color="inherit">
-                { t('continue') }
-              </Typography>
-              )
-              : confirmButton}
-          </span>
-        </Button>
-        {
-          hasCollapsedContent && (
-            <Collapse
-              in={open}
-              onClose={this.handleClose}
-            >
-              <Typography variant="body1" color="inherit" className={classes.expanded}>
-                {
-                  isInFailureState
-                    ? <SanitizedHtml htmlString={failureDescription || ''} ruleSet="iiif" />
-                    : (
-                      <>
-                        <SanitizedHtml htmlString={header || ''} ruleSet="iiif" />
-                        { header && description ? ': ' : '' }
-                        <SanitizedHtml htmlString={description || ''} ruleSet="iiif" />
-                      </>
-                    )
-                }
-              </Typography>
-              <DialogActions>
-                <Button onClick={this.handleClose} color="inherit">
-                  {t('cancel')}
-                </Button>
-                {confirmButton}
-              </DialogActions>
-            </Collapse>
-          )
-      }
-      </Paper>
-    );
-  }
-}
-
-WindowAuthenticationControl.propTypes = {
-  classes: PropTypes.objectOf(PropTypes.string).isRequired,
-  confirmLabel: PropTypes.string,
-  degraded: PropTypes.bool,
-  description: PropTypes.string,
-  failureDescription: PropTypes.string,
-  failureHeader: PropTypes.string,
-  handleAuthInteraction: PropTypes.func.isRequired,
-  header: PropTypes.string,
-  infoId: PropTypes.string,
-  label: PropTypes.string,
-  profile: PropTypes.string,
-  serviceId: PropTypes.string,
-  status: PropTypes.oneOf(['ok', 'fetching', 'failed', null]),
-  t: PropTypes.func,
-  windowId: PropTypes.string.isRequired,
-};
-
-WindowAuthenticationControl.defaultProps = {
-  confirmLabel: undefined,
-  degraded: false,
-  description: undefined,
-  failureDescription: undefined,
-  failureHeader: undefined,
-  header: undefined,
-  infoId: undefined,
-  label: undefined,
-  profile: undefined,
-  serviceId: undefined,
-  status: null,
-  t: () => {},
-};
diff --git a/src/containers/AccessTokenSender.js b/src/containers/AccessTokenSender.js
deleted file mode 100644
index eb8b51fea3aa7441fd6b2b04c43b33eec34a8b93..0000000000000000000000000000000000000000
--- a/src/containers/AccessTokenSender.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { compose } from 'redux';
-import { connect } from 'react-redux';
-import { withPlugins } from '../extend/withPlugins';
-import * as actions from '../state/actions';
-import { getAccessTokens } from '../state/selectors';
-import { AccessTokenSender } from '../components/AccessTokenSender';
-
-/**
- * mapStateToProps - to hook up connect
- * @memberof App
- * @private
- */
-const mapStateToProps = (state) => ({
-  url: (Object.values(getAccessTokens(state)).find(e => e.isFetching) || {}).id,
-});
-
-/**
- * mapDispatchToProps - used to hook up connect to action creators
- * @memberof App
- * @private
- */
-const mapDispatchToProps = {
-  handleAccessTokenMessage: actions.resolveAccessTokenRequest,
-};
-
-const enhance = compose(
-  connect(mapStateToProps, mapDispatchToProps),
-  withPlugins('AccessTokenSender'),
-);
-
-export default enhance(AccessTokenSender);
diff --git a/src/containers/AuthenticationLogout.js b/src/containers/AuthenticationLogout.js
deleted file mode 100644
index 1178992d1a2e45e993bbded92f091b62211cdb14..0000000000000000000000000000000000000000
--- a/src/containers/AuthenticationLogout.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import { compose } from 'redux';
-import { connect } from 'react-redux';
-import { withTranslation } from 'react-i18next';
-import { withStyles } from '@material-ui/core';
-import { withPlugins } from '../extend/withPlugins';
-
-import {
-  getCurrentCanvas,
-  selectAuthStatus,
-  selectCanvasAuthService,
-  selectLogoutAuthService,
-} from '../state/selectors';
-import * as actions from '../state/actions';
-import { AuthenticationLogout } from '../components/AuthenticationLogout';
-
-/**
- * mapStateToProps - to hook up connect
- * @memberof App
- * @private
- */
-const mapStateToProps = (state, { windowId }) => {
-  const canvasId = (getCurrentCanvas(state, { windowId }) || {}).id;
-  const service = selectCanvasAuthService(state, { canvasId, windowId });
-  const logoutService = selectLogoutAuthService(state, { canvasId, windowId });
-  return {
-    authServiceId: service && service.id,
-    label: logoutService && logoutService.getLabel()[0].value,
-    logoutServiceId: logoutService && logoutService.id,
-    status: service && selectAuthStatus(state, service),
-  };
-};
-
-const mapDispatchToProps = {
-  resetAuthenticationState: actions.resetAuthenticationState,
-};
-
-const styles = {};
-
-const enhance = compose(
-  connect(mapStateToProps, mapDispatchToProps),
-  withStyles(styles),
-  withTranslation(),
-  withPlugins('AuthenticationLogout'),
-);
-
-export default enhance(AuthenticationLogout);
diff --git a/src/containers/AuthenticationSender.js b/src/containers/AuthenticationSender.js
deleted file mode 100644
index 97ab2847d775bc1e5467ac1f47f91ff88c4b12d7..0000000000000000000000000000000000000000
--- a/src/containers/AuthenticationSender.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import { compose } from 'redux';
-import { connect } from 'react-redux';
-import { withPlugins } from '../extend/withPlugins';
-import * as actions from '../state/actions';
-import { getAuth, getConfig } from '../state/selectors';
-import { AuthenticationSender } from '../components/AuthenticationSender';
-
-/**
- * mapStateToProps - to hook up connect
- * @memberof App
- * @private
- */
-const mapStateToProps = (state) => ({
-  center: getConfig(state).window.authNewWindowCenter,
-  url: (Object.values(getAuth(state)).find(e => e.isFetching && e.profile !== 'http://iiif.io/api/auth/1/external') || {}).id,
-});
-
-/**
- * mapDispatchToProps - used to hook up connect to action creators
- * @memberof App
- * @private
- */
-const mapDispatchToProps = {
-  handleInteraction: actions.resolveAuthenticationRequest,
-};
-
-const enhance = compose(
-  connect(mapStateToProps, mapDispatchToProps),
-  withPlugins('AuthenticationSender'),
-);
-
-export default enhance(AuthenticationSender);
diff --git a/src/containers/IIIFAuthentication.js b/src/containers/IIIFAuthentication.js
new file mode 100644
index 0000000000000000000000000000000000000000..ba2eb5aed5724352d2a4b77cfcabe21633325b31
--- /dev/null
+++ b/src/containers/IIIFAuthentication.js
@@ -0,0 +1,94 @@
+import { connect } from 'react-redux';
+import { compose } from 'redux';
+import { withTranslation } from 'react-i18next';
+import { Utils } from 'manifesto.js/dist-esmodule/Utils';
+import { withPlugins } from '../extend/withPlugins';
+import * as actions from '../state/actions';
+import {
+  getCurrentCanvas,
+  getAuth,
+  selectCanvasAuthService,
+  getAccessTokens,
+} from '../state/selectors';
+import { IIIFAuthentication } from '../components/IIIFAuthentication';
+
+/**
+ * mapStateToProps - to hook up connect
+ * @memberof FullScreenButton
+ * @private
+ */
+const mapStateToProps = (state, { windowId }) => {
+  const canvasId = (getCurrentCanvas(state, { windowId }) || {}).id;
+  const service = selectCanvasAuthService(state, { canvasId, windowId });
+
+  const accessTokenService = service && (
+    Utils.getService(service, 'http://iiif.io/api/auth/1/token')
+  );
+  const logoutService = service && (
+    Utils.getService(service, 'http://iiif.io/api/auth/1/logout')
+  );
+
+  const authStatuses = getAuth(state) || {};
+  const authStatus = service && authStatuses[service.id];
+  const accessTokens = authStatus && accessTokenService && getAccessTokens(state);
+  const accessTokenStatus = accessTokens && Object.values(accessTokens).find(
+    e => e.id === accessTokenService.id,
+  );
+
+  const profile = service && service.getProfile();
+
+  let status;
+
+  if (!authStatus) {
+    status = null;
+  } else if (authStatus.ok) {
+    status = 'ok';
+  } else if (authStatus.isFetching) {
+    if (authStatus.windowId === windowId) {
+      status = 'cookie';
+    }
+  } else if (accessTokenStatus && accessTokenStatus.isFetching) {
+    if (authStatus.windowId === windowId) {
+      status = 'token';
+    }
+  } else {
+    status = 'failed';
+  }
+
+  const isInteractive = profile !== 'http://iiif.io/api/auth/1/external' && profile !== 'http://iiif.io/api/auth/1/kiosk';
+
+  return {
+    accessTokenServiceId: accessTokenService && accessTokenService.id,
+    authServiceId: service && service.id,
+    confirm: service && service.getConfirmLabel(),
+    description: service && service.getDescription(),
+    failureDescription: service && service.getFailureDescription(),
+    failureHeader: service && service.getFailureHeader(),
+    header: service && service.getHeader(),
+    isInteractive,
+    label: service && service.getLabel()[0].value,
+    logoutServiceId: logoutService && logoutService.id,
+    profile,
+    status,
+  };
+};
+
+/**
+ * mapDispatchToProps - used to hook up connect to action creators
+ * @memberof ManifestListItem
+ * @private
+ */
+const mapDispatchToProps = {
+  handleAuthInteraction: actions.addAuthenticationRequest,
+  resetAuthenticationState: actions.resetAuthenticationState,
+  resolveAccessTokenRequest: actions.resolveAccessTokenRequest,
+  resolveAuthenticationRequest: actions.resolveAuthenticationRequest,
+};
+
+const enhance = compose(
+  withTranslation(),
+  connect(mapStateToProps, mapDispatchToProps),
+  withPlugins('IIIFAuthentication'),
+);
+
+export default enhance(IIIFAuthentication);
diff --git a/src/containers/WindowAuthenticationBar.js b/src/containers/WindowAuthenticationBar.js
new file mode 100644
index 0000000000000000000000000000000000000000..881bf419a1423289b504995b616cb96270366050
--- /dev/null
+++ b/src/containers/WindowAuthenticationBar.js
@@ -0,0 +1,65 @@
+import { compose } from 'redux';
+import { withTranslation } from 'react-i18next';
+import { withStyles } from '@material-ui/core/styles';
+import { fade } from '@material-ui/core/styles/colorManipulator';
+import { withPlugins } from '../extend/withPlugins';
+import { WindowAuthenticationBar } from '../components/WindowAuthenticationBar';
+
+/**
+ * @param theme
+ * @returns {{typographyBody: {flexGrow: number, fontSize: number|string},
+ * windowTopBarStyle: {minHeight: number, paddingLeft: number, backgroundColor: string}}}
+ */
+const styles = theme => ({
+  buttonInvert: {
+    '&:hover': {
+      backgroundColor: fade(
+        theme.palette.secondary.contrastText, 1 - theme.palette.action.hoverOpacity,
+      ),
+    },
+    backgroundColor: theme.palette.secondary.contrastText,
+    marginLeft: theme.spacing(5),
+    paddingBottom: 0,
+    paddingTop: 0,
+  },
+  expanded: {
+    paddingLeft: theme.spacing(),
+    paddingRight: theme.spacing(),
+  },
+  failure: {
+    backgroundColor: theme.palette.error.dark,
+  },
+  fauxButton: {
+    marginLeft: theme.spacing(2.5),
+  },
+  icon: {
+    marginRight: theme.spacing(1.5),
+    verticalAlign: 'text-bottom',
+  },
+  label: {
+    lineHeight: 2.25,
+  },
+  paper: {
+    backgroundColor: theme.palette.secondary.main,
+    color: theme.palette.secondary.contrastText,
+    cursor: 'pointer',
+  },
+  topBar: {
+    '&:hover': {
+      backgroundColor: theme.palette.secondary.main,
+    },
+    alignItems: 'center',
+    display: 'flex',
+    justifyContent: 'inherit',
+    padding: theme.spacing(1),
+    textTransform: 'none',
+  },
+});
+
+const enhance = compose(
+  withTranslation(),
+  withStyles(styles),
+  withPlugins('WindowAuthenticationBar'),
+);
+
+export default enhance(WindowAuthenticationBar);
diff --git a/src/containers/WindowAuthenticationControl.js b/src/containers/WindowAuthenticationControl.js
deleted file mode 100644
index 97b8978ae201e4bc7b5ee87a19251741cc4009b3..0000000000000000000000000000000000000000
--- a/src/containers/WindowAuthenticationControl.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import { compose } from 'redux';
-import { connect } from 'react-redux';
-import { withTranslation } from 'react-i18next';
-import { withStyles } from '@material-ui/core';
-import { fade } from '@material-ui/core/styles/colorManipulator';
-import { withPlugins } from '../extend/withPlugins';
-import * as actions from '../state/actions';
-import {
-  getCurrentCanvas,
-  selectAuthStatus,
-  selectInfoResponse,
-  selectCanvasAuthService,
-} from '../state/selectors';
-import { WindowAuthenticationControl } from '../components/WindowAuthenticationControl';
-
-/**
- * mapStateToProps - to hook up connect
- * @memberof App
- * @private
- */
-const mapStateToProps = (state, { windowId }) => {
-  const canvasId = (getCurrentCanvas(state, { windowId }) || {}).id;
-  const service = selectCanvasAuthService(state, { canvasId, windowId });
-  const infoResponse = selectInfoResponse(state, { canvasId, windowId }) || {};
-
-  return {
-    confirmLabel: service && service.getConfirmLabel(),
-    degraded: infoResponse.degraded,
-    description: service && service.getDescription(),
-    failureDescription: service && service.getFailureDescription(),
-    failureHeader: service && service.getFailureHeader(),
-    header: service && service.getHeader(),
-    infoId: infoResponse.id,
-    label: service && service.getLabel()[0].value,
-    profile: service && service.getProfile(),
-    serviceId: service && service.id,
-    status: service && selectAuthStatus(state, service),
-  };
-};
-
-/**
- * mapDispatchToProps - used to hook up connect to action creators
- * @memberof App
- * @private
- */
-const mapDispatchToProps = {
-  handleAuthInteraction: actions.addAuthenticationRequest,
-};
-/**
- * @param theme
- * @returns {{typographyBody: {flexGrow: number, fontSize: number|string},
- * windowTopBarStyle: {minHeight: number, paddingLeft: number, backgroundColor: string}}}
- */
-const styles = theme => ({
-  buttonInvert: {
-    '&:hover': {
-      backgroundColor: fade(
-        theme.palette.secondary.contrastText, 1 - theme.palette.action.hoverOpacity,
-      ),
-    },
-    backgroundColor: theme.palette.secondary.contrastText,
-  },
-  expanded: {
-    paddingLeft: theme.spacing(),
-    paddingRight: theme.spacing(),
-  },
-  failure: {
-    backgroundColor: theme.palette.error.dark,
-  },
-  fauxButton: {
-    marginLeft: theme.spacing(2.5),
-  },
-  icon: {
-    marginRight: theme.spacing(1.5),
-    verticalAlign: 'text-bottom',
-  },
-  label: {
-    lineHeight: 2.25,
-  },
-  paper: {
-    backgroundColor: theme.palette.secondary.main,
-    color: theme.palette.secondary.contrastText,
-    cursor: 'pointer',
-  },
-  topBar: {
-    '&:hover': {
-      backgroundColor: theme.palette.secondary.main,
-    },
-    justifyContent: 'inherit',
-    textTransform: 'none',
-  },
-});
-const enhance = compose(
-  connect(mapStateToProps, mapDispatchToProps),
-  withStyles(styles),
-  withTranslation(),
-  withPlugins('WindowAuthenticationControl'),
-);
-
-export default enhance(WindowAuthenticationControl);