prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Review: Connected App: Difficult to see how Token Cache get_expires_in could be any more wrong
  https://github.com/frappe/frappe/blob/86e512452d77f3e61405fd33ecd1bf881790ae18/frappe/integrations/doctype/token_cache/token_cache.py#L53
  
  PR to follow
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `frappe/integrations/doctype/token_cache/token_cache.py`
  Content:
  ```
  1 # -*- coding: utf-8 -*-
  2 # Copyright (c) 2019, Frappe Technologies and contributors
  3 # For license information, please see license.txt
  4 
  5 from __future__ import unicode_literals
  6 from datetime import datetime, timedelta
  7 
  8 import frappe
  9 from frappe import _
  10 from frappe.utils import cstr, cint
  11 from frappe.model.document import Document
  12 
  13 class TokenCache(Document):
  14 
  15 	def get_auth_header(self):
  16 		if self.access_token:
  17 			headers = {'Authorization': 'Bearer ' + self.get_password('access_token')}
  18 			return headers
  19 
  20 		raise frappe.exceptions.DoesNotExistError
  21 
  22 	def update_data(self, data):
  23 		"""
  24 		Store data returned by authorization flow.
  25 
  26 		Params:
  27 		data - Dict with access_token, refresh_token, expires_in and scope.
  28 		"""
  29 		token_type = cstr(data.get('token_type', '')).lower()
  30 		if token_type not in ['bearer', 'mac']:
  31 			frappe.throw(_('Received an invalid token type.'))
  32 		# 'Bearer' or 'MAC'
  33 		token_type = token_type.title() if token_type == 'bearer' else token_type.upper()
  34 
  35 		self.token_type = token_type
  36 		self.access_token = cstr(data.get('access_token', ''))
  37 		self.refresh_token = cstr(data.get('refresh_token', ''))
  38 		self.expires_in = cint(data.get('expires_in', 0))
  39 
  40 		new_scopes = data.get('scope')
  41 		if new_scopes:
  42 			if isinstance(new_scopes, str):
  43 				new_scopes = new_scopes.split(' ')
  44 			if isinstance(new_scopes, list):
  45 				self.scopes = None
  46 				for scope in new_scopes:
  47 					self.append('scopes', {'scope': scope})
  48 
  49 		self.state = None
  50 		self.save(ignore_permissions=True)
  51 		frappe.db.commit()
  52 		return self
  53 
  54 	def get_expires_in(self):
  55 		expiry_time = frappe.utils.get_datetime(self.modified) + timedelta(self.expires_in)
  56 		return (datetime.now() - expiry_time).total_seconds()
  57 
  58 	def is_expired(self):
  59 		return self.get_expires_in() < 0
  60 
  61 	def get_json(self):
  62 		return {
  63 			'access_token': self.get_password('access_token', ''),
  64 			'refresh_token': self.get_password('refresh_token', ''),
  65 			'expires_in': self.get_expires_in(),
  66 			'token_type': self.token_type
  67 		}
  68
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/frappe/integrations/doctype/token_cache/token_cache.py b/frappe/integrations/doctype/token_cache/token_cache.py
  --- a/frappe/integrations/doctype/token_cache/token_cache.py
  +++ b/frappe/integrations/doctype/token_cache/token_cache.py
  @@ -3,7 +3,7 @@
   # For license information, please see license.txt
   
   from __future__ import unicode_literals
  -from datetime import datetime, timedelta
  +from datetime import timedelta
   
   import frappe
   from frappe import _
  @@ -52,8 +52,8 @@
   		return self
   
   	def get_expires_in(self):
  -		expiry_time = frappe.utils.get_datetime(self.modified) + timedelta(self.expires_in)
  -		return (datetime.now() - expiry_time).total_seconds()
  +		expiry_time = frappe.utils.get_datetime(self.modified) + timedelta(seconds=self.expires_in)
  +		return (expiry_time - frappe.utils.now_datetime()).total_seconds()
   
   	def is_expired(self):
   		return self.get_expires_in() < 0
  
verification_info: {"golden_diff": "diff --git a/frappe/integrations/doctype/token_cache/token_cache.py b/frappe/integrations/doctype/token_cache/token_cache.py\n--- a/frappe/integrations/doctype/token_cache/token_cache.py\n+++ b/frappe/integrations/doctype/token_cache/token_cache.py\n@@ -3,7 +3,7 @@\n # For license information, please see license.txt\n \n from __future__ import unicode_literals\n-from datetime import datetime, timedelta\n+from datetime import timedelta\n \n import frappe\n from frappe import _\n@@ -52,8 +52,8 @@\n \t\treturn self\n \n \tdef get_expires_in(self):\n-\t\texpiry_time = frappe.utils.get_datetime(self.modified) + timedelta(self.expires_in)\n-\t\treturn (datetime.now() - expiry_time).total_seconds()\n+\t\texpiry_time = frappe.utils.get_datetime(self.modified) + timedelta(seconds=self.expires_in)\n+\t\treturn (expiry_time - frappe.utils.now_datetime()).total_seconds()\n \n \tdef is_expired(self):\n \t\treturn self.get_expires_in() < 0\n", "issue": "Review: Connected App: Difficult to see how Token Cache get_expires_in could be any more wrong\nhttps://github.com/frappe/frappe/blob/86e512452d77f3e61405fd33ecd1bf881790ae18/frappe/integrations/doctype/token_cache/token_cache.py#L53\r\n\r\nPR to follow\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright (c) 2019, Frappe Technologies and contributors\n# For license information, please see license.txt\n\nfrom __future__ import unicode_literals\nfrom datetime import datetime, timedelta\n\nimport frappe\nfrom frappe import _\nfrom frappe.utils import cstr, cint\nfrom frappe.model.document import Document\n\nclass TokenCache(Document):\n\n\tdef get_auth_header(self):\n\t\tif self.access_token:\n\t\t\theaders = {'Authorization': 'Bearer ' + self.get_password('access_token')}\n\t\t\treturn headers\n\n\t\traise frappe.exceptions.DoesNotExistError\n\n\tdef update_data(self, data):\n\t\t\"\"\"\n\t\tStore data returned by authorization flow.\n\n\t\tParams:\n\t\tdata - Dict with access_token, refresh_token, expires_in and scope.\n\t\t\"\"\"\n\t\ttoken_type = cstr(data.get('token_type', '')).lower()\n\t\tif token_type not in ['bearer', 'mac']:\n\t\t\tfrappe.throw(_('Received an invalid token type.'))\n\t\t# 'Bearer' or 'MAC'\n\t\ttoken_type = token_type.title() if token_type == 'bearer' else token_type.upper()\n\n\t\tself.token_type = token_type\n\t\tself.access_token = cstr(data.get('access_token', ''))\n\t\tself.refresh_token = cstr(data.get('refresh_token', ''))\n\t\tself.expires_in = cint(data.get('expires_in', 0))\n\n\t\tnew_scopes = data.get('scope')\n\t\tif new_scopes:\n\t\t\tif isinstance(new_scopes, str):\n\t\t\t\tnew_scopes = new_scopes.split(' ')\n\t\t\tif isinstance(new_scopes, list):\n\t\t\t\tself.scopes = None\n\t\t\t\tfor scope in new_scopes:\n\t\t\t\t\tself.append('scopes', {'scope': scope})\n\n\t\tself.state = None\n\t\tself.save(ignore_permissions=True)\n\t\tfrappe.db.commit()\n\t\treturn self\n\n\tdef get_expires_in(self):\n\t\texpiry_time = frappe.utils.get_datetime(self.modified) + timedelta(self.expires_in)\n\t\treturn (datetime.now() - expiry_time).total_seconds()\n\n\tdef is_expired(self):\n\t\treturn self.get_expires_in() < 0\n\n\tdef get_json(self):\n\t\treturn {\n\t\t\t'access_token': self.get_password('access_token', ''),\n\t\t\t'refresh_token': self.get_password('refresh_token', ''),\n\t\t\t'expires_in': self.get_expires_in(),\n\t\t\t'token_type': self.token_type\n\t\t}\n", "path": "frappe/integrations/doctype/token_cache/token_cache.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright (c) 2019, Frappe Technologies and contributors\n# For license information, please see license.txt\n\nfrom __future__ import unicode_literals\nfrom datetime import timedelta\n\nimport frappe\nfrom frappe import _\nfrom frappe.utils import cstr, cint\nfrom frappe.model.document import Document\n\nclass TokenCache(Document):\n\n\tdef get_auth_header(self):\n\t\tif self.access_token:\n\t\t\theaders = {'Authorization': 'Bearer ' + self.get_password('access_token')}\n\t\t\treturn headers\n\n\t\traise frappe.exceptions.DoesNotExistError\n\n\tdef update_data(self, data):\n\t\t\"\"\"\n\t\tStore data returned by authorization flow.\n\n\t\tParams:\n\t\tdata - Dict with access_token, refresh_token, expires_in and scope.\n\t\t\"\"\"\n\t\ttoken_type = cstr(data.get('token_type', '')).lower()\n\t\tif token_type not in ['bearer', 'mac']:\n\t\t\tfrappe.throw(_('Received an invalid token type.'))\n\t\t# 'Bearer' or 'MAC'\n\t\ttoken_type = token_type.title() if token_type == 'bearer' else token_type.upper()\n\n\t\tself.token_type = token_type\n\t\tself.access_token = cstr(data.get('access_token', ''))\n\t\tself.refresh_token = cstr(data.get('refresh_token', ''))\n\t\tself.expires_in = cint(data.get('expires_in', 0))\n\n\t\tnew_scopes = data.get('scope')\n\t\tif new_scopes:\n\t\t\tif isinstance(new_scopes, str):\n\t\t\t\tnew_scopes = new_scopes.split(' ')\n\t\t\tif isinstance(new_scopes, list):\n\t\t\t\tself.scopes = None\n\t\t\t\tfor scope in new_scopes:\n\t\t\t\t\tself.append('scopes', {'scope': scope})\n\n\t\tself.state = None\n\t\tself.save(ignore_permissions=True)\n\t\tfrappe.db.commit()\n\t\treturn self\n\n\tdef get_expires_in(self):\n\t\texpiry_time = frappe.utils.get_datetime(self.modified) + timedelta(seconds=self.expires_in)\n\t\treturn (expiry_time - frappe.utils.now_datetime()).total_seconds()\n\n\tdef is_expired(self):\n\t\treturn self.get_expires_in() < 0\n\n\tdef get_json(self):\n\t\treturn {\n\t\t\t'access_token': self.get_password('access_token', ''),\n\t\t\t'refresh_token': self.get_password('refresh_token', ''),\n\t\t\t'expires_in': self.get_expires_in(),\n\t\t\t'token_type': self.token_type\n\t\t}\n", "path": "frappe/integrations/doctype/token_cache/token_cache.py"}]}