code: def finish_oauth(self, code):
          
          r = requests.post(self._login_uri("/oauth/token"), data={
                  "code": code,
                  "client_id": self.client_id,
                  "client_secret": self.client_secret
              })
  
          if r.status_code != 200:
              raise ApiError("OAuth token exchange failed", status=r.status_code, json=r.json())
  
          token = r.json()["access_token"]
          scopes = OAuthScopes.parse(r.json()["scopes"])
          expiry = datetime.now() + timedelta(seconds=r.json()['expires_in'])
          refresh_token = r.json()['refresh_token']
  
          return token, scopes, expiry, refresh_token
docs: Given an OAuth Exchange Code, completes the OAuth exchange with the
          authentication server.  This should be called once the user has already
          been directed to the login_uri, and has been sent back after successfully
          authenticating.  For example, in `Flask`_, this might be implemented as
          a route like this::
  
             @app.route("/oauth-redirect")
             def oauth_redirect():
                 exchange_code = request.args.get("code")
                 login_client = LinodeLoginClient(client_id, client_secret)
  
                 token, scopes = login_client.finish_oauth(exchange_code)
  
                 # store the user's OAuth token in their session for later use
                 # and mark that they are logged in.
  
                 return redirect("/")
  
          .. _Flask: http://flask.pocoo.org
  
          :param code: The OAuth Exchange Code returned from the authentication
                       server in the query string.
          :type code: str
  
          :returns: The new OAuth token, and a list of scopes the token has, when
                    the token expires, and a refresh token that can generate a new
                    valid token when this one is expired.
          :rtype: tuple(str, list)
  
          :raise ApiError: If the OAuth exchange fails.