From ba42487a096046af735166656ab2bcd732d83ffc Mon Sep 17 00:00:00 2001 From: David Kocher Date: Sun, 2 Jul 2023 16:59:23 +0200 Subject: [PATCH 1/6] Only set credentials provider for password authentication. --- .../ch/cyberduck/core/dav/DAVSession.java | 76 ++++++++++--------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java index 9f24b265edf..a3227034658 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java @@ -123,44 +123,46 @@ protected void logout() throws BackgroundException { @Override public void login(final Proxy proxy, final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { - final CredentialsProvider provider = new BasicCredentialsProvider(); - provider.setCredentials( - new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.NTLM), - new NTCredentials(host.getCredentials().getUsername(), host.getCredentials().getPassword(), - preferences.getProperty("webdav.ntlm.workstation"), preferences.getProperty("webdav.ntlm.domain")) - ); - provider.setCredentials( - new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.SPNEGO), - new NTCredentials(host.getCredentials().getUsername(), host.getCredentials().getPassword(), - preferences.getProperty("webdav.ntlm.workstation"), preferences.getProperty("webdav.ntlm.domain")) - ); - provider.setCredentials( - new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.BASIC), - new UsernamePasswordCredentials(host.getCredentials().getUsername(), host.getCredentials().getPassword())); - provider.setCredentials( - new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.DIGEST), - new UsernamePasswordCredentials(host.getCredentials().getUsername(), host.getCredentials().getPassword())); - provider.setCredentials( - new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.KERBEROS), - new UsernamePasswordCredentials(host.getCredentials().getUsername(), host.getCredentials().getPassword())); - client.setCredentials(provider); - if(preferences.getBoolean("webdav.basic.preemptive")) { - switch(proxy.getType()) { - case DIRECT: - case SOCKS: - // Enable preemptive authentication. See HttpState#setAuthenticationPreemptive - client.enablePreemptiveAuthentication(host.getHostname(), - host.getPort(), - host.getPort(), - Charset.forName(preferences.getProperty("http.credentials.charset")) - ); - break; - default: - client.disablePreemptiveAuthentication(); + if(host.getProtocol().isPasswordConfigurable()) { + final CredentialsProvider provider = new BasicCredentialsProvider(); + provider.setCredentials( + new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.NTLM), + new NTCredentials(host.getCredentials().getUsername(), host.getCredentials().getPassword(), + preferences.getProperty("webdav.ntlm.workstation"), preferences.getProperty("webdav.ntlm.domain")) + ); + provider.setCredentials( + new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.SPNEGO), + new NTCredentials(host.getCredentials().getUsername(), host.getCredentials().getPassword(), + preferences.getProperty("webdav.ntlm.workstation"), preferences.getProperty("webdav.ntlm.domain")) + ); + provider.setCredentials( + new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.BASIC), + new UsernamePasswordCredentials(host.getCredentials().getUsername(), host.getCredentials().getPassword())); + provider.setCredentials( + new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.DIGEST), + new UsernamePasswordCredentials(host.getCredentials().getUsername(), host.getCredentials().getPassword())); + provider.setCredentials( + new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthSchemes.KERBEROS), + new UsernamePasswordCredentials(host.getCredentials().getUsername(), host.getCredentials().getPassword())); + client.setCredentials(provider); + if(preferences.getBoolean("webdav.basic.preemptive")) { + switch(proxy.getType()) { + case DIRECT: + case SOCKS: + // Enable preemptive authentication. See HttpState#setAuthenticationPreemptive + client.enablePreemptiveAuthentication(host.getHostname(), + host.getPort(), + host.getPort(), + Charset.forName(preferences.getProperty("http.credentials.charset")) + ); + break; + default: + client.disablePreemptiveAuthentication(); + } + } + else { + client.disablePreemptiveAuthentication(); } - } - else { - client.disablePreemptiveAuthentication(); } if(host.getCredentials().isPassed()) { if(log.isWarnEnabled()) { From 2025403cfda6ce9e98297645d891970ac08ad33b Mon Sep 17 00:00:00 2001 From: David Kocher Date: Sun, 2 Jul 2023 17:05:26 +0200 Subject: [PATCH 2/6] Fix #14876. --- owncloud/pom.xml | 5 +++ .../core/owncloud/OwncloudSession.java | 34 +++++++++++++++++++ .../ch/cyberduck/core/dav/DAVSession.java | 12 ++++--- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/owncloud/pom.xml b/owncloud/pom.xml index 063e30adf69..fb8d499867a 100644 --- a/owncloud/pom.xml +++ b/owncloud/pom.xml @@ -39,6 +39,11 @@ webdav ${project.version} + + ch.cyberduck + oauth + ${project.version} + com.fasterxml.jackson.dataformat jackson-dataformat-xml diff --git a/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java b/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java index 7f4a960669a..9846a4a43a3 100644 --- a/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java +++ b/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java @@ -17,8 +17,10 @@ import ch.cyberduck.core.Host; import ch.cyberduck.core.ListService; +import ch.cyberduck.core.LoginCallback; import ch.cyberduck.core.UrlProvider; import ch.cyberduck.core.dav.DAVSession; +import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Home; @@ -36,18 +38,50 @@ import ch.cyberduck.core.nextcloud.NextcloudShareProvider; import ch.cyberduck.core.nextcloud.NextcloudUrlProvider; import ch.cyberduck.core.nextcloud.NextcloudWriteFeature; +import ch.cyberduck.core.oauth.OAuth2AuthorizationService; +import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor; +import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; +import ch.cyberduck.core.proxy.Proxy; import ch.cyberduck.core.shared.DefaultPathHomeFeature; import ch.cyberduck.core.shared.DelegatingHomeFeature; import ch.cyberduck.core.shared.WorkdirHomeFeature; import ch.cyberduck.core.ssl.X509KeyManager; import ch.cyberduck.core.ssl.X509TrustManager; +import ch.cyberduck.core.threading.CancelCallback; + +import org.apache.http.impl.client.HttpClientBuilder; + +import static ch.cyberduck.core.oauth.OAuth2AuthorizationService.CYBERDUCK_REDIRECT_URI; public class OwncloudSession extends DAVSession { + private OAuth2RequestInterceptor authorizationService; + public OwncloudSession(final Host host, final X509TrustManager trust, final X509KeyManager key) { super(host, trust, key); } + @Override + protected HttpClientBuilder getConfiguration(final Proxy proxy, final LoginCallback prompt) { + final HttpClientBuilder configuration = super.getConfiguration(proxy, prompt); + if(host.getProtocol().isOAuthConfigurable()) { + authorizationService = new OAuth2RequestInterceptor(configuration.build(), host) + .withFlowType(OAuth2AuthorizationService.FlowType.valueOf(host.getProtocol().getAuthorization())) + .withRedirectUri(CYBERDUCK_REDIRECT_URI); + configuration.addInterceptorLast(authorizationService); + configuration.setServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService, prompt)); + } + return configuration; + } + + @Override + public void login(final Proxy proxy, final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { + if(host.getProtocol().isOAuthConfigurable()) { + authorizationService.authorize(host, prompt, cancel); + } + super.login(proxy, prompt, cancel); + } + @Override @SuppressWarnings("unchecked") public T _getFeature(final Class type) { diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java index a3227034658..ace5c3a9d28 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java @@ -105,10 +105,14 @@ public DAVSession(final Host host, final X509TrustManager trust, final X509KeyMa @Override protected DAVClient connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { - // Always inject new pool to builder on connect because the pool is shutdown on disconnect - final HttpClientBuilder pool = builder.build(proxy, this, prompt); - pool.setRedirectStrategy(new DAVRedirectStrategy(redirect)); - return new DAVClient(new HostUrlProvider().withUsername(false).get(host), pool); + final HttpClientBuilder configuration = this.getConfiguration(proxy, prompt); + return new DAVClient(new HostUrlProvider().withUsername(false).get(host), configuration); + } + + protected HttpClientBuilder getConfiguration(final Proxy proxy, final LoginCallback prompt) { + final HttpClientBuilder configuration = builder.build(proxy, this, prompt); + configuration.setRedirectStrategy(new DAVRedirectStrategy(redirect)); + return configuration; } @Override From b83b9c479a378dfb9875f711d1f3c6d35988ccd9 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Mon, 3 Jul 2023 10:24:07 +0200 Subject: [PATCH 3/6] Prompt for required client id parameter when missing in profile. May omit the client secret parameter if an empty string. --- .../oauth/OAuth2AuthorizationService.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationService.java b/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationService.java index c43e0fe7bcd..25eae92e0be 100644 --- a/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationService.java +++ b/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationService.java @@ -31,6 +31,7 @@ import ch.cyberduck.core.exception.LoginFailureException; import ch.cyberduck.core.http.DefaultHttpResponseExceptionMappingService; import ch.cyberduck.core.http.UserAgentHttpRequestInitializer; +import ch.cyberduck.core.preferences.HostPreferences; import ch.cyberduck.core.preferences.PreferencesFactory; import ch.cyberduck.core.threading.CancelCallback; @@ -165,11 +166,32 @@ private TokenResponse authorizeWithCode(final Host bookmark, final LoginCallback ); } // Start OAuth2 flow within browser + final ClientParametersAuthentication clientParameters; + if(StringUtils.isBlank(clientid)) { + if(null != new HostPreferences(bookmark).getProperty("oauth.clientid")) { + clientParameters = new ClientParametersAuthentication(new HostPreferences(bookmark).getProperty("oauth.clientid"), StringUtils.EMPTY); + } + else { + final Credentials clientid = prompt.prompt(bookmark, + LocaleFactory.localizedString("OAuth Client ID", "Credentials"), + LocaleFactory.localizedString("Provide additional login credentials", "Credentials"), + new LoginOptions(bookmark.getProtocol()) + .user(true).password(true) + .passwordPlaceholder("OAuth Client ID")); + if(clientid.isSaved()) { + bookmark.setProperty("oauth.clientid", clientid.getPassword()); + } + clientParameters = new ClientParametersAuthentication(clientid.getUsername(), StringUtils.EMPTY); + } + } + else { + clientParameters = new ClientParametersAuthentication(clientid, clientsecret); + } final AuthorizationCodeFlow.Builder flowBuilder = new AuthorizationCodeFlow.Builder( method, transport, json, new GenericUrl(tokenServerUrl), - new ClientParametersAuthentication(clientid, clientsecret), + clientParameters, clientid, authorizationServerUrl) .setScopes(scopes) From b434b371f2d4a442b5e44231f10fff521a2e569a Mon Sep 17 00:00:00 2001 From: David Kocher Date: Mon, 7 Aug 2023 13:24:18 +0200 Subject: [PATCH 4/6] Display failure when OAuth flow does not complete. --- .../java/ch/cyberduck/cli/TerminalLoginService.java | 3 ++- .../ch/cyberduck/core/KeychainLoginService.java | 13 +++++++------ .../main/java/ch/cyberduck/core/LoginService.java | 3 ++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/cli/src/main/java/ch/cyberduck/cli/TerminalLoginService.java b/cli/src/main/java/ch/cyberduck/cli/TerminalLoginService.java index 5ac0c642e40..1b1e0a4f0a4 100644 --- a/cli/src/main/java/ch/cyberduck/cli/TerminalLoginService.java +++ b/cli/src/main/java/ch/cyberduck/cli/TerminalLoginService.java @@ -26,6 +26,7 @@ import ch.cyberduck.core.LoginCallback; import ch.cyberduck.core.LoginOptions; import ch.cyberduck.core.PasswordStoreFactory; +import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.exception.LoginFailureException; import ch.cyberduck.core.preferences.PreferencesFactory; @@ -43,7 +44,7 @@ public TerminalLoginService(final CommandLine input) { } @Override - public void validate(final Host bookmark, final LoginCallback prompt, final LoginOptions options) throws LoginCanceledException, LoginFailureException { + public void validate(final Host bookmark, final LoginCallback prompt, final LoginOptions options) throws ConnectionCanceledException, LoginFailureException { final Credentials credentials = bookmark.getCredentials(); if(input.hasOption(TerminalOptionsBuilder.Params.anonymous.name())) { credentials.setUsername(PreferencesFactory.get().getProperty("connection.login.anon.name")); diff --git a/core/src/main/java/ch/cyberduck/core/KeychainLoginService.java b/core/src/main/java/ch/cyberduck/core/KeychainLoginService.java index 1d49d41c201..04e0ef27df3 100644 --- a/core/src/main/java/ch/cyberduck/core/KeychainLoginService.java +++ b/core/src/main/java/ch/cyberduck/core/KeychainLoginService.java @@ -18,6 +18,7 @@ */ import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.exception.LocalAccessDeniedException; import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.exception.LoginFailureException; @@ -44,7 +45,7 @@ public KeychainLoginService(final HostPasswordStore keychain) { } @Override - public void validate(final Host bookmark, final LoginCallback prompt, final LoginOptions options) throws LoginCanceledException, LoginFailureException { + public void validate(final Host bookmark, final LoginCallback prompt, final LoginOptions options) throws ConnectionCanceledException, LoginFailureException { if(log.isDebugEnabled()) { log.debug(String.format("Validate login credentials for %s", bookmark)); } @@ -137,7 +138,7 @@ public void validate(final Host bookmark, final LoginCallback prompt, final Logi * @return True if credentials have been updated * @throws LoginCanceledException Prompt canceled by user */ - public boolean prompt(final Host bookmark, final String message, final LoginCallback prompt, final LoginOptions options) throws LoginCanceledException { + public boolean prompt(final Host bookmark, final String message, final LoginCallback prompt, final LoginOptions options) throws ConnectionCanceledException { final Credentials credentials = bookmark.getCredentials(); if(options.password) { final Credentials input = prompt.prompt(bookmark, credentials.getUsername(), @@ -158,6 +159,9 @@ public boolean prompt(final Host bookmark, final String message, final LoginCall credentials.setToken(input.getPassword()); } if(options.oauth) { + prompt.warn(bookmark, LocaleFactory.localizedString("Login failed", "Credentials"), message, + LocaleFactory.localizedString("Continue", "Credentials"), + LocaleFactory.localizedString("Cancel", "Localizable"), null); log.warn(String.format("Reset OAuth tokens for %s", bookmark)); credentials.setOauth(OAuthTokens.EMPTY); } @@ -204,10 +208,7 @@ else if(credentials.isCertificateAuthentication()) { listener.message(LocaleFactory.localizedString("Login failed", "Credentials")); credentials.setPassed(false); final LoginOptions options = new LoginOptions(bookmark.getProtocol()); - final StringAppender details = new StringAppender(); - details.append(LocaleFactory.localizedString("Login failed", "Credentials")); - details.append(e.getDetail()); - if(this.prompt(bookmark, details.toString(), prompt, options)) { + if(this.prompt(bookmark, e.getDetail(), prompt, options)) { // Retry return false; } diff --git a/core/src/main/java/ch/cyberduck/core/LoginService.java b/core/src/main/java/ch/cyberduck/core/LoginService.java index 1b4c69bb41c..33cde39e291 100644 --- a/core/src/main/java/ch/cyberduck/core/LoginService.java +++ b/core/src/main/java/ch/cyberduck/core/LoginService.java @@ -18,6 +18,7 @@ */ import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.exception.LoginFailureException; import ch.cyberduck.core.proxy.Proxy; @@ -31,7 +32,7 @@ public interface LoginService { * @param pompt Login prompt * @param options Login mechanism features */ - void validate(Host bookmark, LoginCallback pompt, LoginOptions options) throws LoginCanceledException, LoginFailureException; + void validate(Host bookmark, LoginCallback pompt, LoginOptions options) throws ConnectionCanceledException, LoginFailureException; /** * Login and prompt on failure From 6a6d70a888c388ca774ffd2db3b09786efd6b5d9 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Tue, 8 Aug 2023 17:09:47 +0200 Subject: [PATCH 5/6] Allow override of redirect URI in profile. --- .../main/java/ch/cyberduck/core/owncloud/OwncloudSession.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java b/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java index 9846a4a43a3..ba976addb08 100644 --- a/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java +++ b/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java @@ -51,8 +51,6 @@ import org.apache.http.impl.client.HttpClientBuilder; -import static ch.cyberduck.core.oauth.OAuth2AuthorizationService.CYBERDUCK_REDIRECT_URI; - public class OwncloudSession extends DAVSession { private OAuth2RequestInterceptor authorizationService; @@ -67,7 +65,7 @@ protected HttpClientBuilder getConfiguration(final Proxy proxy, final LoginCallb if(host.getProtocol().isOAuthConfigurable()) { authorizationService = new OAuth2RequestInterceptor(configuration.build(), host) .withFlowType(OAuth2AuthorizationService.FlowType.valueOf(host.getProtocol().getAuthorization())) - .withRedirectUri(CYBERDUCK_REDIRECT_URI); + .withRedirectUri(host.getProtocol().getOAuthRedirectUrl()); configuration.addInterceptorLast(authorizationService); configuration.setServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService, prompt)); } From cbb5a2251e25f9c1a9ecfdc092d8e18e2a3ddabf Mon Sep 17 00:00:00 2001 From: David Kocher Date: Fri, 11 Aug 2023 11:50:52 +0200 Subject: [PATCH 6/6] Prompt for OAuth Client ID and OAuth Client Secret if missing in connection profile. --- .../ch/cyberduck/core/box/BoxSession.java | 6 ++- .../main/java/ch/cyberduck/core/Profile.java | 7 +++- .../ch/cyberduck/core/sds/SDSSession.java | 2 +- .../core/dropbox/DropboxSession.java | 6 ++- .../ch/cyberduck/core/eue/EueSession.java | 2 +- .../core/googledrive/DriveSession.java | 6 ++- .../googlestorage/GoogleStorageSession.java | 6 ++- .../ch/cyberduck/core/hubic/HubicSession.java | 6 ++- .../oauth/OAuth2AuthorizationService.java | 39 ++++++++++++++----- .../core/oauth/OAuth2RequestInterceptor.java | 9 +++-- .../cyberduck/core/onedrive/GraphSession.java | 6 ++- .../core/openstack/SwiftSession.java | 4 +- .../core/owncloud/OwncloudSession.java | 6 ++- .../core/storegate/StoregateSession.java | 6 ++- .../ch/cyberduck/core/dav/DAVSession.java | 2 +- 15 files changed, 77 insertions(+), 36 deletions(-) diff --git a/box/src/main/java/ch/cyberduck/core/box/BoxSession.java b/box/src/main/java/ch/cyberduck/core/box/BoxSession.java index 3081c282bd2..13b5c8ef6cc 100644 --- a/box/src/main/java/ch/cyberduck/core/box/BoxSession.java +++ b/box/src/main/java/ch/cyberduck/core/box/BoxSession.java @@ -24,6 +24,8 @@ import ch.cyberduck.core.box.io.swagger.client.ApiException; import ch.cyberduck.core.box.io.swagger.client.api.UsersApi; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.ConnectionCanceledException; +import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Copy; import ch.cyberduck.core.features.Delete; @@ -64,9 +66,9 @@ public BoxSession(final Host host, final X509TrustManager trust, final X509KeyMa } @Override - public CloseableHttpClient connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) { + public CloseableHttpClient connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws ConnectionCanceledException { final HttpClientBuilder configuration = builder.build(proxy, this, prompt); - authorizationService = new OAuth2RequestInterceptor(configuration.build(), host) + authorizationService = new OAuth2RequestInterceptor(configuration.build(), host, prompt) .withRedirectUri(host.getProtocol().getOAuthRedirectUrl()); configuration.addInterceptorLast(authorizationService); configuration.setServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService, prompt)); diff --git a/core/src/main/java/ch/cyberduck/core/Profile.java b/core/src/main/java/ch/cyberduck/core/Profile.java index 5056d6b7785..dd1539f61f4 100644 --- a/core/src/main/java/ch/cyberduck/core/Profile.java +++ b/core/src/main/java/ch/cyberduck/core/Profile.java @@ -50,6 +50,9 @@ public class Profile implements Protocol { */ private final Protocol parent; + public static final String OAUTH_CLIENT_ID_KEY = "OAuth Client ID"; + public static final String OAUTH_CLIENT_SECRET_KEY = "OAuth Client Secret"; + private Local disk; private Local icon; @@ -512,7 +515,7 @@ public String getOAuthRedirectUrl() { @Override public String getOAuthClientId() { - final String v = this.value("OAuth Client ID"); + final String v = this.value(OAUTH_CLIENT_ID_KEY); if(StringUtils.isBlank(v)) { return parent.getOAuthClientId(); } @@ -521,7 +524,7 @@ public String getOAuthClientId() { @Override public String getOAuthClientSecret() { - final String v = this.value("OAuth Client Secret"); + final String v = this.value(OAUTH_CLIENT_SECRET_KEY); if(StringUtils.isBlank(v)) { return parent.getOAuthClientSecret(); } diff --git a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSSession.java b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSSession.java index 44ef137e4cb..48995439a23 100644 --- a/dracoon/src/main/java/ch/cyberduck/core/sds/SDSSession.java +++ b/dracoon/src/main/java/ch/cyberduck/core/sds/SDSSession.java @@ -156,7 +156,7 @@ public void process(final HttpRequest request, final HttpContext context) { } } } - }).build(), host) { + }).build(), host, prompt) { @Override public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { if(request instanceof HttpRequestWrapper) { diff --git a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSession.java b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSession.java index 5f658db3e2c..78283cb4c52 100644 --- a/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSession.java +++ b/dropbox/src/main/java/ch/cyberduck/core/dropbox/DropboxSession.java @@ -27,6 +27,8 @@ import ch.cyberduck.core.UrlProvider; import ch.cyberduck.core.UseragentProvider; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.ConnectionCanceledException; +import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.features.*; import ch.cyberduck.core.http.DefaultHttpRateLimiter; import ch.cyberduck.core.http.HttpSession; @@ -72,9 +74,9 @@ public DropboxSession(final Host host, final X509TrustManager trust, final X509K } @Override - protected CustomDbxRawClientV2 connect(final Proxy proxy, final HostKeyCallback callback, final LoginCallback prompt, final CancelCallback cancel) { + protected CustomDbxRawClientV2 connect(final Proxy proxy, final HostKeyCallback callback, final LoginCallback prompt, final CancelCallback cancel) throws ConnectionCanceledException { final HttpClientBuilder configuration = builder.build(proxy, this, prompt); - authorizationService = new OAuth2RequestInterceptor(configuration.build(), host) + authorizationService = new OAuth2RequestInterceptor(configuration.build(), host, prompt) .withRedirectUri(host.getProtocol().getOAuthRedirectUrl()); configuration.addInterceptorLast(authorizationService); configuration.setServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService, prompt)); diff --git a/eue/src/main/java/ch/cyberduck/core/eue/EueSession.java b/eue/src/main/java/ch/cyberduck/core/eue/EueSession.java index 0d0a51e058a..84ebd0ae09b 100644 --- a/eue/src/main/java/ch/cyberduck/core/eue/EueSession.java +++ b/eue/src/main/java/ch/cyberduck/core/eue/EueSession.java @@ -107,7 +107,7 @@ public void process(final HttpRequest request, final HttpContext context) { request.addHeader(HttpHeaders.AUTHORIZATION, String.format("Basic %s", Base64.encodeToString(String.format("%s:%s", host.getProtocol().getOAuthClientId(), host.getProtocol().getOAuthClientSecret()).getBytes(StandardCharsets.UTF_8), false))); } - }).build(), host) + }).build(), host, prompt) .withRedirectUri(host.getProtocol().getOAuthRedirectUrl() ); configuration.setServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService, prompt)); diff --git a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveSession.java b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveSession.java index 922b2241819..ad6e5750233 100644 --- a/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveSession.java +++ b/googledrive/src/main/java/ch/cyberduck/core/googledrive/DriveSession.java @@ -25,7 +25,9 @@ import ch.cyberduck.core.UrlProvider; import ch.cyberduck.core.UseragentProvider; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.exception.HostParserException; +import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.features.*; import ch.cyberduck.core.http.DefaultHttpRateLimiter; import ch.cyberduck.core.http.HttpSession; @@ -68,9 +70,9 @@ public DriveSession(final Host host, final X509TrustManager trust, final X509Key } @Override - protected Drive connect(final Proxy proxy, final HostKeyCallback callback, final LoginCallback prompt, final CancelCallback cancel) throws HostParserException { + protected Drive connect(final Proxy proxy, final HostKeyCallback callback, final LoginCallback prompt, final CancelCallback cancel) throws HostParserException, ConnectionCanceledException { final HttpClientBuilder configuration = builder.build(proxy, this, prompt); - authorizationService = new OAuth2RequestInterceptor(builder.build(ProxyFactory.get().find(host.getProtocol().getOAuthAuthorizationUrl()), this, prompt).build(), host) + authorizationService = new OAuth2RequestInterceptor(builder.build(ProxyFactory.get().find(host.getProtocol().getOAuthAuthorizationUrl()), this, prompt).build(), host, prompt) .withRedirectUri(host.getProtocol().getOAuthRedirectUrl()); configuration.addInterceptorLast(authorizationService); configuration.setServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService, prompt)); diff --git a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageSession.java b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageSession.java index ae3d57b831a..d6c0744eca4 100644 --- a/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageSession.java +++ b/googlestorage/src/main/java/ch/cyberduck/core/googlestorage/GoogleStorageSession.java @@ -25,6 +25,8 @@ import ch.cyberduck.core.UseragentProvider; import ch.cyberduck.core.cdn.DistributionConfiguration; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.ConnectionCanceledException; +import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.features.*; import ch.cyberduck.core.http.HttpSession; import ch.cyberduck.core.http.UserAgentHttpRequestInitializer; @@ -63,9 +65,9 @@ public GoogleStorageSession(final Host host, final X509TrustManager trust, final } @Override - protected Storage connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) { + protected Storage connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws ConnectionCanceledException { final HttpClientBuilder configuration = builder.build(proxy, this, prompt); - authorizationService = new OAuth2RequestInterceptor(builder.build(ProxyFactory.get().find(host.getProtocol().getOAuthAuthorizationUrl()), this, prompt).build(), host) + authorizationService = new OAuth2RequestInterceptor(builder.build(ProxyFactory.get().find(host.getProtocol().getOAuthAuthorizationUrl()), this, prompt).build(), host, prompt) .withRedirectUri(host.getProtocol().getOAuthRedirectUrl()); configuration.addInterceptorLast(authorizationService); configuration.setServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService, prompt)); diff --git a/hubic/src/main/java/ch/cyberduck/core/hubic/HubicSession.java b/hubic/src/main/java/ch/cyberduck/core/hubic/HubicSession.java index 116525ee39d..175bd2ab95b 100644 --- a/hubic/src/main/java/ch/cyberduck/core/hubic/HubicSession.java +++ b/hubic/src/main/java/ch/cyberduck/core/hubic/HubicSession.java @@ -22,6 +22,8 @@ import ch.cyberduck.core.OAuthTokens; import ch.cyberduck.core.cdn.DistributionConfiguration; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.ConnectionCanceledException; +import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor; import ch.cyberduck.core.oauth.OAuth2RequestInterceptor; import ch.cyberduck.core.openstack.SwiftExceptionMappingService; @@ -50,9 +52,9 @@ public HubicSession(final Host host, final X509TrustManager trust, final X509Key } @Override - protected Client connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) { + protected Client connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws ConnectionCanceledException { final HttpClientBuilder configuration = builder.build(proxy, this, prompt); - authorizationService = new OAuth2RequestInterceptor(configuration.build(), host) + authorizationService = new OAuth2RequestInterceptor(configuration.build(), host, prompt) .withRedirectUri(host.getProtocol().getOAuthRedirectUrl()); configuration.addInterceptorLast(authorizationService); configuration.setServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService, prompt)); diff --git a/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationService.java b/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationService.java index 25eae92e0be..1e4735e19af 100644 --- a/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationService.java +++ b/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2AuthorizationService.java @@ -24,6 +24,7 @@ import ch.cyberduck.core.LoginOptions; import ch.cyberduck.core.OAuthTokens; import ch.cyberduck.core.PreferencesUseragentProvider; +import ch.cyberduck.core.Profile; import ch.cyberduck.core.StringAppender; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.InteroperabilityException; @@ -91,21 +92,23 @@ public class OAuth2AuthorizationService { private final HttpTransport transport; - public OAuth2AuthorizationService(final HttpClient client, + public OAuth2AuthorizationService(final HttpClient client, final Host host, final String tokenServerUrl, final String authorizationServerUrl, - final String clientid, final String clientsecret, final List scopes, final boolean pkce) { - this(new ApacheHttpTransport(client), - tokenServerUrl, authorizationServerUrl, clientid, clientsecret, scopes, pkce); + final String clientid, final String clientsecret, final List scopes, final boolean pkce, final LoginCallback prompt) throws LoginCanceledException { + this(new ApacheHttpTransport(client), host, + tokenServerUrl, authorizationServerUrl, clientid, clientsecret, scopes, pkce, prompt); } - public OAuth2AuthorizationService(final HttpTransport transport, + public OAuth2AuthorizationService(final HttpTransport transport, final Host host, final String tokenServerUrl, final String authorizationServerUrl, - final String clientid, final String clientsecret, final List scopes, final boolean pkce) { + final String clientid, final String clientsecret, final List scopes, final boolean pkce, final LoginCallback prompt) throws LoginCanceledException { this.transport = transport; this.tokenServerUrl = tokenServerUrl; this.authorizationServerUrl = authorizationServerUrl; - this.clientid = clientid; - this.clientsecret = clientsecret; + this.clientid = prompt(host, prompt, Profile.OAUTH_CLIENT_ID_KEY, LocaleFactory.localizedString( + Profile.OAUTH_CLIENT_ID_KEY, "Credentials"), clientid); + this.clientsecret = prompt(host, prompt, Profile.OAUTH_CLIENT_SECRET_KEY, LocaleFactory.localizedString( + Profile.OAUTH_CLIENT_SECRET_KEY, "Credentials"), clientsecret); this.scopes = scopes; this.pkce = pkce; } @@ -173,11 +176,11 @@ private TokenResponse authorizeWithCode(final Host bookmark, final LoginCallback } else { final Credentials clientid = prompt.prompt(bookmark, - LocaleFactory.localizedString("OAuth Client ID", "Credentials"), + LocaleFactory.localizedString(Profile.OAUTH_CLIENT_ID_KEY, "Credentials"), LocaleFactory.localizedString("Provide additional login credentials", "Credentials"), new LoginOptions(bookmark.getProtocol()) .user(true).password(true) - .passwordPlaceholder("OAuth Client ID")); + .passwordPlaceholder(LocaleFactory.localizedString(Profile.OAUTH_CLIENT_ID_KEY, "Credentials"))); if(clientid.isSaved()) { bookmark.setProperty("oauth.clientid", clientid.getPassword()); } @@ -377,4 +380,20 @@ public TokenResponse toTokenResponse() { } } + /** + * Prompt for value if missing + */ + private static String prompt(final Host bookmark, final LoginCallback prompt, + final String property, final String message, final String value) throws LoginCanceledException { + if(null == value) { + final Credentials input = prompt.prompt(bookmark, message, + LocaleFactory.localizedString("Provide additional login credentials", "Credentials"), + new LoginOptions().icon(bookmark.getProtocol().disk())); + if(input.isSaved()) { + bookmark.setProperty(property, input.getPassword()); + } + return input.getPassword(); + } + return value; + } } diff --git a/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2RequestInterceptor.java b/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2RequestInterceptor.java index 691d6d47495..2c31ac47d66 100644 --- a/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2RequestInterceptor.java +++ b/oauth/src/main/java/ch/cyberduck/core/oauth/OAuth2RequestInterceptor.java @@ -25,6 +25,7 @@ import ch.cyberduck.core.Scheme; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.LocalAccessDeniedException; +import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.threading.CancelCallback; import org.apache.commons.lang3.StringUtils; @@ -54,7 +55,7 @@ public class OAuth2RequestInterceptor extends OAuth2AuthorizationService impleme private final HostPasswordStore store = PasswordStoreFactory.get(); private final Host host; - public OAuth2RequestInterceptor(final HttpClient client, final Host host) { + public OAuth2RequestInterceptor(final HttpClient client, final Host host, final LoginCallback prompt) throws LoginCanceledException { this(client, host, Scheme.isURL(host.getProtocol().getOAuthTokenUrl()) ? host.getProtocol().getOAuthTokenUrl() : new HostUrlProvider().withUsername(false).withPath(true).get( host.getProtocol().getScheme(), host.getPort(), null, host.getHostname(), host.getProtocol().getOAuthTokenUrl()), @@ -63,12 +64,12 @@ public OAuth2RequestInterceptor(final HttpClient client, final Host host) { host.getProtocol().getOAuthClientId(), host.getProtocol().getOAuthClientSecret(), host.getProtocol().getOAuthScopes(), - host.getProtocol().isOAuthPKCE()); + host.getProtocol().isOAuthPKCE(), prompt); } public OAuth2RequestInterceptor(final HttpClient client, final Host host, final String tokenServerUrl, final String authorizationServerUrl, - final String clientid, final String clientsecret, final List scopes, final boolean pkce) { - super(client, tokenServerUrl, authorizationServerUrl, clientid, clientsecret, scopes, pkce); + final String clientid, final String clientsecret, final List scopes, final boolean pkce, final LoginCallback prompt) throws LoginCanceledException { + super(client, host, tokenServerUrl, authorizationServerUrl, clientid, clientsecret, scopes, pkce, prompt); this.host = host; } diff --git a/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphSession.java b/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphSession.java index 88e01281abd..ba3d542b3bf 100644 --- a/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphSession.java +++ b/onedrive/src/main/java/ch/cyberduck/core/onedrive/GraphSession.java @@ -24,7 +24,9 @@ import ch.cyberduck.core.SimplePathPredicate; import ch.cyberduck.core.UrlProvider; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.exception.HostParserException; +import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.features.*; import ch.cyberduck.core.http.HttpSession; import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor; @@ -125,10 +127,10 @@ public User.Metadata getUser() { } @Override - protected OneDriveAPI connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws HostParserException { + protected OneDriveAPI connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws HostParserException, ConnectionCanceledException { final HttpClientBuilder configuration = builder.build(proxy, this, prompt); authorizationService = new OAuth2RequestInterceptor( - builder.build(ProxyFactory.get().find(host.getProtocol().getOAuthAuthorizationUrl()), this, prompt).build(), host) { + builder.build(ProxyFactory.get().find(host.getProtocol().getOAuthAuthorizationUrl()), this, prompt).build(), host, prompt) { @Override public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { if(request.containsHeader(HttpHeaders.AUTHORIZATION)) { diff --git a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftSession.java b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftSession.java index b9dbd0cd4c3..254262b02fb 100644 --- a/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftSession.java +++ b/openstack/src/main/java/ch/cyberduck/core/openstack/SwiftSession.java @@ -30,7 +30,9 @@ import ch.cyberduck.core.cdn.DistributionConfiguration; import ch.cyberduck.core.exception.AccessDeniedException; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.ConnectionCanceledException; import ch.cyberduck.core.exception.InteroperabilityException; +import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.exception.LoginFailureException; import ch.cyberduck.core.features.*; import ch.cyberduck.core.http.HttpSession; @@ -71,7 +73,7 @@ public SwiftSession(final Host host, final X509TrustManager trust, final X509Key } @Override - protected Client connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) { + protected Client connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws ConnectionCanceledException { // Always inject new pool to builder on connect because the pool is shutdown on disconnect final HttpClientBuilder pool = builder.build(proxy, this, prompt); pool.disableContentCompression(); diff --git a/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java b/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java index ba976addb08..93515f26030 100644 --- a/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java +++ b/owncloud/src/main/java/ch/cyberduck/core/owncloud/OwncloudSession.java @@ -21,6 +21,8 @@ import ch.cyberduck.core.UrlProvider; import ch.cyberduck.core.dav.DAVSession; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.ConnectionCanceledException; +import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Home; @@ -60,10 +62,10 @@ public OwncloudSession(final Host host, final X509TrustManager trust, final X509 } @Override - protected HttpClientBuilder getConfiguration(final Proxy proxy, final LoginCallback prompt) { + protected HttpClientBuilder getConfiguration(final Proxy proxy, final LoginCallback prompt) throws ConnectionCanceledException { final HttpClientBuilder configuration = super.getConfiguration(proxy, prompt); if(host.getProtocol().isOAuthConfigurable()) { - authorizationService = new OAuth2RequestInterceptor(configuration.build(), host) + authorizationService = new OAuth2RequestInterceptor(configuration.build(), host, prompt) .withFlowType(OAuth2AuthorizationService.FlowType.valueOf(host.getProtocol().getAuthorization())) .withRedirectUri(host.getProtocol().getOAuthRedirectUrl()); configuration.addInterceptorLast(authorizationService); diff --git a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateSession.java b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateSession.java index 25661760222..c98c7600200 100644 --- a/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateSession.java +++ b/storegate/src/main/java/ch/cyberduck/core/storegate/StoregateSession.java @@ -27,6 +27,8 @@ import ch.cyberduck.core.PreferencesUseragentProvider; import ch.cyberduck.core.Scheme; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.ConnectionCanceledException; +import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.exception.LoginFailureException; import ch.cyberduck.core.features.*; import ch.cyberduck.core.http.HttpSession; @@ -95,7 +97,7 @@ public StoregateSession(final Host host, final X509TrustManager trust, final X50 } @Override - protected StoregateApiClient connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) { + protected StoregateApiClient connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws ConnectionCanceledException { final HttpClientBuilder configuration = builder.build(proxy, this, prompt); final PreferencesReader preferences = new HostPreferences(host); authorizationService = new OAuth2RequestInterceptor(builder.build(proxy, this, prompt).addInterceptorLast(new HttpRequestInterceptor() { @@ -104,7 +106,7 @@ public void process(final HttpRequest request, final HttpContext context) { request.addHeader(HttpHeaders.AUTHORIZATION, String.format("Basic %s", Base64.encodeToString(String.format("%s:%s", host.getProtocol().getOAuthClientId(), host.getProtocol().getOAuthClientSecret()).getBytes(StandardCharsets.UTF_8), false))); } - }).build(), host) + }).build(), host, prompt) .withRedirectUri(CYBERDUCK_REDIRECT_URI.equals(host.getProtocol().getOAuthRedirectUrl()) ? host.getProtocol().getOAuthRedirectUrl() : Scheme.isURL(host.getProtocol().getOAuthRedirectUrl()) ? host.getProtocol().getOAuthRedirectUrl() : new HostUrlProvider().withUsername(false).withPath(true).get( host.getProtocol().getScheme(), host.getPort(), null, host.getHostname(), host.getProtocol().getOAuthRedirectUrl()) diff --git a/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java b/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java index ace5c3a9d28..598ba59d8e6 100644 --- a/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java +++ b/webdav/src/main/java/ch/cyberduck/core/dav/DAVSession.java @@ -109,7 +109,7 @@ protected DAVClient connect(final Proxy proxy, final HostKeyCallback key, final return new DAVClient(new HostUrlProvider().withUsername(false).get(host), configuration); } - protected HttpClientBuilder getConfiguration(final Proxy proxy, final LoginCallback prompt) { + protected HttpClientBuilder getConfiguration(final Proxy proxy, final LoginCallback prompt) throws ConnectionCanceledException { final HttpClientBuilder configuration = builder.build(proxy, this, prompt); configuration.setRedirectStrategy(new DAVRedirectStrategy(redirect)); return configuration;