def authenticate(self, address):
        '''
        Authenticate with the given address. This will prompt user for password
        unless valid credentials are already available. Client state will be
        updated if new tokens are generated.

        Returns an access token.
        '''
        def _cache_token(token_info, username=None):
            '''
            Helper to update state with new token info and optional username.
            Returns the latest access token.
            '''
            token_info['expires_at'] = time.time() + float(token_info['expires_in']) - 60.0
            del token_info['expires_in']
            auth['token_info'] = token_info
            if username is not None:
                auth['username'] = username
            self.save_state()
            return token_info['access_token']

        # Check the cache for a valid token
        from codalab.client.remote_bundle_client import RemoteBundleClient
        auth_info = self.state['auth'].get(address, {})
        if 'token_info' in auth_info:
            token_info = auth_info['token_info']
            expires_at = token_info.get('expires_at', 0.0)
            if expires_at > time.time():
                # Token is usable but check if it's nearing expiration
                if expires_at >= (time.time() + 900.0):
                    return token_info['access_token']
                # Try to refresh token
                remote_client = RemoteBundleClient(address, lambda command: None)
                token_info = remote_client.login('refresh_token',
                                                 token_info['refresh_token'],
                                                 auth_info['username'])
                if token_info is not None:
                    return _cache_token(token_info)

        # If we get here, a valid token is not already available.
        auth = self.state['auth'][address] = {}
        print 'Requesting access at %s' % address
        print 'Username: '******'credentials', username, password)
        if token_info is None:
            raise UsageError("Invalid username or password")
        return _cache_token(token_info, username)