diff --git a/__tests__/src/sagas/auth.test.js b/__tests__/src/sagas/auth.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..f4979f7bfce8b1cb54bab63ea5e9a0ef40202c73
--- /dev/null
+++ b/__tests__/src/sagas/auth.test.js
@@ -0,0 +1,153 @@
+import { call, select } from 'redux-saga/effects';
+import { expectSaga } from 'redux-saga-test-plan';
+import { Utils } from 'manifesto.js/dist-esmodule/Utils';
+import serviceFixture from '../../fixtures/version-2/canvasService.json';
+import ActionTypes from '../../../src/state/actions/action-types';
+import {
+  refetchInfoResponses,
+} from '../../../src/state/sagas/auth';
+import {
+  fetchInfoResponse,
+} from '../../../src/state/sagas/iiif';
+import {
+  getAccessTokens,
+  getWindows,
+  selectInfoResponses,
+  getVisibleCanvases,
+} from '../../../src/state/selectors';
+
+describe('IIIF Authentication sagas', () => {
+  describe('refetchInfoResponses', () => {
+    it('discards info responses that could hvae used the new access token', () => {
+      const serviceId = 'https://authentication.example.org/token';
+      const tokenService = { id: serviceId };
+
+      const authStanza = {
+        service: [{
+          '@context': 'http://iiif.io/api/auth/1/context.json',
+          '@id': 'https://authentication.example.org/login',
+          profile: 'http://iiif.io/api/auth/1/login',
+          service: [
+            {
+              '@id': serviceId,
+              profile: 'http://iiif.io/api/auth/1/token',
+            },
+          ],
+        }],
+      };
+
+      const x = {
+        id: 'x',
+        json: {
+          ...authStanza,
+        },
+      };
+
+      const y = {
+        id: 'y',
+        json: {
+          ...authStanza,
+        },
+      };
+
+      return expectSaga(refetchInfoResponses, { serviceId })
+        .provide([
+          [select(getAccessTokens), { [serviceId]: tokenService }],
+          [select(getWindows), {}],
+          [select(selectInfoResponses), { x, y }],
+        ])
+        .put({ infoId: 'x', type: ActionTypes.REMOVE_INFO_RESPONSE })
+        .put({ infoId: 'y', type: ActionTypes.REMOVE_INFO_RESPONSE })
+        .run();
+    });
+
+    it('ignores info responses that would not use the token', () => {
+      const serviceId = 'https://authentication.example.org/token';
+      const tokenService = { id: serviceId };
+
+      const authStanza = {
+        service: [{
+          '@context': 'http://iiif.io/api/auth/1/context.json',
+          '@id': 'https://authentication.example.org/login',
+          profile: 'http://iiif.io/api/auth/1/login',
+          service: [
+            {
+              '@id': 'https://authentication.example.org/some-other-token-service',
+              profile: 'http://iiif.io/api/auth/1/token',
+            },
+          ],
+        }],
+      };
+
+      const wrongService = {
+        id: 'wrongService',
+        json: {
+          ...authStanza,
+        },
+      };
+
+      const noAuth = {
+        id: 'noAuth',
+        json: {},
+      };
+
+      const noJson = {
+        id: 'noJson',
+      };
+
+      return expectSaga(refetchInfoResponses, { serviceId })
+        .provide([
+          [select(getAccessTokens), { [serviceId]: tokenService }],
+          [select(getWindows), {}],
+          [select(selectInfoResponses), { noAuth, noJson, wrongService }],
+        ])
+        .not.put({ infoId: 'noAuth', type: ActionTypes.REMOVE_INFO_RESPONSE })
+        .not.put({ infoId: 'noJson', type: ActionTypes.REMOVE_INFO_RESPONSE })
+        .not.put({ infoId: 'wrongService', type: ActionTypes.REMOVE_INFO_RESPONSE })
+        .run();
+    });
+
+    it('re-requests info responses for visible canvases', () => {
+      const serviceId = 'https://authentication.example.org/token';
+      const tokenService = { id: serviceId };
+
+      const authStanza = {
+        service: [{
+          '@context': 'http://iiif.io/api/auth/1/context.json',
+          '@id': 'https://authentication.example.org/login',
+          profile: 'http://iiif.io/api/auth/1/login',
+          service: [
+            {
+              '@id': serviceId,
+              profile: 'http://iiif.io/api/auth/1/token',
+            },
+          ],
+        }],
+      };
+
+      const window = {};
+      const canvases = [
+        Utils.parseManifest(serviceFixture).getSequences()[0].getCanvases()[0],
+      ];
+
+      const iiifInfoId = 'https://api.digitale-sammlungen.de/iiif/image/v2/bsb00122140_00001';
+      const infoResponse = {
+        id: iiifInfoId,
+        json: {
+          ...authStanza,
+        },
+      };
+
+      return expectSaga(refetchInfoResponses, { serviceId })
+        .provide([
+          [select(getAccessTokens), { [serviceId]: tokenService }],
+          [select(getWindows), { window }],
+          [select(getVisibleCanvases, { windowId: 'window' }), canvases],
+          [select(selectInfoResponses), { [iiifInfoId]: infoResponse }],
+          [call(fetchInfoResponse, { infoId: iiifInfoId }), {}],
+        ])
+        .call(fetchInfoResponse, { infoId: iiifInfoId })
+        .run();
+    });
+  });
+});
diff --git a/src/state/sagas/auth.js b/src/state/sagas/auth.js
new file mode 100644
index 0000000000000000000000000000000000000000..23fafe71772889f7297d48f64e0385c509c620ae
--- /dev/null
+++ b/src/state/sagas/auth.js
@@ -0,0 +1,59 @@
+import {
+  all, call, put, select, takeEvery,
+} from 'redux-saga/effects';
+import { Utils } from 'manifesto.js/dist-esmodule/Utils';
+import flatten from 'lodash/flatten';
+import ActionTypes from '../actions/action-types';
+import MiradorCanvas from '../../lib/MiradorCanvas';
+import {
+  selectInfoResponses,
+  getVisibleCanvases,
+  getWindows,
+} from '../selectors';
+import { fetchInfoResponse } from './iiif';
+
+/**
+ * Figure out what info responses could have used the access token service and:
+ *   - refetch, if they are currently visible
+ *   - throw them out (and lazy re-fetch) otherwise
+ */
+export function* refetchInfoResponses({ serviceId }) {
+  const windows = yield select(getWindows);
+
+  const canvases = yield all(
+    Object.keys(windows).map(windowId => select(getVisibleCanvases, { windowId })),
+  );
+
+  const visibleImageApiIds = flatten(flatten(canvases).map((canvas) => {
+    const miradorCanvas = new MiradorCanvas(canvas);
+    return miradorCanvas.imageServiceIds;
+  }));
+
+  const infoResponses = yield select(selectInfoResponses);
+  /** */
+  const haveThisTokenService = infoResponse => {
+    const services = Utils.getServices(infoResponse);
+    return services.some(e => {
+      const infoTokenService = Utils.getService(e, 'http://iiif.io/api/auth/1/token');
+      return infoTokenService && infoTokenService.id === serviceId;
+    });
+  };
+
+  const obsoleteInfoResponses = Object.values(infoResponses).filter(
+    i => i.json && haveThisTokenService(i.json),
+  );
+
+  yield all(obsoleteInfoResponses.map(({ id: infoId }) => {
+    if (visibleImageApiIds.includes(infoId)) {
+      return call(fetchInfoResponse, { infoId });
+    }
+    return put({ infoId, type: ActionTypes.REMOVE_INFO_RESPONSE });
+  }));
+}
+
+/** */
+export default function* authSaga() {
+  yield all([
+    takeEvery(ActionTypes.RECEIVE_ACCESS_TOKEN, refetchInfoResponses),
+  ]);
+}
diff --git a/src/state/sagas/iiif.js b/src/state/sagas/iiif.js
index e8d44578ded2d5451a7e0f4b9460b3b8bd871024..862783c10fcbe1b81246bbf8cca7464de67e1f59 100644
--- a/src/state/sagas/iiif.js
+++ b/src/state/sagas/iiif.js
@@ -194,22 +194,6 @@ export function* fetchResourceManifest({ manifestId, manifestJson }) {
   if (!manifests[manifestId]) yield* fetchManifest({ manifestId });
 }
 
-/** @private */
-export function* refetchInfoResponses({ serviceId }) {
-  const accessTokens = yield select(getAccessTokens);
-  const tokenService = accessTokens && accessTokens[serviceId];
-
-  if (!tokenService || tokenService.infoIds === []) return;
-
-  yield all(
-    tokenService.infoIds.map(infoId => call(fetchInfoResponse, { infoId, tokenService })),
-  );
-
-  // TODO: Other resources could be refetched too
-
-  yield put({ serviceId, type: ActionTypes.CLEAR_ACCESS_TOKEN_QUEUE });
-}
-
 /** */
 export function* fetchManifests(...manifestIds) {
   const manifests = yield select(getManifests);
@@ -227,7 +211,6 @@ export default function* iiifSaga() {
     takeEvery(ActionTypes.REQUEST_INFO_RESPONSE, fetchInfoResponse),
     takeEvery(ActionTypes.REQUEST_SEARCH, fetchSearchResponse),
     takeEvery(ActionTypes.REQUEST_ANNOTATION, fetchAnnotation),
-    takeEvery(ActionTypes.RECEIVE_ACCESS_TOKEN, refetchInfoResponses),
     takeEvery(ActionTypes.ADD_RESOURCE, fetchResourceManifest),
   ]);
 }
diff --git a/src/state/sagas/index.js b/src/state/sagas/index.js
index 3ebb80e450e713a8833833568d12182bc1854425..d3e23cca68b1591746be40fbf5c981e6c5dbd588 100644
--- a/src/state/sagas/index.js
+++ b/src/state/sagas/index.js
@@ -5,7 +5,8 @@ import {
 import appSaga from './app';
 import iiifSaga from './iiif';
 import windowSaga from './windows';
-import annotations from './annotations';
+import annotationsSaga from './annotations';
+import authSaga from './auth';
 
 /** */
 function* launchSaga(saga) {
@@ -23,10 +24,11 @@ function* launchSaga(saga) {
 function getRootSaga(pluginSagas = []) {
   return function* rootSaga() {
     const sagas = [
-      annotations,
+      annotationsSaga,
       appSaga,
       iiifSaga,
       windowSaga,
+      authSaga,
       ...pluginSagas,
     ];