Repository: lutris/lutris

Issue:
ubisoft key error expiration
### Bug description

error when refreshing ubisoft account

### How to Reproduce

Steps to reproduce the behavior:
1. register ubisoft account
2. click refresh button

### Expected behavior

no error

### Log output

```shell
> (cd ~/Documents/lutris && poetry run ./bin/lutris -d)
INFO     2022-03-12 07:49:35,572 [startup.init_lutris:164]:Starting Lutris 0.5.10
WARNING  2022-03-12 07:49:35,805 [libretro.get_libretro_cores:24]:No folder at /home/r3r/.local/share/lutris/runners/retroarch/
INFO     2022-03-12 07:49:35,933 [startup.check_driver:65]:Running AMD Mesa driver 22.0.0 on AMD Radeon Vega 8 Graphics (raven, LLVM 13.0.0, DRM 3.41, 5.13.0-35-generic) (0x15d8)
INFO     2022-03-12 07:49:35,933 [startup.check_driver:77]:GPU: 1002:15D8 1043:876B (amdgpu drivers)
ERROR    2022-03-12 07:49:35,934 [startup.check_libs:112]:i386 libgnutls.so.30 missing (needed by gnutls)
DEBUG    2022-03-12 07:49:36,750 [http.get:83]:GET https://lutris.net/api/runtimes?enabled=1
DEBUG    2022-03-12 07:49:38,723 [http.get:83]:GET https://lutris.net/api/runtimes/icons
DEBUG    2022-03-12 07:49:40,205 [http.get:83]:GET https://api.github.com/repos/lutris/dxvk/releases
DEBUG    2022-03-12 07:49:42,044 [http.write_to_file:134]:Writing to /home/r3r/.local/share/lutris/runtime/dxvk/dxvk_versions.json
DEBUG    2022-03-12 07:49:42,071 [http.get:83]:GET https://api.github.com/repos/lutris/dxvk-nvapi/releases
DEBUG    2022-03-12 07:49:43,057 [http.write_to_file:134]:Writing to /home/r3r/.local/share/lutris/runtime/dxvk-nvapi/dxvk-nvapi_versions.json
DEBUG    2022-03-12 07:49:43,100 [http.get:83]:GET https://api.github.com/repos/lutris/vkd3d/releases
DEBUG    2022-03-12 07:49:44,065 [http.write_to_file:134]:Writing to /home/r3r/.local/share/lutris/runtime/vkd3d/vkd3d_versions.json
DEBUG    2022-03-12 07:49:44,068 [http.get:83]:GET https://api.github.com/repos/lutris/d3d_extras/releases
DEBUG    2022-03-12 07:49:44,859 [http.write_to_file:134]:Writing to /home/r3r/.local/share/lutris/runtime/d3d_extras/d3d_extras_versions.json
DEBUG    2022-03-12 07:49:45,015 [http.get:83]:GET https://api.github.com/repos/lutris/dgvoodoo2/releases
DEBUG    2022-03-12 07:49:45,771 [http.write_to_file:134]:Writing to /home/r3r/.local/share/lutris/runtime/dgvoodoo2/dgvoodoo2_versions.json
INFO     2022-03-12 07:49:45,786 [startup.update_runtime:203]:Startup complete
DEBUG    2022-03-12 07:49:46,497 [lutriswindow.update_store:430]:Showing 153 games
DEBUG    2022-03-12 07:49:56,646 [lutriswindow.update_store:430]:Showing 0 games
DEBUG    2022-03-12 07:49:58,405 [base.wipe_game_cache:297]:Deleting ubisoft cache /home/r3r/.cache/lutris/ubisoft/library/
DEBUG    2022-03-12 07:49:58,405 [base.wipe_game_cache:121]:Deleting games from service-games for ubisoft
DEBUG    2022-03-12 07:49:58,427 [client._do_request_safe:74]:rememberMeTicket expiration time: 1645683512
DEBUG    2022-03-12 07:49:58,427 [client._refresh_ticket:148]:Refreshing ticket
INFO     2022-03-12 07:50:00,529 [client._do_request:66]:Response status: <Response [200]>
INFO     2022-03-12 07:50:00,827 [client._do_request:66]:Response status: <Response [401]>
DEBUG    2022-03-12 07:50:00,828 [client._refresh_remember_me:126]:Refreshing rememberMeTicket
INFO     2022-03-12 07:50:01,133 [client._do_request:66]:Response status: <Response [200]>
INFO     2022-03-12 07:50:01,457 [client._do_request:66]:Response status: <Response [401]>
ERROR    2022-03-12 07:50:01,457 [client._do_request_safe:95]:Unable to refresh authentication calling auth lost: 'expiration'
DEBUG    2022-03-12 07:50:01,460 [lutriswindow.update_store:430]:Showing 0 games
ERROR    2022-03-12 07:50:01,488 [jobs.target:36]:Error while completing task <bound method BaseService.reload of <ubisoft.UbisoftConnectService object at 0x7f729401ee00 (lutris+services+ubisoft+UbisoftConnectService at 0x5646ddbd5ec0)>>: <class 'KeyError'> 'expiration'
  File "/mnt/ac54dceb-73a5-4f94-b52c-cb7a426c0f29/Documents/lutris/lutris/util/jobs.py", line 34, in target
    result = self.function(*args, **kwargs)
  File "/mnt/ac54dceb-73a5-4f94-b52c-cb7a426c0f29/Documents/lutris/lutris/services/base.py", line 96, in reload
    self.load()
  File "/mnt/ac54dceb-73a5-4f94-b52c-cb7a426c0f29/Documents/lutris/lutris/services/ubisoft.py", line 150, in load
    self.client.authorise_with_stored_credentials(self.load_credentials())
  File "/mnt/ac54dceb-73a5-4f94-b52c-cb7a426c0f29/Documents/lutris/lutris/util/ubisoft/client.py", line 208, in authorise_with_stored_credentials
    self.post_sessions()
  File "/mnt/ac54dceb-73a5-4f94-b52c-cb7a426c0f29/Documents/lutris/lutris/util/ubisoft/client.py", line 306, in post_sessions
    return self._do_request_safe(
  File "/mnt/ac54dceb-73a5-4f94-b52c-cb7a426c0f29/Documents/lutris/lutris/util/ubisoft/client.py", line 80, in _do_request_safe
    self._refresh_auth()
  File "/mnt/ac54dceb-73a5-4f94-b52c-cb7a426c0f29/Documents/lutris/lutris/util/ubisoft/client.py", line 119, in _refresh_auth
    self._refresh_remember_me()
  File "/mnt/ac54dceb-73a5-4f94-b52c-cb7a426c0f29/Documents/lutris/lutris/util/ubisoft/client.py", line 145, in _refresh_remember_me
    self._handle_authorization_response(j)
  File "/mnt/ac54dceb-73a5-4f94-b52c-cb7a426c0f29/Documents/lutris/lutris/util/ubisoft/client.py", line 168, in _handle_authorization_response
    refresh_time = datetime.now() + (parse_date(j['expiration']) - parse_date(j['serverTime']))
```


### System Information

```shell
[System]
OS:              Ubuntu 21.10 Impish Indri
Arch:            x86_64
Kernel:          5.13.0-35-generic
Desktop:         Regolith:GNOME-Flashback:GNOME
Display Server:  x11

[CPU]
Vendor:          AuthenticAMD
Model:           AMD Ryzen 3 3200G with Radeon Vega Graphics
Physical cores:  4
Logical cores:   4

[Memory]
RAM:             5.7 GB
Swap:            3.5 GB

[Graphics]
Vendor:          AMD
OpenGL Renderer: AMD Radeon Vega 8 Graphics (raven, LLVM 13.0.0, DRM 3.41, 5.13.0-35-generic)
OpenGL Version:  4.6 (Compatibility Profile) Mesa 22.0.0 - kisak-mesa PPA
OpenGL Core:     4.6 (Core Profile) Mesa 22.0.0 - kisak-mesa PPA
OpenGL ES:       OpenGL ES 3.2 Mesa 22.0.0 - kisak-mesa PPA
Vulkan:          Supported
```


### Media (optional)

_No response_

### Checklist:

- [X] I'm not asking for support with a game or the wine runner.
- [X] I have followed the above mentioned guides and have all the graphics and wine dependencies installed.
- [X] I have checked for existing issues that describe my problem prior to opening this one.
- [X] I understand that improperly formatted bug reports may be closed without explanation.


Discussion:
variable j before key error

```python
(Pdb) pprint.pprint(j)
{'additionalDetails': {},
 'environment': 'prod',
 'errorCode': 3,
 'errorContext': 'Shipyard.Profiles.Services',
 'httpCode': 401,
 'message': 'Ticket is expired',
 'moreInfo': '',
 'transactionId': 'random_number',
 'transactionTime': '2022-03-11T11:25:28.5747367Z'}
```
Looks like this is an error from the Ubisoft service; it could report better that this is a "Ticket is expired" error.

But something is strange. There should be a message about "Unable to refresh authentication" in there.

But there's also no Lutris version number in your log output. What Lutris version are you running? Is there more to the log?
> But there's also no Lutris version number in your log output. What Lutris version are you running? Is there more to the log?

sorry i just noticed that i should not cut the log

i already replace the log with full one

it is run on f25dc6e5
Ah, there it is.

Did you get an error *outside* of the log output we see? If so, what did it say?
just box with `expiration` and ok button
That is a very strange box. Can we get a screenshot of that?

I don't have a Ubisoft account so I can't reproduce this myself.
![20220312_0802](https://user-images.githubusercontent.com/6340878/157993893-c49e0dbd-b299-4b0d-a920-09c26df044bf.jpg)

Expiration in single quotes. That is odd; I'll see what I can make of that.

Resolving patch:
diff --git a/lutris/util/ubisoft/client.py b/lutris/util/ubisoft/client.py
--- a/lutris/util/ubisoft/client.py
+++ b/lutris/util/ubisoft/client.py
@@ -4,6 +4,7 @@
 import json
 import time
 from datetime import datetime
+from gettext import gettext as _
 
 import requests
 
@@ -64,7 +65,10 @@ def _do_request(self, method, *args, **kwargs):
 
         response = self.request(method, *args, **kwargs)
         logger.info("Response status: %s", response)
-        return response.json()
+        result = response.json()
+        if 'errorCode' in result and 'message' in result:
+            raise RuntimeError(result['message'])
+        return result
 
     def _do_request_safe(self, method, *args, **kwargs):
         result = {}
@@ -95,7 +99,7 @@ def _do_request_safe(self, method, *args, **kwargs):
             logger.error("Unable to refresh authentication calling auth lost: %s", ex)
             if self._auth_lost_callback:
                 self._auth_lost_callback()
-            raise
+            raise RuntimeError(_("Ubisoft authentication has been lost: %s") % ex) from ex
         return result
 
     def _do_options_request(self):
@@ -165,9 +169,10 @@ def _refresh_ticket(self):
         self._handle_authorization_response(j)
 
     def _handle_authorization_response(self, j):
-        refresh_time = datetime.now() + (parse_date(j['expiration']) - parse_date(j['serverTime']))
-        j['refreshTime'] = round(refresh_time.timestamp())
-        self.restore_credentials(j)
+        if 'expiration' in j and 'serverTime' in j:
+            refresh_time = datetime.now() + (parse_date(j['expiration']) - parse_date(j['serverTime']))
+            j['refreshTime'] = round(refresh_time.timestamp())
+            self.restore_credentials(j)
 
     def restore_credentials(self, data):
         self.token = data['ticket']


Test patch: