fix: fix expiry for `to_json()` (#589)

* This patch for </issues/501> includes the following fixes:

- The access token is always set to `None`, so the fix involves using (the access) `token` from the saved JSON credentials file.
- For refresh needs, `expiry` also needs to be saved via `to_json()`.
    - DUMP: As `expiry` is a `datetime.datetime` object, serialize to `datetime.isoformat()` in the same [`oauth2client` format](https://github.com/googleapis/oauth2client/blob/master/oauth2client/client.py#L55) for consistency.
    - LOAD: Add code to restore `expiry` back to `datetime.datetime` object when imported.
    - LOAD: If `expiry` was unsaved, automatically set it as expired so refresh takes place.
- Minor `scopes` updates
    - DUMP: Add property for `scopes` so `to_json()` can grab it
    - LOAD: `scopes` may be saved as a string instead of a JSON array (Python list), so ensure it is Sequence[str] when imported.
diff --git a/google/oauth2/credentials.py b/google/oauth2/credentials.py
index 6e58f63..36b8f0c 100644
--- a/google/oauth2/credentials.py
+++ b/google/oauth2/credentials.py
@@ -31,6 +31,7 @@
 .. _rfc6749 section 4.1: https://tools.ietf.org/html/rfc6749#section-4.1
 """
 
+from datetime import datetime
 import io
 import json
 
@@ -66,6 +67,7 @@
         client_secret=None,
         scopes=None,
         quota_project_id=None,
+        expiry=None,
     ):
         """
         Args:
@@ -95,6 +97,7 @@
         """
         super(Credentials, self).__init__()
         self.token = token
+        self.expiry = expiry
         self._refresh_token = refresh_token
         self._id_token = id_token
         self._scopes = scopes
@@ -129,6 +132,11 @@
         return self._refresh_token
 
     @property
+    def scopes(self):
+        """Optional[str]: The OAuth 2.0 permission scopes."""
+        return self._scopes
+
+    @property
     def token_uri(self):
         """Optional[str]: The OAuth 2.0 authorization server's token endpoint
         URI."""
@@ -241,16 +249,30 @@
                 "fields {}.".format(", ".join(missing))
             )
 
+        # access token expiry (datetime obj); auto-expire if not saved
+        expiry = info.get("expiry")
+        if expiry:
+            expiry = datetime.strptime(
+                expiry.rstrip("Z").split(".")[0], "%Y-%m-%dT%H:%M:%S"
+            )
+        else:
+            expiry = _helpers.utcnow() - _helpers.CLOCK_SKEW
+
+        # process scopes, which needs to be a seq
+        if scopes is None and "scopes" in info:
+            scopes = info.get("scopes")
+            if isinstance(scopes, str):
+                scopes = scopes.split(" ")
+
         return cls(
-            None,  # No access token, must be refreshed.
-            refresh_token=info["refresh_token"],
-            token_uri=_GOOGLE_OAUTH2_TOKEN_ENDPOINT,
+            token=info.get("token"),
+            refresh_token=info.get("refresh_token"),
+            token_uri=_GOOGLE_OAUTH2_TOKEN_ENDPOINT,  # always overrides
             scopes=scopes,
-            client_id=info["client_id"],
-            client_secret=info["client_secret"],
-            quota_project_id=info.get(
-                "quota_project_id"
-            ),  # quota project may not exist
+            client_id=info.get("client_id"),
+            client_secret=info.get("client_secret"),
+            quota_project_id=info.get("quota_project_id"),  # may not exist
+            expiry=expiry,
         )
 
     @classmethod
@@ -294,8 +316,10 @@
             "client_secret": self.client_secret,
             "scopes": self.scopes,
         }
+        if self.expiry:  # flatten expiry timestamp
+            prep["expiry"] = self.expiry.isoformat() + "Z"
 
-        # Remove empty entries
+        # Remove empty entries (those which are None)
         prep = {k: v for k, v in prep.items() if v is not None}
 
         # Remove entries that explicitely need to be removed
@@ -316,7 +340,6 @@
             specified, the current active account will be used.
         quota_project_id (Optional[str]): The project ID used for quota
             and billing.
-
     """
 
     def __init__(self, account=None, quota_project_id=None):