Index: trunk/src/org/openstreetmap/josm/data/oauth/OAuthParameters.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/oauth/OAuthParameters.java	(revision 9354)
+++ trunk/src/org/openstreetmap/josm/data/oauth/OAuthParameters.java	(revision 9355)
@@ -4,8 +4,8 @@
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.Objects;
 
 import oauth.signpost.OAuthConsumer;
 import oauth.signpost.OAuthProvider;
-
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Preferences;
@@ -27,16 +27,4 @@
      */
     public static final String DEFAULT_JOSM_CONSUMER_SECRET = "rIkjpPcBNkMQxrqzcOvOC4RRuYupYr7k8mfP13H5";
-    /**
-     * The default OSM OAuth request token URL.
-     */
-    public static final String DEFAULT_REQUEST_TOKEN_URL = Main.getOSMWebsite() + "/oauth/request_token";
-    /**
-     * The default OSM OAuth access token URL.
-     */
-    public static final String DEFAULT_ACCESS_TOKEN_URL = Main.getOSMWebsite() + "/oauth/access_token";
-    /**
-     * The default OSM OAuth authorize URL.
-     */
-    public static final String DEFAULT_AUTHORISE_URL = Main.getOSMWebsite() + "/oauth/authorize";
 
     /**
@@ -60,22 +48,35 @@
      */
     public static OAuthParameters createDefault(String apiUrl) {
-        String host = "";
-        if (!OsmApi.DEFAULT_API_URL.equals(apiUrl)) {
+        final String consumerKey;
+        final String consumerSecret;
+        final String serverUrl;
+
+        if (apiUrl != null) {
+            // validate URL syntax
             try {
-                host = new URL(apiUrl).getHost();
+                new URL(apiUrl);
             } catch (MalformedURLException e) {
-                // Ignored
-                if (Main.isTraceEnabled()) {
-                    Main.trace(e.getMessage());
-                }
+                apiUrl = null;
             }
         }
-        boolean osmDevServer = host.endsWith("dev.openstreetmap.org");
+
+        if (apiUrl != null && !OsmApi.DEFAULT_API_URL.equals(apiUrl)) {
+            consumerKey = ""; // a custom consumer key is required
+            consumerSecret = ""; // a custom consumer secret is requireds
+            serverUrl = apiUrl.replaceAll("/api$", "");
+        } else {
+            consumerKey = DEFAULT_JOSM_CONSUMER_KEY;
+            consumerSecret = DEFAULT_JOSM_CONSUMER_SECRET;
+            serverUrl = Main.getOSMWebsite();
+        }
+
         return new OAuthParameters(
-            DEFAULT_JOSM_CONSUMER_KEY,
-            DEFAULT_JOSM_CONSUMER_SECRET,
-            osmDevServer ? DEFAULT_REQUEST_TOKEN_URL.replace("www.openstreetmap.org", host) : DEFAULT_REQUEST_TOKEN_URL,
-            osmDevServer ? DEFAULT_ACCESS_TOKEN_URL.replace("www.openstreetmap.org", host) : DEFAULT_ACCESS_TOKEN_URL,
-            osmDevServer ? DEFAULT_AUTHORISE_URL.replace("www.openstreetmap.org", host) : DEFAULT_AUTHORISE_URL);
+                consumerKey,
+                consumerSecret,
+                serverUrl + "/oauth/request_token",
+                serverUrl + "/oauth/access_token",
+                serverUrl + "/oauth/authorize",
+                serverUrl + "/login",
+                serverUrl + "/logout");
     }
 
@@ -93,6 +94,24 @@
                 pref.get("oauth.settings.request-token-url", parameters.getRequestTokenUrl()),
                 pref.get("oauth.settings.access-token-url", parameters.getAccessTokenUrl()),
-                pref.get("oauth.settings.authorise-url", parameters.getAuthoriseUrl())
-                );
+                pref.get("oauth.settings.authorise-url", parameters.getAuthoriseUrl()),
+                pref.get("oauth.settings.osm-login-url", parameters.getOsmLoginUrl()),
+                pref.get("oauth.settings.osm-logout-url", parameters.getOsmLogoutUrl()));
+    }
+
+    /**
+     * Remembers the current values in the preferences <code>pref</code>.
+     *
+     * @param pref the preferences. Must not be null.
+     * @throws IllegalArgumentException if pref is null.
+     */
+    public void rememberPreferences(Preferences pref) {
+        CheckParameterUtil.ensureParameterNotNull(pref, "pref");
+        pref.put("oauth.settings.consumer-key", getConsumerKey());
+        pref.put("oauth.settings.consumer-secret", getConsumerSecret());
+        pref.put("oauth.settings.request-token-url", getRequestTokenUrl());
+        pref.put("oauth.settings.access-token-url", getAccessTokenUrl());
+        pref.put("oauth.settings.authorise-url", getAuthoriseUrl());
+        pref.put("oauth.settings.osm-login-url", getOsmLoginUrl());
+        pref.put("oauth.settings.osm-logout-url", getOsmLogoutUrl());
     }
 
@@ -102,4 +121,6 @@
     private final String accessTokenUrl;
     private final String authoriseUrl;
+    private final String osmLoginUrl;
+    private final String osmLogoutUrl;
 
     /**
@@ -110,5 +131,6 @@
      * @param accessTokenUrl access token URL
      * @param authoriseUrl authorise URL
-     *
+     * @param osmLoginUrl the OSM login URL (for automatic mode)
+     * @param osmLogoutUrl the OSM logout URL (for automatic mode)
      * @see #createDefault
      * @see #createFromPreferences
@@ -116,5 +138,5 @@
      */
     public OAuthParameters(String consumerKey, String consumerSecret,
-            String requestTokenUrl, String accessTokenUrl, String authoriseUrl) {
+                           String requestTokenUrl, String accessTokenUrl, String authoriseUrl, String osmLoginUrl, String osmLogoutUrl) {
         this.consumerKey = consumerKey;
         this.consumerSecret = consumerSecret;
@@ -122,4 +144,6 @@
         this.accessTokenUrl = accessTokenUrl;
         this.authoriseUrl = authoriseUrl;
+        this.osmLoginUrl = osmLoginUrl;
+        this.osmLogoutUrl = osmLogoutUrl;
     }
 
@@ -137,4 +161,6 @@
         this.requestTokenUrl = other.requestTokenUrl;
         this.authoriseUrl = other.authoriseUrl;
+        this.osmLoginUrl = other.osmLoginUrl;
+        this.osmLogoutUrl = other.osmLogoutUrl;
     }
 
@@ -177,4 +203,20 @@
     public String getAuthoriseUrl() {
         return authoriseUrl;
+    }
+
+    /**
+     * Gets the URL used to login users on the website (for automatic mode).
+     * @return The URL used to login users
+     */
+    public String getOsmLoginUrl() {
+        return osmLoginUrl;
+    }
+
+    /**
+     * Gets the URL used to logout users on the website (for automatic mode).
+     * @return The URL used to logout users
+     */
+    public String getOsmLogoutUrl() {
+        return osmLogoutUrl;
     }
 
@@ -205,50 +247,20 @@
 
     @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        OAuthParameters that = (OAuthParameters) o;
+        return Objects.equals(consumerKey, that.consumerKey) &&
+                Objects.equals(consumerSecret, that.consumerSecret) &&
+                Objects.equals(requestTokenUrl, that.requestTokenUrl) &&
+                Objects.equals(accessTokenUrl, that.accessTokenUrl) &&
+                Objects.equals(authoriseUrl, that.authoriseUrl) &&
+                Objects.equals(osmLoginUrl, that.osmLoginUrl) &&
+                Objects.equals(osmLogoutUrl, that.osmLogoutUrl);
+    }
+
+    @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((accessTokenUrl == null) ? 0 : accessTokenUrl.hashCode());
-        result = prime * result + ((authoriseUrl == null) ? 0 : authoriseUrl.hashCode());
-        result = prime * result + ((consumerKey == null) ? 0 : consumerKey.hashCode());
-        result = prime * result + ((consumerSecret == null) ? 0 : consumerSecret.hashCode());
-        result = prime * result + ((requestTokenUrl == null) ? 0 : requestTokenUrl.hashCode());
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj)
-            return true;
-        if (obj == null)
-            return false;
-        if (getClass() != obj.getClass())
-            return false;
-        OAuthParameters other = (OAuthParameters) obj;
-        if (accessTokenUrl == null) {
-            if (other.accessTokenUrl != null)
-                return false;
-        } else if (!accessTokenUrl.equals(other.accessTokenUrl))
-            return false;
-        if (authoriseUrl == null) {
-            if (other.authoriseUrl != null)
-                return false;
-        } else if (!authoriseUrl.equals(other.authoriseUrl))
-            return false;
-        if (consumerKey == null) {
-            if (other.consumerKey != null)
-                return false;
-        } else if (!consumerKey.equals(other.consumerKey))
-            return false;
-        if (consumerSecret == null) {
-            if (other.consumerSecret != null)
-                return false;
-        } else if (!consumerSecret.equals(other.consumerSecret))
-            return false;
-        if (requestTokenUrl == null) {
-            if (other.requestTokenUrl != null)
-                return false;
-        } else if (!requestTokenUrl.equals(other.requestTokenUrl))
-            return false;
-        return true;
+        return Objects.hash(consumerKey, consumerSecret, requestTokenUrl, accessTokenUrl, authoriseUrl, osmLoginUrl, osmLogoutUrl);
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/oauth/AdvancedOAuthPropertiesPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/AdvancedOAuthPropertiesPanel.java	(revision 9354)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/AdvancedOAuthPropertiesPanel.java	(revision 9355)
@@ -35,4 +35,6 @@
  * <li>Access token URL</li>
  * <li>Authorize URL</li>
+ * <li>OSM login URL</li>
+ * <li>OSM logout URL</li>
  * </ul>
  *
@@ -48,4 +50,6 @@
     private JosmTextField tfAccessTokenURL;
     private JosmTextField tfAuthoriseURL;
+    private JosmTextField tfOsmLoginURL;
+    private JosmTextField tfOsmLogoutURL;
     private transient UseDefaultItemListener ilUseDefault;
     private String apiUrl;
@@ -120,4 +124,28 @@
         SelectAllOnFocusGainedDecorator.decorate(tfAuthoriseURL);
 
+
+        // -- OSM login URL
+        gc.gridy = 6;
+        gc.gridx = 0;
+        gc.weightx = 0.0;
+        add(new JLabel(tr("OSM login URL:")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        add(tfOsmLoginURL = new JosmTextField(), gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfOsmLoginURL);
+
+
+        // -- OSM logout URL
+        gc.gridy = 7;
+        gc.gridx = 0;
+        gc.weightx = 0.0;
+        add(new JLabel(tr("OSM logout URL:")), gc);
+
+        gc.gridx = 1;
+        gc.weightx = 1.0;
+        add(tfOsmLogoutURL = new JosmTextField(), gc);
+        SelectAllOnFocusGainedDecorator.decorate(tfOsmLogoutURL);
+
         cbUseDefaults.addItemListener(ilUseDefault = new UseDefaultItemListener());
     }
@@ -125,10 +153,5 @@
     protected boolean hasCustomSettings() {
         OAuthParameters params = OAuthParameters.createDefault(apiUrl);
-        return
-           !tfConsumerKey.getText().equals(params.getConsumerKey())
-        || !tfConsumerSecret.getText().equals(params.getConsumerSecret())
-        || !tfRequestTokenURL.getText().equals(params.getRequestTokenUrl())
-        || !tfAccessTokenURL.getText().equals(params.getAccessTokenUrl())
-        || !tfAuthoriseURL.getText().equals(params.getAuthoriseUrl());
+        return !params.equals(getAdvancedParameters());
     }
 
@@ -173,4 +196,6 @@
         tfAccessTokenURL.setText(params.getAccessTokenUrl());
         tfAuthoriseURL.setText(params.getAuthoriseUrl());
+        tfOsmLoginURL.setText(params.getOsmLoginUrl());
+        tfOsmLogoutURL.setText(params.getOsmLogoutUrl());
 
         setChildComponentsEnabled(false);
@@ -198,5 +223,7 @@
             tfRequestTokenURL.getText(),
             tfAccessTokenURL.getText(),
-            tfAuthoriseURL.getText());
+            tfAuthoriseURL.getText(),
+            tfOsmLoginURL.getText(),
+            tfOsmLogoutURL.getText());
     }
 
@@ -220,4 +247,6 @@
             tfAccessTokenURL.setText(parameters.getAccessTokenUrl() == null ? "" : parameters.getAccessTokenUrl());
             tfAuthoriseURL.setText(parameters.getAuthoriseUrl() == null ? "" : parameters.getAuthoriseUrl());
+            tfOsmLoginURL.setText(parameters.getOsmLoginUrl() == null ? "" : parameters.getOsmLoginUrl());
+            tfOsmLogoutURL.setText(parameters.getOsmLogoutUrl() == null ? "" : parameters.getOsmLogoutUrl());
         }
     }
@@ -244,11 +273,5 @@
             resetToDefaultSettings();
         } else {
-            cbUseDefaults.setSelected(false);
-            tfConsumerKey.setText(pref.get("oauth.settings.consumer-key", OAuthParameters.DEFAULT_JOSM_CONSUMER_KEY));
-            tfConsumerSecret.setText(pref.get("oauth.settings.consumer-secret", OAuthParameters.DEFAULT_JOSM_CONSUMER_SECRET));
-            tfRequestTokenURL.setText(pref.get("oauth.settings.request-token-url", OAuthParameters.DEFAULT_REQUEST_TOKEN_URL));
-            tfAccessTokenURL.setText(pref.get("oauth.settings.access-token-url", OAuthParameters.DEFAULT_ACCESS_TOKEN_URL));
-            tfAuthoriseURL.setText(pref.get("oauth.settings.authorise-url", OAuthParameters.DEFAULT_AUTHORISE_URL));
-            setChildComponentsEnabled(true);
+            setAdvancedParameters(OAuthParameters.createFromPreferences(pref));
         }
         ilUseDefault.setEnabled(true);
@@ -265,15 +288,7 @@
         pref.put("oauth.settings.use-default", cbUseDefaults.isSelected());
         if (cbUseDefaults.isSelected()) {
-            pref.put("oauth.settings.consumer-key", null);
-            pref.put("oauth.settings.consumer-secret", null);
-            pref.put("oauth.settings.request-token-url", null);
-            pref.put("oauth.settings.access-token-url", null);
-            pref.put("oauth.settings.authorise-url", null);
+            new OAuthParameters(null, null, null, null, null, null, null).rememberPreferences(pref);
         } else {
-            pref.put("oauth.settings.consumer-key", tfConsumerKey.getText().trim());
-            pref.put("oauth.settings.consumer-secret", tfConsumerSecret.getText().trim());
-            pref.put("oauth.settings.request-token-url", tfRequestTokenURL.getText().trim());
-            pref.put("oauth.settings.access-token-url", tfAccessTokenURL.getText().trim());
-            pref.put("oauth.settings.authorise-url", tfAuthoriseURL.getText().trim());
+            getAdvancedParameters().rememberPreferences(pref);
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/oauth/FullyAutomaticAuthorizationUI.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/FullyAutomaticAuthorizationUI.java	(revision 9354)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/FullyAutomaticAuthorizationUI.java	(revision 9355)
@@ -479,11 +479,5 @@
 
         protected void alertLoginFailed(OsmLoginFailedException e) {
-            String loginUrl = null;
-            try {
-                loginUrl = authClient.buildOsmLoginUrl();
-            } catch (OsmOAuthAuthorizationException e1) {
-                alertInvalidLoginUrl();
-                return;
-            }
+            final String loginUrl = getAdvancedPropertiesPanel().getAdvancedParameters().getOsmLoginUrl();
             HelpAwareOptionPane.showOptionDialog(
                     FullyAutomaticAuthorizationUI.this,
Index: trunk/src/org/openstreetmap/josm/gui/oauth/OsmOAuthAuthorizationClient.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/OsmOAuthAuthorizationClient.java	(revision 9354)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/OsmOAuthAuthorizationClient.java	(revision 9355)
@@ -8,5 +8,4 @@
 import java.lang.reflect.Field;
 import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
@@ -247,38 +246,4 @@
 
     /**
-     * Derives the OSM login URL from the OAuth Authorization Website URL
-     *
-     * @return the OSM login URL
-     * @throws OsmOAuthAuthorizationException if something went wrong, in particular if the
-     * URLs are malformed
-     */
-    public String buildOsmLoginUrl() throws OsmOAuthAuthorizationException {
-        try {
-            URL autUrl = new URL(oauthProviderParameters.getAuthoriseUrl());
-            URL url = new URL(Main.pref.get("oauth.protocol", "https"), autUrl.getHost(), autUrl.getPort(), "/login");
-            return url.toString();
-        } catch (MalformedURLException e) {
-            throw new OsmOAuthAuthorizationException(e);
-        }
-    }
-
-    /**
-     * Derives the OSM logout URL from the OAuth Authorization Website URL
-     *
-     * @return the OSM logout URL
-     * @throws OsmOAuthAuthorizationException if something went wrong, in particular if the
-     * URLs are malformed
-     */
-    protected String buildOsmLogoutUrl() throws OsmOAuthAuthorizationException {
-        try {
-            URL autUrl = new URL(oauthProviderParameters.getAuthoriseUrl());
-            URL url = new URL(Main.pref.get("oauth.protocol", "https"), autUrl.getHost(), autUrl.getPort(), "/logout");
-            return url.toString();
-        } catch (MalformedURLException e) {
-            throw new OsmOAuthAuthorizationException(e);
-        }
-    }
-
-    /**
      * Submits a request to the OSM website for a login form. The OSM website replies a session ID in
      * a cookie.
@@ -289,7 +254,5 @@
     protected SessionId fetchOsmWebsiteSessionId() throws OsmOAuthAuthorizationException {
         try {
-            StringBuilder sb = new StringBuilder();
-            sb.append(buildOsmLoginUrl()).append("?cookie_test=true");
-            URL url = new URL(sb.toString());
+            final URL url = new URL(oauthProviderParameters.getOsmLoginUrl() + "?cookie_test=true");
             synchronized (this) {
                 connection = HttpClient.create(url);
@@ -341,5 +304,5 @@
     protected void authenticateOsmSession(SessionId sessionId, String userName, String password) throws OsmLoginFailedException {
         try {
-            URL url = new URL(buildOsmLoginUrl());
+            final URL url = new URL(oauthProviderParameters.getOsmLoginUrl());
             final HttpClient client = HttpClient.create(url, "POST").useCache(false);
 
@@ -383,5 +346,5 @@
     protected void logoutOsmSession(SessionId sessionId) throws OsmOAuthAuthorizationException {
         try {
-            URL url = new URL(buildOsmLogoutUrl());
+            URL url = new URL(oauthProviderParameters.getOsmLogoutUrl());
             synchronized (this) {
                 connection = HttpClient.create(url).setMaxRedirects(-1);
