From 0630ff671045e3828efecef7efb3b38ca21f3cc7 Mon Sep 17 00:00:00 2001
From: Jonathan Harris <jonathan@marginal.org.uk>
Date: Sun, 30 Dec 2018 20:02:20 +0000
Subject: [PATCH] Force re-authentication if credentials entered for wrong Cmdr

---
 EDMarketConnector.py |  6 ++++++
 README.md            |  4 +---
 companion.py         | 18 +++++++++++++++++-
 3 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/EDMarketConnector.py b/EDMarketConnector.py
index e6588a82..993e94e6 100755
--- a/EDMarketConnector.py
+++ b/EDMarketConnector.py
@@ -475,6 +475,12 @@ class AppWindow:
                 self.w.after(int(SERVER_RETRY * 1000), lambda:self.getandsend(event, True))
                 return	# early exit to avoid starting cooldown count
 
+        except companion.CmdrError as e:	# Companion API return doesn't match Journal
+            self.status['text'] = unicode(e)
+            play_bad = True
+            companion.session.invalidate()
+            self.login()
+
         except Exception as e:
             if __debug__: print_exc()
             self.status['text'] = unicode(e)
diff --git a/README.md b/README.md
index ddfcd639..319f5f5a 100644
--- a/README.md
+++ b/README.md
@@ -134,7 +134,7 @@ You won't be redirected to Frontier's authentication website and can't edit your
 - Your “E:D journal file location” setting is incorrect. See [above](#doesnt-auto-update-or-track-systems-visited).
 
 ### Error: Wrong Cmdr
-The Frontier server that supplies data to this app is supplying data for a different Cmdr than the one that you're currently playing. Either:
+The Frontier server that supplies data to this app is supplying data for a different Cmdr than the one that you're currently playing. You are redirected to Frontier's authentication website and prompted again for your username and password. Either:
 
 1. You have multiple accounts and the username/password setting is not for the account that you're currently playing; or
 2. You have reset your Cmdr but Frontier's server is still supplying data for the old Cmdr.
@@ -143,8 +143,6 @@ If 1 check your username/password settings.
 
 If 2 this problem may or may not resolve itself in time.
 
-This problem is tracked as [Issue #165](https://github.com/Marginal/EDMarketConnector/issues/165).
-
 ### I run two instances of E:D simultaneously, but I can't run two instances of EDMC
 EDMC supports this scenario if you run the second instance of E:D in a *different* user account - e.g. using `runas` on Windows. Run the second instance of EDMC in the same user account as the second instance of E:D.
 
diff --git a/companion.py b/companion.py
index df338c20..29606388 100644
--- a/companion.py
+++ b/companion.py
@@ -180,7 +180,8 @@ class Auth:
 
         # New request
         self.verifier = self.base64URLEncode(os.urandom(32))
-        self.state = self.base64URLEncode(os.urandom(8))	# Keep small to stay under 256 ShellExecute limit on Windows
+        self.state = self.base64URLEncode(os.urandom(8))
+        # Won't work under IE <= 10 : https://blogs.msdn.microsoft.com/ieinternals/2011/07/13/understanding-protocols/
         webbrowser.open('%s%s?response_type=code&approval_prompt=auto&client_id=%s&code_challenge=%s&code_challenge_method=S256&state=%s&redirect_uri=edmc://auth' % (SERVER_AUTH, URL_AUTH, CLIENT_ID, self.base64URLEncode(hashlib.sha256(self.verifier).digest()), self.state))
 
     def authorize(self, payload):
@@ -223,6 +224,16 @@ class Auth:
         self.dump(r)
         raise CredentialsError()
 
+    @staticmethod
+    def invalidate(cmdr):
+        cmdrs = config.get('cmdrs')
+        idx = cmdrs.index(cmdr)
+        tokens = config.get('fdev_apikeys') or []
+        tokens = tokens + [''] * (len(cmdrs) - len(tokens))
+        tokens[idx] = ''
+        config.set('fdev_apikeys', tokens)
+        config.save()	# Save settings now for use by command-line app
+
     def dump(self, r):
         print_exc()
         print 'Auth\t' + r.url, r.status_code, r.headers, r.text.encode('utf-8')
@@ -360,6 +371,11 @@ class Session:
                 if __debug__: print_exc()
         self.session = None
 
+    def invalidate(self):
+        # Force a full re-authentication
+        self.close()
+        Auth.invalidate(self.credentials['cmdr'])
+
     def dump(self, r):
         print_exc()
         print 'cAPI\t' + r.url, r.status_code, r.headers, r.text.encode('utf-8')