docs: def fetch_tokens(self, client_id=None, client_secret=None, code=None,
                       redirect_uri=None, **kwargs):
          """Exchange authorization code for token.
  
          Args:
              client_id (str): OAuth2 client ID. Defaults to ``None``.
              client_secret (str): OAuth2 client secret. Defaults to ``None``.
              code (str): Authorization code. Defaults to ``None``.
              redirect_uri (str): Redirect URI. Defaults to ``None``.
  
          Returns:
              dict: Response from token URL.
  
          """
          client_id = client_id or self.client_id
          client_secret = client_secret or self.client_secret
          redirect_uri = redirect_uri or self.redirect_uri
          data = {
             'grant_type': 'authorization_code',
             'client_id': client_id,
             'client_secret': client_secret,
             'code': code,
             'redirect_uri': redirect_uri
          }
          r = self._httpclient.request(
              method='POST',
              url=self.token_url,
              json=data,
              path='/api/oauth2/RequestToken',
              auth=None,
              **kwargs
          )
          if not r.ok:
              raise PanCloudError(
                  '%s %s: %s' % (r.status_code, r.reason, r.text)
              )
          try:
              r_json = r.json()
          except ValueError as e:
              raise PanCloudError("Invalid JSON: %s" % e)
          else:
              if r.json().get(
                  'error_description'
              ) or r.json().get(
                  'error'
              ):
                  raise PanCloudError(r.text)
              self.access_token = r_json.get('access_token')
              self.jwt_exp = self._decode_exp(self.access_token_)
              self.refresh_token = r_json.get('refresh_token')
              self.write_credentials()
              return r_json