def validate_token(refresh_url, exceptions=(), callback=None,
                   access_key='access_token', refresh_key='refresh_token'):
    ''' a decorator used to validate the access_token for oauth based
    data sources.
    This decorator should be used on every method in the data source that
    fetches data from the oauth controlled resource, and that relies on a
    valid access_token in order to operate properly.
    If the token is valid, the normal flow continues without any change.
    Otherwise, if any of `exceptions` tuple is raised, the normal flow
    will be preceded by the following steps:

    1. `refresh_url` will be called in order to refresh the token
    2. the newly refreshed token will be saved in the source
    3. the `callback` function will be called

    If the refresh fails for any reason, the user would have to re-grant
    permission for the application

    Parameters
    ----------
    refresh_url : str
        The URL to be called in order to refresh the access token.
    callback : str or callable
        A callback function to be called whenever the access_token is
        validated. The callback function would be called with the refreshed
        token as an argument.
        If the `callback` is not `callable`, but an `str` it will be called
        on `self` (i.e. call a method on your Data Source)
        Defaults to None
    exceptions : tuple
        A list of exceptions that should cause token revalidation
        Defaults to Exception, meaning that all errors will cause token
        refresh
    access_key : str
        The access token key as defined in the source and in the response from
        the refresh URL.
        Defaults to `access_token`
    refresh_key : str
        The refresh token key as defined in the source and in the request to
        the refresh URL.
        Defaults to `refresh_token`
    '''
    def _validate_token(f):
        def wrapper(*args):
            self = args[0]
            try:
                return f(*args)
            except exceptions:
                try:
                    self.log('Revalidating the access token...')
                    self.source[access_key] = None

                    # get a new token from refresh_url
                    token = self.source.get(refresh_key)
                    data = dict(self.options['refresh'],
                                **{refresh_key: token})
                    r = requests.post(refresh_url, data=data)
                    self.source[access_key] = r.json()[access_key]

                    # save the new token in the database
                    changes = {access_key: self.source[access_key]}
                    self.fire('source-change', changes)

                    # notify the callback that a new token was issued
                    if callback:
                        if callable(callback):
                            _callback = callback
                        else:
                            _callback = getattr(self, callback)
                        _callback(self.source.get(access_key))

                    return f(*args)
                except Exception, e:
                    self.log('Error: Access token can\'t be revalidated. '
                             'The user would have to re-authenticate',
                             traceback.format_exc())
                    # raise a non-retryable exception
                    raise PanoplyException(
                        'access token could not be refreshed ({})'
                        .format(str(e)), retryable=False)
        return wrapper
    return _validate_token