diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a340d0bd7a230524f3c983bdcfd3b9029dabf3e2..c0f2c9466999f885671d41fc7551809771df1fae 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -19,6 +19,8 @@ build-esap-gui:
         expire_in: 1 hour
         paths:
             - build
+    only:
+        - master
 
 deploy-esap-gui:
     stage: deploy
diff --git a/src/components/basket/EmptyBasketButton.js b/src/components/basket/EmptyBasketButton.js
index b4bb8be7481af6912e5b8eb2ff8627bda6a53f30..d45581cff79f83dc02995d9023e0dee2dc2a9dd8 100644
--- a/src/components/basket/EmptyBasketButton.js
+++ b/src/components/basket/EmptyBasketButton.js
@@ -1,23 +1,27 @@
 import React, { useContext, useState } from "react";
+import { useHistory } from "react-router-dom";
 import { Button, Modal } from "react-bootstrap";
 import { GlobalContext } from "../../contexts/GlobalContext";
 import { BasketContext } from "../../contexts/BasketContext";
 import { getTrashIcon, getOKIcon } from "../../utils/styling";
+import { saveBasket } from "./SaveBasketButton"
 
 export default function EmptyBasketButton(props) {
-    const { api_host, isAuthenticated } = useContext(GlobalContext);
+    const { api_host, isAuthenticated, isTokenValid, loginAgain } = useContext(GlobalContext);
     const basketContext = useContext(BasketContext);
     const { setHasChanged } = useContext(BasketContext);
-
     const [show, setShow] = useState(false);
 
     const handleClose = () => setShow(false);
     const handleShow = () => setShow(true);
 
-    function emptyBasket(){
+    let history = useHistory()
+
+    function emptyBasket(basketContext , api_host, isTokenValid, loginAgain, history){
         basketContext.setDatasets([])
         setHasChanged(true)
         setShow(false)
+        saveBasket(basketContext , api_host, isTokenValid, loginAgain, history)
     }
 
 
@@ -39,7 +43,9 @@ export default function EmptyBasketButton(props) {
                     </Modal.Header>
                     <Modal.Body>Are you sure you want to empty the shopping basket?</Modal.Body>
                     <Modal.Footer>
-                        <Button variant="success" onClick={emptyBasket}>
+                        <Button variant="success"
+                                onClick={() => emptyBasket(basketContext, api_host, isTokenValid, loginAgain, history)}
+                        >
                             {getOKIcon()}{' '}OK
                         </Button>
                         <Button variant="warning" onClick={handleClose}>
diff --git a/src/components/basket/LoadBasket.js b/src/components/basket/LoadBasket.js
index 4961f25a65ba752d2518b0707ff6bb39c10bf0ba..98f747b416c78bfea9f6a45480fda0039ee7130b 100644
--- a/src/components/basket/LoadBasket.js
+++ b/src/components/basket/LoadBasket.js
@@ -13,9 +13,8 @@ function ResponseToDatasets(response) {
     let datasets = shopping_cart.map((item) => {
         // make item_data an object instead of a string
 
-        console.log('itemdata = '+item.item_data)
+        //console.log('itemdata = '+item.item_data)
         let o = JSON.parse(item.item_data)
-        //alert(o)
         return o
         //return item.item_data
 
@@ -23,6 +22,8 @@ function ResponseToDatasets(response) {
 
     return datasets
 }
+
+
 export function loadBasket(basketContext, api_host, isAuthenticated){
     //alert('loadBasket: authenticated = '+isAuthenticated)
     if (!isAuthenticated) {
diff --git a/src/components/basket/SaveBasketButton.js b/src/components/basket/SaveBasketButton.js
index 729ea79746bea102e07d69083f1e17e2d3b903f0..2d566a516232027ff2c56da02c1a149d4f7fbed8 100644
--- a/src/components/basket/SaveBasketButton.js
+++ b/src/components/basket/SaveBasketButton.js
@@ -1,57 +1,71 @@
 import React, { useContext } from "react";
+import { useHistory } from "react-router-dom";
 import { Button } from "react-bootstrap";
 import { GlobalContext } from "../../contexts/GlobalContext";
 import { BasketContext } from "../../contexts/BasketContext";
 import axios from "axios";
 import { getShoppingIcon } from "../../utils/styling";
 
-export default function SaveBasketButton(props) {
-  const { api_host, isAuthenticated } = useContext(GlobalContext);
-  const basketContext = useContext(BasketContext);
-  const { hasChanged, setHasChanged } = useContext(BasketContext);
-
-  function saveBasket(basketData){
-    const payload = {shopping_cart: basketData};
-    console.log(payload);
 
+export function saveBasket(basketContext, api_host, isTokenValid, loginAgain, history){
+    const payload = {shopping_cart: basketContext.datasets};
+    console.log('saveBasket()')
     const profileUrl = api_host + "accounts/user-profiles/";
+
+    // check if he token is still valid
+    let token_is_valid = isTokenValid()
+    console.log('token valid: '+token_is_valid)
+
+    // if the token is not valid, then refresh it by logging in again
+    if (token_is_valid < 0) {
+
+        console.log('token no longer valid, retrying login...')
+        loginAgain(history)
+        //saveBasket(basketContext, api_host, isTokenValid, history)
+
+        return
+    }
+
     axios
-      .get(profileUrl, {
-          withCredentials: true,
-          //headers : {"Access-Control-Allow-Origin": "*"}
-      })
-      .then((response) => {
-        console.log(response.data)
-        const userProfileUrl = profileUrl + response.data.results[0].user_name + "/";
+        .get(profileUrl, {
+            withCredentials: true,
+        })
+        .then((response) => {
+            // build the userProfileUrl based on the user_name in the original id_token
+            console.log(response.data)
+            const userProfileUrl = profileUrl + response.data.results[0].user_name + "/";
 
-        axios
-          .patch(userProfileUrl, payload, {withCredentials: true})
-          .then((response) => {
-            console.log("patch", response);
-            basketContext.setHasChanged(false)
-          })
-          .catch((error) => {
+            // send the payload to the userProfile
+            axios
+                .patch(userProfileUrl, payload, {withCredentials: true})
+                .then((response) => {
+                    console.log("patch", response);
+                    basketContext.setHasChanged(false)
+                })
+                .catch((error) => {
+                    console.log(error);
+                });
+        })
+        .catch((error) => {
             console.log(error);
-            alert(error)
-          });
-      })
-      .catch((error) => {
-        console.log(error);
-        //alert(error)
-      });
-  }
+        });
+}
+
+export default function SaveBasketButton(props) {
+  const { api_host, isAuthenticated, isTokenValid, loginAgain } = useContext(GlobalContext);
+  const basketContext = useContext(BasketContext);
+  const { hasChanged, setHasChanged } = useContext(BasketContext);
 
-  // fake authentication when in 'development' mode.
-  //let authenticated = isAuthenticated || (process.env.NODE_ENV === "development")
-  let authenticated = isAuthenticated
+  let history = useHistory()
 
-  if (authenticated)  {
+  // only show the 'save basket' button when a user is logged in and something in the basket has changed
+  if (isAuthenticated)  {
       if (hasChanged) {
           return (
               <Button
                   type="button"
                   variant="primary"
-                  onClick={() => saveBasket(basketContext.datasets)}
+                  onClick={() => saveBasket(basketContext , api_host, isTokenValid, loginAgain, history)}
                   {...props}>
                   {getShoppingIcon("save_cart")} Save Basket</Button>
           )
@@ -60,11 +74,7 @@ export default function SaveBasketButton(props) {
       }
   }
   else{
-    return (<>
-      <Button variant="warning" disabled {...props}>
-        Log In to Enable Data Selection
-      </Button>
-    </>
-    );
+    return null
+
   }
 }
diff --git a/src/contexts/GlobalContext.js b/src/contexts/GlobalContext.js
index 62c9a2650d2e59e5b56bc0b9cb520bdecffe7cfa..174f751c0da15c61cdf1d9d76c15f767e4500ed1 100644
--- a/src/contexts/GlobalContext.js
+++ b/src/contexts/GlobalContext.js
@@ -11,8 +11,8 @@ function setProfileState(api_host,
                          setIdToken,
                          setAccessToken,
                          setTokenExpiration,
-                         setSecondsLeft,
                          setIsAuthenticated){
+
     const profileUrl = api_host + "accounts/user-profiles/";
     axios
     .get(profileUrl, {withCredentials: true})
@@ -30,12 +30,13 @@ function setProfileState(api_host,
     })
 
     .catch((error) => {
-        alert('GlobalContext.setProfileState:' + error)
+        console.log('GlobalContext.setProfileState:' + error)
         // when the token is no longer valid, .get with credentials will fail
         // mark the user as being logged out
         localStorage.removeItem('esap_logged_in')
         setIsAuthenticated(false);
         setLoggedInUserName("");
+
     });
 }
 
@@ -53,32 +54,30 @@ export function GlobalContextProvider({ children }) {
     const [idToken, setIdToken] = useState([]);
     const [accessToken, setAccessToken] = useState([]);
     const [tokenExpiration, setTokenExpiration] = useState([]);
-    const [secondsLeft, setSecondsLeft] = useState(undefined)
-
-    useEffect(() => {
-    axios
-      .get(api_host + "query/archives-uri")
-      .then((response) => setArchives(response.data.results));
-    }, [api_host]);
 
     useEffect(() => {
-    axios
-    .get(api_host + "query/configuration?name=navbar")
-      .then((response) => {
-        console.log("navbar response", response.data.configuration);
-        setNavbar(response.data.configuration);
-      });
-    }, [api_host]);
+        axios
+          .get(api_host + "query/archives-uri")
+          .then((response) => setArchives(response.data.results));
+        }, [api_host]);
+
+        useEffect(() => {
+        axios
+        .get(api_host + "query/configuration?name=navbar")
+          .then((response) => {
+            console.log("navbar response", response.data.configuration);
+            setNavbar(response.data.configuration);
+          });
+        }, [api_host]);
 
     // Zheng: "!!!!! Still need to look at sessionid and stuff"
     const [sessionid, setSessionid] = useState(getCookie("sessionid"));
     console.log("waah", sessionid, getCookie("sessionid"), document.cookie);
 
-    const [isAuthenticated, setIsAuthenticated] = useState(
-        sessionid ? true : false
-    );
+    const [isAuthenticated, setIsAuthenticated] = useState(sessionid ? true : false);
 
     const handleLogin = ({ history }) => {
+        console.log('handleLogin()')
       setIsAuthenticated(true);
       setSessionid(getCookie("sessionid"));
       history.replace("/");
@@ -88,17 +87,29 @@ export function GlobalContextProvider({ children }) {
           setIdToken,
           setAccessToken,
           setTokenExpiration,
-          setSecondsLeft,
           setIsAuthenticated);
       return null;
     };
 
+    // used when token expiration is detected before or during an axios fetch
+    const loginAgain = (history) => {
+        console.log('loginAgain()')
+        const loginUrl = api_host + "oidc/authenticate"
+        console.log('history = '+history)
+
+        //history.replace("/login");
+        window.location = loginUrl
+        //alert('history push')
+        //history.push("/login");
+    }
+
     const handleLogout = ({ history }) => {
-      setIsAuthenticated(false);
-      setSessionid(null);
-      history.replace("/");
-      setLoggedInUserName("");
-      localStorage.removeItem('esap_logged_in')
+        console.log('handleLogout()')
+        setIsAuthenticated(false);
+        setSessionid(null);
+        history.replace("/");
+        setLoggedInUserName("");
+        localStorage.removeItem('esap_logged_in')
     return null;
     };
 
@@ -112,6 +123,15 @@ export function GlobalContextProvider({ children }) {
         }
     }
 
+    // compare the tokenExpiration timestamp with the current time
+    // to determine if the token is still valid.
+    const isTokenValid = () => {
+        let expiration = Date.parse(tokenExpiration)
+        let now = Date.parse(new Date())
+        let valid = (expiration - now) > 0
+        return (expiration - now)/1000
+    }
+
   const handleError = (event) => {
     setIsAuthenticated(false);
     setSessionid(null);
@@ -140,9 +160,9 @@ export function GlobalContextProvider({ children }) {
           idToken,
           accessToken,
           tokenExpiration,
-          secondsLeft,
-          setSecondsLeft,
-          refreshLogin
+          refreshLogin,
+          isTokenValid,
+          loginAgain
       }}
     >
       {children}
diff --git a/src/contexts/QueryContext.js b/src/contexts/QueryContext.js
index 47af255063e07a0db8b6681fd00aff0c146023f9..bd9d7c8a573e729407426b236a1abd5ce1a3d91e 100644
--- a/src/contexts/QueryContext.js
+++ b/src/contexts/QueryContext.js
@@ -16,7 +16,7 @@ export function QueryContextProvider({ children }) {
     const [collection, setCollection] = useState();
     const [config, setConfig] = useState();
     const [configName, setConfigName] = useState(defaultConf);
-    const { api_host } = useContext(GlobalContext);
+    const { api_host, loginAgain } = useContext(GlobalContext);
     const [preview, setPreview] = useState(false);
     const [ds9, setDS9] = useState(false);
 
@@ -61,6 +61,11 @@ export function QueryContextProvider({ children }) {
                 let description = ". Configuration not loaded. Is ESAP-API online? " + api_host
                 console.log(error.toString() + description)
                 //alert(description)
+                // frantic attempt to solve cors errors by trying to trigger a login
+                //const loginUrl = api_host + "oidc/authenticate"
+                //alert('(QueryContext) token expired, attempting login: '+loginUrl)
+                //window.location = loginUrl
+                loginAgain()
                 return false
             });
 
@@ -95,9 +100,13 @@ export function QueryContextProvider({ children }) {
                 setConfig(config);
             })
             .catch((error) => {
+
                 let description = ". Configuration not loaded. Is ESAP-API online? " + api_host
                 console.log(error.toString() + description)
-                //alert(description)
+                alert(description)
+                //const loginUrl = api_host + "oidc/authenticate"
+                // window.location = loginUrl
+                loginAgain()
             });
 
         return true
diff --git a/src/routes/Routes.js b/src/routes/Routes.js
index ac421d3f8acca6bc3b63d7119f182d1c95c5ef7a..9c49d4b80f1bea83f14893b46d7bdafb36a7a22f 100644
--- a/src/routes/Routes.js
+++ b/src/routes/Routes.js
@@ -87,7 +87,7 @@ export default function Routes() {
 
         </Switch>
 
-      <footer><small>esap-gui version 23 aug 2021 - 15:00</small></footer>
+      <footer><small>esap-gui version 27 aug 2021 - 11:00</small></footer>
     </Router>
   );
 }
diff --git a/src/utils/ShowTimeLeft.js b/src/utils/ShowTimeLeft.js
deleted file mode 100644
index 1ce85680b97650b23b15b8b1832b603f8a6e4ffb..0000000000000000000000000000000000000000
--- a/src/utils/ShowTimeLeft.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import React, { useContext, useState, useRef, useEffect } from "react";
-import { GlobalContext } from "../contexts/GlobalContext";
-
-export function ShowTimeLeft() {
-    const { tokenExpiration } = useContext(GlobalContext);
-    const [timer, setTimer] = useState(undefined)
-    const [secondsLeft, setSecondsLeft] = useState(undefined)
-
-    useEffect(() => {
-            setTimer(setInterval(() => showTimeLeft(), 10000))
-
-            // this function is automatically called when the component unmounts
-            return function cleanup() {
-                clearInterval(timer);
-            }
-        },[]
-    );
-
-    function showTimeLeft() {
-        //console.log({tokenExpiration})
-        alert(tokenExpiration)
-        //let expiration = new Date(tokenExpiration)
-
-        setSecondsLeft(1)
-    }
-
-    alert('showtimeleft')
-
-    return (<h5>{secondsLeft}</h5>)
-
-}
\ No newline at end of file