@Override
    public Boolean authenticate(final Host bookmark, final LoginCallback callback, final CancelCallback cancel) throws BackgroundException {
        if(log.isDebugEnabled()) {
            log.debug(String.format("Login using challenge response authentication for %s", bookmark));
        }
        final AtomicBoolean canceled = new AtomicBoolean();
        final AtomicBoolean publickey = new AtomicBoolean();
        try {
            final Credentials credentials = bookmark.getCredentials();
            client.auth(credentials.getUsername(), new AuthKeyboardInteractive(new ChallengeResponseProvider() {
                private String name = StringUtils.EMPTY;
                private String instruction = StringUtils.EMPTY;

                /**
                 * Reply for default password prompt challenge sent
                 */
                private final AtomicBoolean flag = new AtomicBoolean(false);

                @Override
                public List<String> getSubmethods() {
                    return Collections.emptyList();
                }

                @Override
                public void init(final Resource resource, final String name, final String instruction) {
                    if(log.isDebugEnabled()) {
                        log.debug(String.format("Initialize with name '%s' and instruction '%s'", name, instruction));
                    }
                    if(StringUtils.isNotBlank(instruction)) {
                        this.instruction = instruction;
                    }
                    if(StringUtils.isNotBlank(name)) {
                        this.name = name;
                    }
                }

                @Override
                public char[] getResponse(final String prompt, final boolean echo) {
                    if(log.isDebugEnabled()) {
                        log.debug(String.format("Reply to challenge name '%s' with instruction '%s' and prompt '%s'",
                                name, instruction, prompt));
                    }
                    if(!flag.get() && DEFAULT_PROMPT_PATTERN.matcher(prompt).matches()) {
                        if(log.isDebugEnabled()) {
                            log.debug(String.format("Prompt '%s' matches %s", prompt, DEFAULT_PROMPT_PATTERN));
                        }
                        if(StringUtils.isNotBlank(credentials.getPassword())) {
                            flag.set(true);
                            return credentials.getPassword().toCharArray();
                        }
                        if(log.isDebugEnabled()) {
                            log.debug(String.format("Prompt for password input with %s", callback));
                        }
                        try {
                            final Credentials input = callback.prompt(bookmark, credentials.getUsername(),
                                    String.format("%s %s", LocaleFactory.localizedString("Login", "Login"), bookmark.getHostname()),
                                    MessageFormat.format(LocaleFactory.localizedString(
                                            "Login {0} with username and password", "Credentials"), BookmarkNameProvider.toString(bookmark)),
                                    // Change of username or service not allowed
                                    new LoginOptions(bookmark.getProtocol()).user(false));
                            if(input.isPublicKeyAuthentication()) {
                                credentials.setIdentity(input.getIdentity());
                                publickey.set(true);
                                // Return null to cancel if user wants to use public key auth
                                return StringUtils.EMPTY.toCharArray();
                            }
                            flag.set(true);
                            return credentials
                                    .withPassword(input.getPassword())
                                    .withSaved(input.isSaved()).getPassword().toCharArray();
                        }
                        catch(LoginCanceledException e) {
                            canceled.set(true);
                            // Return null if user cancels
                            return StringUtils.EMPTY.toCharArray();
                        }
                    }
                    else {
                        if(log.isDebugEnabled()) {
                            log.debug(String.format("Prompt for additional credentials with prompt %s", prompt));
                        }
                        final StringAppender message = new StringAppender().append(instruction).append(prompt);
                        // Properly handle an instruction field with embedded newlines.  They should also
                        // be able to display at least 30 characters for the name and prompts.
                        final Credentials additional;
                        try {
                            final StringAppender title = new StringAppender().append(
                                    LocaleFactory.localizedString("Provide additional login credentials", "Credentials")
                            ).append(name);
                            additional = callback.prompt(bookmark, title.toString(),
                                    message.toString(), new LoginOptions()
                                            .icon(bookmark.getProtocol().disk())
                                            .password(true)
                                            .user(false)
                                            .keychain(false)
                                            .anonymous(true)
                            );
                        }
                        catch(LoginCanceledException e) {
                            canceled.set(true);
                            // Return null if user cancels
                            return StringUtils.EMPTY.toCharArray();
                        }
                        // Responses are encoded in ISO-10646 UTF-8.
                        return additional.getPassword().toCharArray();
                    }
                }

                @Override
                public boolean shouldRetry() {
                    return false;
                }
            }));
        }
        catch(IOException e) {
            if(publickey.get()) {
                return new SFTPPublicKeyAuthentication(client).authenticate(bookmark, callback, cancel);
            }
            if(canceled.get()) {
                throw new LoginCanceledException();
            }
            throw new SFTPExceptionMappingService().map(e);
        }
        return client.isAuthenticated();
    }
@Test(expected = LoginFailureException.class)
    @Ignore
    public void testAuthenticate() throws Exception {
        assertFalse(new SFTPChallengeResponseAuthentication(session.getClient()).authenticate(session.getHost(), new DisabledLoginCallback(), new DisabledCancelCallback()));
    }