Make more use of Guice to reduce boiler plate code

This also enforces a stricter adherence to naming conventions used by
almost all implemented OAuth providers.

Change-Id: I5e394c2e0062507b0e14a07cf67e216c18d3355a
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/AzureOAuthServiceModule.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/AzureOAuthServiceModule.java
new file mode 100644
index 0000000..95563bf
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/AzureOAuthServiceModule.java
@@ -0,0 +1,60 @@
+// Copyright (C) 2025 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.oauth;
+
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
+import com.google.gerrit.server.config.PluginConfig;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.ProvisionException;
+import com.googlesource.gerrit.plugins.oauth.azure.AzureActiveDirectoryService;
+
+public class AzureOAuthServiceModule extends AbstractModule {
+  private final OAuthPluginConfigFactory cfgFactory;
+
+  @Inject
+  public AzureOAuthServiceModule(OAuthPluginConfigFactory cfgFactory) {
+    this.cfgFactory = cfgFactory;
+  }
+
+  @Override
+  public void configure() {
+    boolean office365LegacyProviderBound = false;
+    PluginConfig cfg = cfgFactory.create(AzureActiveDirectoryService.LEGACY_PROVIDER_NAME);
+    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
+      office365LegacyProviderBound = true;
+      bindOAuthServiceProvider(AzureActiveDirectoryService.LEGACY_PROVIDER_NAME);
+    }
+    cfg = cfgFactory.create(AzureActiveDirectoryService.PROVIDER_NAME);
+    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
+      // ?: Check if the legacy Office365 is already bound, we can only have one of these bound at
+      // one time
+      if (office365LegacyProviderBound) {
+        // -> Yes, the legacy Office365 is already bound and we are trying to bind the
+        // AzureActiveDirectoryService.CONFIG_SUFFIX at the same time.
+        throw new ProvisionException("Legacy Office365 OAuth provider is already bound!");
+      }
+      bindOAuthServiceProvider(AzureActiveDirectoryService.PROVIDER_NAME);
+    }
+  }
+
+  protected void bindOAuthServiceProvider(String serviceProviderName) {
+    String extIdScheme = OAuthServiceProviderExternalIdScheme.create(serviceProviderName);
+    bind(OAuthServiceProvider.class)
+        .annotatedWith(Exports.named(extIdScheme))
+        .to(AzureActiveDirectoryService.class);
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/HttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/HttpModule.java
index 3cf1564..9b826ed 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/HttpModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/HttpModule.java
@@ -14,18 +14,11 @@
 
 package com.googlesource.gerrit.plugins.oauth;
 
-import com.google.gerrit.extensions.annotations.Exports;
-import com.google.gerrit.extensions.annotations.PluginName;
-import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
-import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
-import com.google.inject.ProvisionException;
-import com.google.inject.servlet.ServletModule;
 import com.googlesource.gerrit.plugins.oauth.airvantage.AirVantageOAuthService;
 import com.googlesource.gerrit.plugins.oauth.auth0.Auth0OAuthService;
 import com.googlesource.gerrit.plugins.oauth.authentik.AuthentikOAuthService;
-import com.googlesource.gerrit.plugins.oauth.azure.AzureActiveDirectoryService;
 import com.googlesource.gerrit.plugins.oauth.bitbucket.BitbucketOAuthService;
 import com.googlesource.gerrit.plugins.oauth.cas.CasOAuthService;
 import com.googlesource.gerrit.plugins.oauth.cognito.CognitoOAuthService;
@@ -39,147 +32,31 @@
 import com.googlesource.gerrit.plugins.oauth.phabricator.PhabricatorOAuthService;
 import com.googlesource.gerrit.plugins.oauth.tuleap.TuleapOAuthService;
 
-class HttpModule extends ServletModule {
-
-  private final PluginConfigFactory cfgFactory;
-  private final String pluginName;
+class HttpModule extends AbstractModule {
+  private final OAuthPluginConfigFactory cfgFactory;
 
   @Inject
-  HttpModule(PluginConfigFactory cfgFactory, @PluginName String pluginName) {
+  HttpModule(OAuthPluginConfigFactory cfgFactory) {
     this.cfgFactory = cfgFactory;
-    this.pluginName = pluginName;
   }
 
   @Override
-  protected void configureServlets() {
-    PluginConfig cfg =
-        cfgFactory.getFromGerritConfig(pluginName + GoogleOAuthService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(GoogleOAuthService.CONFIG_SUFFIX))
-          .to(GoogleOAuthService.class);
-    }
-
-    cfg = cfgFactory.getFromGerritConfig(pluginName + GitHubOAuthService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(GitHubOAuthService.CONFIG_SUFFIX))
-          .to(GitHubOAuthService.class);
-    }
-
-    cfg = cfgFactory.getFromGerritConfig(pluginName + BitbucketOAuthService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(BitbucketOAuthService.CONFIG_SUFFIX))
-          .to(BitbucketOAuthService.class);
-    }
-
-    cfg = cfgFactory.getFromGerritConfig(pluginName + CasOAuthService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(CasOAuthService.CONFIG_SUFFIX))
-          .to(CasOAuthService.class);
-    }
-
-    cfg = cfgFactory.getFromGerritConfig(pluginName + FacebookOAuthService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(FacebookOAuthService.CONFIG_SUFFIX))
-          .to(FacebookOAuthService.class);
-    }
-
-    cfg = cfgFactory.getFromGerritConfig(pluginName + GitLabOAuthService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(GitLabOAuthService.CONFIG_SUFFIX))
-          .to(GitLabOAuthService.class);
-    }
-
-    cfg = cfgFactory.getFromGerritConfig(pluginName + LemonLDAPOAuthService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(LemonLDAPOAuthService.CONFIG_SUFFIX))
-          .to(LemonLDAPOAuthService.class);
-    }
-
-    cfg = cfgFactory.getFromGerritConfig(pluginName + DexOAuthService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(DexOAuthService.CONFIG_SUFFIX))
-          .to(DexOAuthService.class);
-    }
-
-    cfg = cfgFactory.getFromGerritConfig(pluginName + KeycloakOAuthService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(KeycloakOAuthService.CONFIG_SUFFIX))
-          .to(KeycloakOAuthService.class);
-    }
-
-    boolean office365LegacyProviderBound = false;
-    cfg =
-        cfgFactory.getFromGerritConfig(
-            pluginName + AzureActiveDirectoryService.CONFIG_SUFFIX_LEGACY);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      office365LegacyProviderBound = true;
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(AzureActiveDirectoryService.CONFIG_SUFFIX))
-          .to(AzureActiveDirectoryService.class);
-    }
-    cfg = cfgFactory.getFromGerritConfig(pluginName + AzureActiveDirectoryService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      // ?: Check if the legacy Office365 is already bound, we can only have one of these bound at
-      // one time
-      if (office365LegacyProviderBound) {
-        // -> Yes, the legacy Office365 is already bound and we are trying to bind the
-        // AzureActiveDirectoryService.CONFIG_SUFFIX at the same time.
-        throw new ProvisionException("Legacy Office365 OAuth provider is already bound!");
-      }
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(AzureActiveDirectoryService.CONFIG_SUFFIX))
-          .to(AzureActiveDirectoryService.class);
-    }
-
-    cfg = cfgFactory.getFromGerritConfig(pluginName + AirVantageOAuthService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(AirVantageOAuthService.CONFIG_SUFFIX))
-          .to(AirVantageOAuthService.class);
-    }
-
-    cfg = cfgFactory.getFromGerritConfig(pluginName + PhabricatorOAuthService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(PhabricatorOAuthService.CONFIG_SUFFIX))
-          .to(PhabricatorOAuthService.class);
-    }
-
-    cfg = cfgFactory.getFromGerritConfig(pluginName + TuleapOAuthService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(TuleapOAuthService.CONFIG_SUFFIX))
-          .to(TuleapOAuthService.class);
-    }
-
-    cfg = cfgFactory.getFromGerritConfig(pluginName + Auth0OAuthService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(Auth0OAuthService.CONFIG_SUFFIX))
-          .to(Auth0OAuthService.class);
-    }
-
-    cfg = cfgFactory.getFromGerritConfig(pluginName + AuthentikOAuthService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(AuthentikOAuthService.CONFIG_SUFFIX))
-          .to(AuthentikOAuthService.class);
-    }
-
-    cfg = cfgFactory.getFromGerritConfig(pluginName + CognitoOAuthService.CONFIG_SUFFIX);
-    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
-      bind(OAuthServiceProvider.class)
-          .annotatedWith(Exports.named(CognitoOAuthService.CONFIG_SUFFIX))
-          .to(CognitoOAuthService.class);
-    }
+  protected void configure() {
+    install(new OAuthServiceModule(cfgFactory, AirVantageOAuthService.class));
+    install(new OAuthServiceModule(cfgFactory, Auth0OAuthService.class));
+    install(new OAuthServiceModule(cfgFactory, AuthentikOAuthService.class));
+    install(new AzureOAuthServiceModule(cfgFactory));
+    install(new OAuthServiceModule(cfgFactory, BitbucketOAuthService.class));
+    install(new OAuthServiceModule(cfgFactory, CasOAuthService.class));
+    install(new OAuthServiceModule(cfgFactory, CognitoOAuthService.class));
+    install(new OAuthServiceModule(cfgFactory, DexOAuthService.class));
+    install(new OAuthServiceModule(cfgFactory, FacebookOAuthService.class));
+    install(new OAuthServiceModule(cfgFactory, GitHubOAuthService.class));
+    install(new OAuthServiceModule(cfgFactory, GitLabOAuthService.class));
+    install(new OAuthServiceModule(cfgFactory, GoogleOAuthService.class));
+    install(new OAuthServiceModule(cfgFactory, KeycloakOAuthService.class));
+    install(new OAuthServiceModule(cfgFactory, LemonLDAPOAuthService.class));
+    install(new OAuthServiceModule(cfgFactory, PhabricatorOAuthService.class));
+    install(new OAuthServiceModule(cfgFactory, TuleapOAuthService.class));
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/InitOAuth.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/InitOAuth.java
index 15986bd..b93fce5 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/InitOAuth.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/InitOAuth.java
@@ -17,6 +17,7 @@
 
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.pgm.init.api.ConsoleUI;
 import com.google.gerrit.pgm.init.api.InitStep;
 import com.google.gerrit.pgm.init.api.Section;
@@ -59,6 +60,8 @@
   static String FIX_LEGACY_USER_ID_QUESTION = "Fix legacy user id, without oauth provider prefix?";
 
   private final ConsoleUI ui;
+  private final Section.Factory sections;
+  private final String pluginName;
   private final Section googleOAuthProviderSection;
   private final Section githubOAuthProviderSection;
   private final Section bitbucketOAuthProviderSection;
@@ -80,40 +83,27 @@
   @Inject
   InitOAuth(ConsoleUI ui, Section.Factory sections, @PluginName String pluginName) {
     this.ui = ui;
-    this.googleOAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + GoogleOAuthService.CONFIG_SUFFIX);
-    this.githubOAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + GitHubOAuthService.CONFIG_SUFFIX);
-    this.bitbucketOAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + BitbucketOAuthService.CONFIG_SUFFIX);
-    this.casOAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + CasOAuthService.CONFIG_SUFFIX);
-    this.facebookOAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + FacebookOAuthService.CONFIG_SUFFIX);
-    this.gitlabOAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + GitLabOAuthService.CONFIG_SUFFIX);
-    this.lemonldapOAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + LemonLDAPOAuthService.CONFIG_SUFFIX);
-    this.dexOAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + DexOAuthService.CONFIG_SUFFIX);
-    this.keycloakOAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + KeycloakOAuthService.CONFIG_SUFFIX);
-    this.office365OAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + AzureActiveDirectoryService.CONFIG_SUFFIX_LEGACY);
+    this.sections = sections;
+    this.pluginName = pluginName;
+    this.googleOAuthProviderSection = getConfigSection(GoogleOAuthService.class);
+    this.githubOAuthProviderSection = getConfigSection(GitHubOAuthService.class);
+    this.bitbucketOAuthProviderSection = getConfigSection(BitbucketOAuthService.class);
+    this.casOAuthProviderSection = getConfigSection(CasOAuthService.class);
+    this.facebookOAuthProviderSection = getConfigSection(FacebookOAuthService.class);
+    this.gitlabOAuthProviderSection = getConfigSection(GitLabOAuthService.class);
+    this.lemonldapOAuthProviderSection = getConfigSection(LemonLDAPOAuthService.class);
+    this.dexOAuthProviderSection = getConfigSection(DexOAuthService.class);
+    this.keycloakOAuthProviderSection = getConfigSection(KeycloakOAuthService.class);
     this.azureActiveDirectoryAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + AzureActiveDirectoryService.CONFIG_SUFFIX);
-    this.airVantageOAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + AirVantageOAuthService.CONFIG_SUFFIX);
-    this.phabricatorOAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + PhabricatorOAuthService.CONFIG_SUFFIX);
-    this.tuleapOAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + TuleapOAuthService.CONFIG_SUFFIX);
-    this.auth0OAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + Auth0OAuthService.CONFIG_SUFFIX);
-    this.authentikOAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + AuthentikOAuthService.CONFIG_SUFFIX);
-    this.cognitoOAuthProviderSection =
-        sections.get(PLUGIN_SECTION, pluginName + CognitoOAuthService.CONFIG_SUFFIX);
+        getConfigSection(AzureActiveDirectoryService.class);
+    this.office365OAuthProviderSection =
+        getConfigSection(AzureActiveDirectoryService.LEGACY_PROVIDER_NAME);
+    this.airVantageOAuthProviderSection = getConfigSection(AirVantageOAuthService.class);
+    this.phabricatorOAuthProviderSection = getConfigSection(PhabricatorOAuthService.class);
+    this.tuleapOAuthProviderSection = getConfigSection(TuleapOAuthService.class);
+    this.auth0OAuthProviderSection = getConfigSection(Auth0OAuthService.class);
+    this.authentikOAuthProviderSection = getConfigSection(AuthentikOAuthService.class);
+    this.cognitoOAuthProviderSection = getConfigSection(CognitoOAuthService.class);
   }
 
   @Override
@@ -307,6 +297,17 @@
     }
   }
 
+  private Section getConfigSection(Class<? extends OAuthServiceProvider> serviceClass) {
+    String serviceProviderName =
+        serviceClass.getAnnotation(OAuthServiceProviderConfig.class).name();
+    return getConfigSection(serviceProviderName);
+  }
+
+  private Section getConfigSection(String serviceProviderName) {
+    String sectionName = pluginName + "-" + serviceProviderName + "-oauth";
+    return sections.get(PLUGIN_SECTION, sectionName);
+  }
+
   @Override
   public void postRun() throws Exception {}
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/Module.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/Module.java
index 72d59d1..683602d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/Module.java
@@ -14,24 +14,11 @@
 
 package com.googlesource.gerrit.plugins.oauth;
 
-import com.google.gerrit.extensions.annotations.Exports;
-import com.google.gerrit.extensions.annotations.PluginName;
-import com.google.gerrit.extensions.auth.oauth.OAuthLoginProvider;
 import com.google.inject.AbstractModule;
-import com.google.inject.Inject;
 
 public class Module extends AbstractModule {
-  private final String pluginName;
-
-  @Inject
-  Module(@PluginName String pluginName) {
-    this.pluginName = pluginName;
-  }
-
   @Override
   protected void configure() {
-    bind(OAuthLoginProvider.class)
-        .annotatedWith(Exports.named(pluginName))
-        .to(DisabledOAuthLoginProvider.class);
+    bind(OAuthPluginConfigFactory.class);
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuthPluginConfigFactory.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuthPluginConfigFactory.java
new file mode 100644
index 0000000..50f50d3
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuthPluginConfigFactory.java
@@ -0,0 +1,41 @@
+// Copyright (C) 2025 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.oauth;
+
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.server.config.PluginConfig;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+@Singleton
+public class OAuthPluginConfigFactory {
+  private final PluginConfigFactory cfgFactory;
+  private final String pluginName;
+
+  @Inject
+  OAuthPluginConfigFactory(PluginConfigFactory cfgFactory, @PluginName String pluginName) {
+    this.cfgFactory = cfgFactory;
+    this.pluginName = pluginName;
+  }
+
+  static String getConfigSuffix(String providerName) {
+    return String.format("-%s-oauth", providerName);
+  }
+
+  public PluginConfig create(String providerName) {
+    return cfgFactory.getFromGerritConfig(pluginName + getConfigSuffix(providerName));
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuthServiceModule.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuthServiceModule.java
new file mode 100644
index 0000000..78183db
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuthServiceModule.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2025 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.oauth;
+
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
+import com.google.gerrit.server.config.PluginConfig;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+
+public class OAuthServiceModule extends AbstractModule {
+  private final OAuthPluginConfigFactory cfgFactory;
+  private final Class<? extends OAuthServiceProvider> serviceProviderClass;
+  private final String serviceProviderName;
+
+  @Inject
+  public OAuthServiceModule(
+      OAuthPluginConfigFactory cfgFactory,
+      Class<? extends OAuthServiceProvider> serviceProviderClass) {
+    this.cfgFactory = cfgFactory;
+    this.serviceProviderClass = serviceProviderClass;
+
+    serviceProviderName =
+        serviceProviderClass.getAnnotation(OAuthServiceProviderConfig.class).name();
+  }
+
+  @Override
+  public void configure() {
+    PluginConfig cfg = cfgFactory.create(serviceProviderName);
+    if (cfg.getString(InitOAuth.CLIENT_ID) != null) {
+      bindOAuthServiceProvider();
+      configureAdditionalServiceComponents();
+    }
+  }
+
+  protected void bindOAuthServiceProvider() {
+    String extIdScheme = OAuthServiceProviderExternalIdScheme.create(serviceProviderName);
+    bind(OAuthServiceProvider.class)
+        .annotatedWith(Exports.named(extIdScheme))
+        .to(serviceProviderClass);
+  }
+
+  public void configureAdditionalServiceComponents() {}
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuthServiceProviderConfig.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuthServiceProviderConfig.java
new file mode 100644
index 0000000..488101f
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuthServiceProviderConfig.java
@@ -0,0 +1,23 @@
+// Copyright (C) 2025 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.oauth;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface OAuthServiceProviderConfig {
+  String name();
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuthServiceProviderExternalIdScheme.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuthServiceProviderExternalIdScheme.java
new file mode 100644
index 0000000..5c93b79
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuthServiceProviderExternalIdScheme.java
@@ -0,0 +1,21 @@
+// Copyright (C) 2025 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.oauth;
+
+public class OAuthServiceProviderExternalIdScheme {
+  public static String create(String serviceProviderName) {
+    return serviceProviderName + "-oauth";
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/airvantage/AirVantageOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/airvantage/AirVantageOAuthService.java
index 0ee981d..07fefde 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/airvantage/AirVantageOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/airvantage/AirVantageOAuthService.java
@@ -26,39 +26,39 @@
 import com.github.scribejava.core.model.Verb;
 import com.github.scribejava.core.oauth.OAuth20Service;
 import com.google.common.base.CharMatcher;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderExternalIdScheme;
 import java.io.IOException;
 import java.util.concurrent.ExecutionException;
 import org.slf4j.Logger;
 
 @Singleton
+@OAuthServiceProviderConfig(name = AirVantageOAuthService.PROVIDER_NAME)
 public class AirVantageOAuthService implements OAuthServiceProvider {
   private static final Logger log = getLogger(AirVantageOAuthService.class);
-  public static final String CONFIG_SUFFIX = "-airvantage-oauth";
-  private static final String AV_PROVIDER_PREFIX = "airvantage-oauth:";
+  public static final String PROVIDER_NAME = "airvantage";
   private static final String PROTECTED_RESOURCE_URL =
       "https://eu.airvantage.net/api/v1/users/current";
   private final OAuth20Service service;
+  private final String extIdScheme;
 
   @Inject
   AirVantageOAuthService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     String canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/";
 
     service =
@@ -66,6 +66,7 @@
             .apiSecret(cfg.getString(InitOAuth.CLIENT_SECRET))
             .callback(canonicalWebUrl + "oauth")
             .build(new AirVantageApi());
+    extIdScheme = OAuthServiceProviderExternalIdScheme.create(PROVIDER_NAME);
   }
 
   @Override
@@ -95,7 +96,7 @@
         JsonElement email = jsonObject.get("email");
         JsonElement name = jsonObject.get("name");
         return new OAuthUserInfo(
-            AV_PROVIDER_PREFIX + id.getAsString(),
+            extIdScheme + ":" + id.getAsString(),
             null,
             email.getAsString(),
             name.getAsString(),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/auth0/Auth0OAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/auth0/Auth0OAuthService.java
index 7133988..ac1934f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/auth0/Auth0OAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/auth0/Auth0OAuthService.java
@@ -25,20 +25,22 @@
 import com.github.scribejava.core.model.Verb;
 import com.github.scribejava.core.oauth.OAuth20Service;
 import com.google.common.base.CharMatcher;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.ProvisionException;
+import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderExternalIdScheme;
 import java.io.IOException;
 import java.net.URI;
 import java.util.concurrent.ExecutionException;
@@ -46,21 +48,21 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
+@OAuthServiceProviderConfig(name = Auth0OAuthService.PROVIDER_NAME)
 public class Auth0OAuthService implements OAuthServiceProvider {
   private static final Logger log = LoggerFactory.getLogger(Auth0OAuthService.class);
-  private static final String AUTH0_PROVIDER_PREFIX = "auth0-oauth:";
-  public static final String CONFIG_SUFFIX = "-auth0-oauth";
+  public static final String PROVIDER_NAME = "auth0";
   private static final String PROTECTED_RESOURCE_URL = "%s/userinfo";
   private final OAuth20Service service;
   private final String serviceName;
   private final String rootUrl;
+  private final String extIdScheme;
 
   @Inject
   Auth0OAuthService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     String canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/";
 
     rootUrl = cfg.getString(InitOAuth.ROOT_URL);
@@ -75,6 +77,7 @@
             .callback(canonicalWebUrl + "oauth")
             .defaultScope("openid profile email")
             .build(new Auth0Api(rootUrl));
+    extIdScheme = OAuthServiceProviderExternalIdScheme.create(PROVIDER_NAME);
   }
 
   @Override
@@ -104,7 +107,7 @@
       JsonElement email = jsonObject.get("email");
       JsonElement name = jsonObject.get("name");
       return new OAuthUserInfo(
-          AUTH0_PROVIDER_PREFIX + id.getAsString(),
+          extIdScheme + ":" + id.getAsString(),
           asString(username),
           asString(email),
           asString(name),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/authentik/AuthentikOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/authentik/AuthentikOAuthService.java
index f0e57a9..fd1be49 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/authentik/AuthentikOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/authentik/AuthentikOAuthService.java
@@ -25,20 +25,22 @@
 import com.github.scribejava.core.model.Verb;
 import com.github.scribejava.core.oauth.OAuth20Service;
 import com.google.common.base.CharMatcher;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.ProvisionException;
+import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderExternalIdScheme;
 import java.io.IOException;
 import java.net.URI;
 import java.util.concurrent.ExecutionException;
@@ -46,22 +48,22 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
+@OAuthServiceProviderConfig(name = AuthentikOAuthService.PROVIDER_NAME)
 public class AuthentikOAuthService implements OAuthServiceProvider {
   private static final Logger log = LoggerFactory.getLogger(AuthentikOAuthService.class);
-  private static final String AUTHENTIK_PROVIDER_PREFIX = "authentik-oauth:";
-  public static final String CONFIG_SUFFIX = "-authentik-oauth";
+  public static final String PROVIDER_NAME = "authentik";
   private static final String PROTECTED_RESOURCE_URL = "%s/application/o/userinfo/";
   private final OAuth20Service service;
   private final String serviceName;
   private final String rootUrl;
   private final boolean linkExistingGerrit;
+  private final String extIdScheme;
 
   @Inject
   AuthentikOAuthService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     String canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/";
 
     rootUrl = cfg.getString(InitOAuth.ROOT_URL);
@@ -77,6 +79,7 @@
             .callback(canonicalWebUrl + "oauth")
             .defaultScope("openid profile email")
             .build(new AuthentikApi(rootUrl));
+    extIdScheme = OAuthServiceProviderExternalIdScheme.create(PROVIDER_NAME);
   }
 
   @Override
@@ -106,7 +109,7 @@
       JsonElement email = jsonObject.get("email");
       JsonElement name = jsonObject.get("name");
       return new OAuthUserInfo(
-          AUTHENTIK_PROVIDER_PREFIX + id.getAsString(),
+          extIdScheme + ":" + id.getAsString(),
           asString(username),
           asString(email),
           asString(name),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/azure/AzureActiveDirectoryService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/azure/AzureActiveDirectoryService.java
index 5fa1f27..6f89662 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/azure/AzureActiveDirectoryService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/azure/AzureActiveDirectoryService.java
@@ -28,14 +28,12 @@
 import com.github.scribejava.core.oauth.OAuth20Service;
 import com.google.common.base.CharMatcher;
 import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.Gson;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
@@ -43,6 +41,9 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderExternalIdScheme;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.Base64;
@@ -52,12 +53,12 @@
 import org.slf4j.LoggerFactory;
 
 @Singleton
+@OAuthServiceProviderConfig(name = AzureActiveDirectoryService.PROVIDER_NAME)
 public class AzureActiveDirectoryService implements OAuthServiceProvider {
   private static final Logger log = LoggerFactory.getLogger(AzureActiveDirectoryService.class);
-  public static final String CONFIG_SUFFIX_LEGACY = "-office365-oauth";
-  public static final String CONFIG_SUFFIX = "-azure-oauth";
-  private static final String AZURE_PROVIDER_PREFIX = "azure-oauth:";
   private static final String OFFICE365_PROVIDER_PREFIX = "office365-oauth:";
+  public static final String PROVIDER_NAME = "azure";
+  public static final String LEGACY_PROVIDER_NAME = "office365";
   private static final String PROTECTED_RESOURCE_URL = "https://graph.microsoft.com/v1.0/me";
   private static final String SCOPE =
       "openid offline_access https://graph.microsoft.com/user.readbasic.all";
@@ -70,26 +71,19 @@
   private final boolean useEmailAsUsername;
   private final String tenant;
   private final String clientId;
-  private String providerPrefix;
   private final boolean linkOffice365Id;
+  private final String extIdScheme;
 
   @Inject
   AzureActiveDirectoryService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
-    providerPrefix = AZURE_PROVIDER_PREFIX;
-
-    // ?: Did we find the client_id with the CONFIG_SUFFIX
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     if (cfg.getString(InitOAuth.CLIENT_ID) == null) {
-      // -> No, we did not find the client_id in the azure config so we should try the old legacy
-      // office365 section
-      cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX_LEGACY);
-      // We must also use the new provider prefix
-      providerPrefix = OFFICE365_PROVIDER_PREFIX;
+      cfg = cfgFactory.create(LEGACY_PROVIDER_NAME);
+      extIdScheme = OAuthServiceProviderExternalIdScheme.create(LEGACY_PROVIDER_NAME);
+    } else {
+      extIdScheme = OAuthServiceProviderExternalIdScheme.create(PROVIDER_NAME);
     }
-    this.linkOffice365Id = cfg.getBoolean(InitOAuth.LINK_TO_EXISTING_OFFICE365_ACCOUNT, false);
     this.canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/";
     this.useEmailAsUsername = cfg.getBoolean(InitOAuth.USE_EMAIL_AS_USERNAME, false);
     this.tenant = cfg.getString(InitOAuth.TENANT, DEFAULT_TENANT);
@@ -106,6 +100,7 @@
       log.debug("OAuth2: scope={}", SCOPE);
       log.debug("OAuth2: useEmailAsUsername={}", useEmailAsUsername);
     }
+    this.linkOffice365Id = cfg.getBoolean(InitOAuth.LINK_TO_EXISTING_OFFICE365_ACCOUNT, false);
   }
 
   @Override
@@ -187,7 +182,7 @@
         }
 
         return new OAuthUserInfo(
-            providerPrefix + id.getAsString(),
+            extIdScheme + ":" + id.getAsString(),
             login,
             asString(email),
             asString(name),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/bitbucket/BitbucketOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/bitbucket/BitbucketOAuthService.java
index d766e33..6ec4577 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/bitbucket/BitbucketOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/bitbucket/BitbucketOAuthService.java
@@ -26,40 +26,39 @@
 import com.github.scribejava.core.model.Verb;
 import com.github.scribejava.core.oauth.OAuth20Service;
 import com.google.common.base.CharMatcher;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderExternalIdScheme;
 import java.io.IOException;
 import java.util.concurrent.ExecutionException;
 import org.slf4j.Logger;
 
 @Singleton
+@OAuthServiceProviderConfig(name = BitbucketOAuthService.PROVIDER_NAME)
 public class BitbucketOAuthService implements OAuthServiceProvider {
   private static final Logger log = getLogger(BitbucketOAuthService.class);
-  public static final String CONFIG_SUFFIX = "-bitbucket-oauth";
-  private static final String BITBUCKET_PROVIDER_PREFIX = "bitbucket-oauth:";
+  public static final String PROVIDER_NAME = "bitbucket";
   private static final String PROTECTED_RESOURCE_URL = "https://bitbucket.org/api/1.0/user/";
   private final boolean fixLegacyUserId;
   private final OAuth20Service service;
+  private final String extIdScheme;
 
   @Inject
   BitbucketOAuthService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
-
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     String canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/";
     fixLegacyUserId = cfg.getBoolean(InitOAuth.FIX_LEGACY_USER_ID, false);
     service =
@@ -67,6 +66,7 @@
             .apiSecret(cfg.getString(InitOAuth.CLIENT_SECRET))
             .callback(canonicalWebUrl + "oauth")
             .build(new BitbucketApi());
+    extIdScheme = OAuthServiceProviderExternalIdScheme.create(PROVIDER_NAME);
   }
 
   @Override
@@ -99,7 +99,7 @@
 
         JsonElement displayName = jsonObject.get("display_name");
         return new OAuthUserInfo(
-            BITBUCKET_PROVIDER_PREFIX + username,
+            extIdScheme + ":" + username,
             username,
             null,
             displayName == null || displayName.isJsonNull() ? null : displayName.getAsString(),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/cas/CasOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/cas/CasOAuthService.java
index 1be982f..25f5e90 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/cas/CasOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/cas/CasOAuthService.java
@@ -25,14 +25,12 @@
 import com.github.scribejava.core.model.Verb;
 import com.github.scribejava.core.oauth.OAuth20Service;
 import com.google.common.base.CharMatcher;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
@@ -41,6 +39,9 @@
 import com.google.inject.ProvisionException;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderExternalIdScheme;
 import java.io.IOException;
 import java.net.URI;
 import java.util.concurrent.ExecutionException;
@@ -49,23 +50,22 @@
 import org.slf4j.LoggerFactory;
 
 @Singleton
+@OAuthServiceProviderConfig(name = CasOAuthService.PROVIDER_NAME)
 public class CasOAuthService implements OAuthServiceProvider {
   private static final Logger log = LoggerFactory.getLogger(CasOAuthService.class);
-  public static final String CONFIG_SUFFIX = "-cas-oauth";
-  private static final String CAS_PROVIDER_PREFIX = "cas-oauth:";
+  public static final String PROVIDER_NAME = "cas";
   private static final String PROTECTED_RESOURCE_URL = "%s/oauth2.0/profile";
   private static final String USE_JSON_EXTRACTOR = "use-json-extractor";
 
   private final String rootUrl;
   private final boolean fixLegacyUserId;
   private final OAuth20Service service;
+  private final String extIdScheme;
 
   @Inject
   CasOAuthService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     rootUrl = cfg.getString(InitOAuth.ROOT_URL);
     if (!URI.create(rootUrl).isAbsolute()) {
       throw new ProvisionException("Root URL must be absolute URL");
@@ -78,6 +78,7 @@
             .apiSecret(cfg.getString(InitOAuth.CLIENT_SECRET))
             .callback(canonicalWebUrl + "oauth")
             .build(new CasApi(rootUrl, useJsonExtractor));
+    extIdScheme = OAuthServiceProviderExternalIdScheme.create(PROVIDER_NAME);
   }
 
   @Override
@@ -148,7 +149,7 @@
       }
 
       return new OAuthUserInfo(
-          CAS_PROVIDER_PREFIX + id.getAsString(),
+          extIdScheme + ":" + id.getAsString(),
           login,
           email,
           name,
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/cognito/CognitoOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/cognito/CognitoOAuthService.java
index 77cb974..612a822 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/cognito/CognitoOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/cognito/CognitoOAuthService.java
@@ -25,20 +25,22 @@
 import com.github.scribejava.core.model.Verb;
 import com.github.scribejava.core.oauth.OAuth20Service;
 import com.google.common.base.CharMatcher;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.ProvisionException;
+import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderExternalIdScheme;
 import com.googlesource.gerrit.plugins.oauth.github.GitHubOAuthService;
 import java.io.IOException;
 import java.net.URI;
@@ -47,22 +49,22 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
+@OAuthServiceProviderConfig(name = CognitoOAuthService.PROVIDER_NAME)
 public class CognitoOAuthService implements OAuthServiceProvider {
   private static final Logger log = LoggerFactory.getLogger(GitHubOAuthService.class);
-  public static final String CONFIG_SUFFIX = "-cognito-oauth";
-  private static final String COGNITO_PROVIDER_PREFIX = "cognito-oauth:";
+  public static final String PROVIDER_NAME = "cognito";
   private static final String PROTECTED_RESOURCE_URL = "%s/oauth2/userInfo";
   private final String rootUrl;
   private final OAuth20Service service;
   private final String serviceName;
   private final boolean linkExistingGerrit;
+  private final String extIdScheme;
 
   @Inject
   CognitoOAuthService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     String canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/";
 
     rootUrl = cfg.getString(InitOAuth.ROOT_URL);
@@ -79,6 +81,7 @@
             .callback(canonicalWebUrl + "oauth")
             .defaultScope("openid profile email")
             .build(new CognitoApi(rootUrl));
+    extIdScheme = OAuthServiceProviderExternalIdScheme.create(PROVIDER_NAME);
   }
 
   @Override
@@ -110,7 +113,7 @@
       JsonElement email = jsonObject.get("email");
       JsonElement name = jsonObject.get("name");
       return new OAuthUserInfo(
-          COGNITO_PROVIDER_PREFIX + id.getAsString(),
+          extIdScheme + ":" + id.getAsString(),
           asString(username),
           asString(email),
           asString(name),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/dex/DexOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/dex/DexOAuthService.java
index 03c8a07..4f8e7c8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/dex/DexOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/dex/DexOAuthService.java
@@ -22,14 +22,12 @@
 import com.github.scribejava.core.oauth.OAuth20Service;
 import com.google.common.base.CharMatcher;
 import com.google.common.base.Preconditions;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Inject;
@@ -37,6 +35,9 @@
 import com.google.inject.ProvisionException;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderExternalIdScheme;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
@@ -47,22 +48,21 @@
 import org.slf4j.LoggerFactory;
 
 @Singleton
+@OAuthServiceProviderConfig(name = DexOAuthService.PROVIDER_NAME)
 public class DexOAuthService implements OAuthServiceProvider {
   private static final Logger log = LoggerFactory.getLogger(DexOAuthService.class);
+  public static final String PROVIDER_NAME = "dex";
 
-  public static final String CONFIG_SUFFIX = "-dex-oauth";
-  private static final String DEX_PROVIDER_PREFIX = "dex-oauth:";
   private final OAuth20Service service;
   private final String rootUrl;
   private final String domain;
   private final String serviceName;
+  private final String extIdScheme;
 
   @Inject
   DexOAuthService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     String canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/";
 
     rootUrl = cfg.getString(InitOAuth.ROOT_URL);
@@ -78,6 +78,7 @@
             .defaultScope("openid profile email offline_access")
             .callback(canonicalWebUrl + "oauth")
             .build(new DexApi(rootUrl));
+    extIdScheme = OAuthServiceProviderExternalIdScheme.create(PROVIDER_NAME);
   }
 
   private String parseJwt(String input) throws UnsupportedEncodingException {
@@ -125,7 +126,7 @@
     }
 
     return new OAuthUserInfo(
-        DEX_PROVIDER_PREFIX + email /*externalId*/,
+        extIdScheme + ":" + email /*externalId*/,
         username /*username*/,
         email /*email*/,
         name /*displayName*/,
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/facebook/FacebookOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/facebook/FacebookOAuthService.java
index dfb56d6..5888369 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/facebook/FacebookOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/facebook/FacebookOAuthService.java
@@ -25,20 +25,21 @@
 import com.github.scribejava.core.model.Verb;
 import com.github.scribejava.core.oauth.OAuth20Service;
 import com.google.common.base.CharMatcher;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderExternalIdScheme;
 import java.io.IOException;
 import java.util.concurrent.ExecutionException;
 import javax.servlet.http.HttpServletResponse;
@@ -46,24 +47,21 @@
 import org.slf4j.LoggerFactory;
 
 @Singleton
+@OAuthServiceProviderConfig(name = FacebookOAuthService.PROVIDER_NAME)
 public class FacebookOAuthService implements OAuthServiceProvider {
   private static final Logger log = LoggerFactory.getLogger(FacebookOAuthService.class);
-  public static final String CONFIG_SUFFIX = "-facebook-oauth";
   private static final String PROTECTED_RESOURCE_URL = "https://graph.facebook.com/me";
-
-  private static final String FACEBOOK_PROVIDER_PREFIX = "facebook-oauth:";
+  public static final String PROVIDER_NAME = "facebook";
   private static final String SCOPE = "email";
   private static final String FIELDS_QUERY = "fields";
   private static final String FIELDS = "email,name";
   private final OAuth20Service service;
+  private final String extIdScheme;
 
   @Inject
   FacebookOAuthService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     String canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/";
 
     service =
@@ -72,6 +70,7 @@
             .callback(canonicalWebUrl + "oauth")
             .defaultScope(SCOPE)
             .build(new Facebook2Api());
+    extIdScheme = OAuthServiceProviderExternalIdScheme.create(PROVIDER_NAME);
   }
 
   @Override
@@ -108,7 +107,7 @@
         JsonElement login = jsonObject.get("email");
 
         return new OAuthUserInfo(
-            FACEBOOK_PROVIDER_PREFIX + id.getAsString(),
+            extIdScheme + ":" + id.getAsString(),
             asString(login),
             asString(email),
             asString(name),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/github/GitHubOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/github/GitHubOAuthService.java
index 2ccf49b..b72a93f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/github/GitHubOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/github/GitHubOAuthService.java
@@ -25,20 +25,21 @@
 import com.github.scribejava.core.model.Verb;
 import com.github.scribejava.core.oauth.OAuth20Service;
 import com.google.common.base.CharMatcher;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderExternalIdScheme;
 import java.io.IOException;
 import java.util.concurrent.ExecutionException;
 import javax.servlet.http.HttpServletResponse;
@@ -46,10 +47,10 @@
 import org.slf4j.LoggerFactory;
 
 @Singleton
+@OAuthServiceProviderConfig(name = GitHubOAuthService.PROVIDER_NAME)
 public class GitHubOAuthService implements OAuthServiceProvider {
   private static final Logger log = LoggerFactory.getLogger(GitHubOAuthService.class);
-  public static final String CONFIG_SUFFIX = "-github-oauth";
-  private static final String GITHUB_PROVIDER_PREFIX = "github-oauth:";
+  public static final String PROVIDER_NAME = "github";
   private static final String GITHUB_API_ENDPOINT_URL = "https://api.github.com/";
   private static final String GHE_API_ENDPOINT_URL = "%sapi/v3/";
   static final String GITHUB_ROOT_URL = "https://github.com/";
@@ -58,13 +59,12 @@
   static final String SCOPE = "user:email";
   private final boolean fixLegacyUserId;
   private final OAuth20Service service;
+  private final String extIdScheme;
 
   @Inject
   GitHubOAuthService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     String canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/";
     fixLegacyUserId = cfg.getBoolean(InitOAuth.FIX_LEGACY_USER_ID, false);
     rootUrl =
@@ -77,6 +77,7 @@
             .callback(canonicalWebUrl + "oauth")
             .defaultScope(SCOPE)
             .build(new GitHub2Api(rootUrl));
+    extIdScheme = OAuthServiceProviderExternalIdScheme.create(PROVIDER_NAME);
   }
 
   private String getApiUrl() {
@@ -117,7 +118,7 @@
         JsonElement name = jsonObject.get("name");
         JsonElement login = jsonObject.get("login");
         return new OAuthUserInfo(
-            GITHUB_PROVIDER_PREFIX + id.getAsString(),
+            extIdScheme + ":" + id.getAsString(),
             asString(login),
             asString(email),
             asString(name),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/gitlab/GitLabOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/gitlab/GitLabOAuthService.java
index 6174019..426b06f 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/gitlab/GitLabOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/gitlab/GitLabOAuthService.java
@@ -27,14 +27,12 @@
 import com.github.scribejava.core.model.Verb;
 import com.github.scribejava.core.oauth.OAuth20Service;
 import com.google.common.base.CharMatcher;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Inject;
@@ -42,26 +40,28 @@
 import com.google.inject.ProvisionException;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderExternalIdScheme;
 import java.io.IOException;
 import java.net.URI;
 import java.util.concurrent.ExecutionException;
 import org.slf4j.Logger;
 
 @Singleton
+@OAuthServiceProviderConfig(name = GitLabOAuthService.PROVIDER_NAME)
 public class GitLabOAuthService implements OAuthServiceProvider {
   private static final Logger log = getLogger(GitLabOAuthService.class);
-  public static final String CONFIG_SUFFIX = "-gitlab-oauth";
   private static final String PROTECTED_RESOURCE_URL = "%s/api/v3/user";
-  private static final String GITLAB_PROVIDER_PREFIX = "gitlab-oauth:";
+  public static final String PROVIDER_NAME = "gitlab";
   private final OAuth20Service service;
   private final String rootUrl;
+  private final String extIdScheme;
 
   @Inject
   GitLabOAuthService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     String canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/";
     rootUrl = cfg.getString(InitOAuth.ROOT_URL);
     if (!URI.create(rootUrl).isAbsolute()) {
@@ -72,6 +72,7 @@
             .apiSecret(cfg.getString(InitOAuth.CLIENT_SECRET))
             .callback(canonicalWebUrl + "oauth")
             .build(new GitLabApi(rootUrl));
+    extIdScheme = OAuthServiceProviderExternalIdScheme.create(PROVIDER_NAME);
   }
 
   @Override
@@ -101,7 +102,7 @@
       JsonElement email = jsonObject.get("email");
       JsonElement name = jsonObject.get("name");
       return new OAuthUserInfo(
-          GITLAB_PROVIDER_PREFIX + id.getAsString(),
+          extIdScheme + ":" + id.getAsString(),
           asString(username),
           asString(email),
           asString(name),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/google/GoogleOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/google/GoogleOAuthService.java
index 01851cb..2daee21 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/google/GoogleOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/google/GoogleOAuthService.java
@@ -27,20 +27,21 @@
 import com.google.common.base.CharMatcher;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderExternalIdScheme;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
@@ -54,10 +55,10 @@
 import org.slf4j.LoggerFactory;
 
 @Singleton
+@OAuthServiceProviderConfig(name = GoogleOAuthService.PROVIDER_NAME)
 public class GoogleOAuthService implements OAuthServiceProvider {
   private static final Logger log = LoggerFactory.getLogger(GoogleOAuthService.class);
-  public static final String CONFIG_SUFFIX = "-google-oauth";
-  private static final String GOOGLE_PROVIDER_PREFIX = "google-oauth:";
+  public static final String PROVIDER_NAME = "google";
   private static final String PROTECTED_RESOURCE_URL =
       "https://www.googleapis.com/oauth2/v2/userinfo";
   private static final String SCOPE = "email profile";
@@ -66,13 +67,12 @@
   private final List<String> domains;
   private final boolean useEmailAsUsername;
   private final boolean fixLegacyUserId;
+  private final String extIdScheme;
 
   @Inject
   GoogleOAuthService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     this.canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/";
     if (cfg.getBoolean(InitOAuth.LINK_TO_EXISTING_OPENID_ACCOUNT, false)) {
       log.warn(
@@ -94,6 +94,7 @@
       log.debug("OAuth2: domains={}", domains);
       log.debug("OAuth2: useEmailAsUsername={}", useEmailAsUsername);
     }
+    extIdScheme = OAuthServiceProviderExternalIdScheme.create(PROVIDER_NAME);
   }
 
   @Override
@@ -144,7 +145,7 @@
           login = email.getAsString().split("@")[0];
         }
         return new OAuthUserInfo(
-            GOOGLE_PROVIDER_PREFIX + id.getAsString(),
+            extIdScheme + ":" + id.getAsString(),
             login,
             asString(email),
             asString(name),
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/keycloak/KeycloakOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/keycloak/KeycloakOAuthService.java
index 352a1b6..2a06f06 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/keycloak/KeycloakOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/keycloak/KeycloakOAuthService.java
@@ -22,20 +22,22 @@
 import com.github.scribejava.core.oauth.OAuth20Service;
 import com.google.common.base.CharMatcher;
 import com.google.common.base.Preconditions;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.ProvisionException;
+import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderExternalIdScheme;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
@@ -45,22 +47,22 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
+@OAuthServiceProviderConfig(name = KeycloakOAuthService.PROVIDER_NAME)
 public class KeycloakOAuthService implements OAuthServiceProvider {
 
   private static final Logger log = LoggerFactory.getLogger(KeycloakOAuthService.class);
+  public static final String PROVIDER_NAME = "keycloak";
 
-  public static final String CONFIG_SUFFIX = "-keycloak-oauth";
-  private static final String KEYCLOAK_PROVIDER_PREFIX = "keycloak-oauth:";
   private final OAuth20Service service;
   private final String serviceName;
   private final boolean usePreferredUsername;
+  private final String extIdScheme;
 
   @Inject
   KeycloakOAuthService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     String canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/";
 
     String rootUrl = cfg.getString(InitOAuth.ROOT_URL);
@@ -77,6 +79,7 @@
             .callback(canonicalWebUrl + "oauth")
             .defaultScope("openid")
             .build(new KeycloakApi(rootUrl, realm));
+    extIdScheme = OAuthServiceProviderExternalIdScheme.create(PROVIDER_NAME);
   }
 
   private String parseJwt(String input) throws UnsupportedEncodingException {
@@ -124,7 +127,7 @@
     if (usePreferredUsername) {
       username = usernameAsString;
     }
-    String externalId = KEYCLOAK_PROVIDER_PREFIX + usernameAsString;
+    String externalId = extIdScheme + ":" + usernameAsString;
     String email = emailElement.getAsString();
     String name = nameElement.getAsString();
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/lemon/LemonLDAPOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/lemon/LemonLDAPOAuthService.java
index cdaa090..ea3fbd8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/lemon/LemonLDAPOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/lemon/LemonLDAPOAuthService.java
@@ -26,29 +26,30 @@
 import com.github.scribejava.core.model.Verb;
 import com.github.scribejava.core.oauth.OAuth20Service;
 import com.google.common.base.CharMatcher;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
 import java.io.IOException;
 import java.util.concurrent.ExecutionException;
 import javax.servlet.http.HttpServletResponse;
 import org.slf4j.Logger;
 
 @Singleton
+@OAuthServiceProviderConfig(name = LemonLDAPOAuthService.PROVIDER_NAME)
 public class LemonLDAPOAuthService implements OAuthServiceProvider {
   private static final Logger log = getLogger(LemonLDAPOAuthService.class);
-  public static final String CONFIG_SUFFIX = "-lemonldap-oauth";
+  public static final String PROVIDER_NAME = "lemonldap";
   private static final String PROTECTED_RESOURCE_URL = "%s/oauth2/userinfo";
   private static final String LEMONLDAP_PROVIDER_PREFIX = "llng-oauth:";
   private final OAuth20Service service;
@@ -56,10 +57,8 @@
 
   @Inject
   LemonLDAPOAuthService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     String canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/";
     rootUrl = cfg.getString(InitOAuth.ROOT_URL);
     service =
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/phabricator/PhabricatorOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/phabricator/PhabricatorOAuthService.java
index 05cd721..7a67fc7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/phabricator/PhabricatorOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/phabricator/PhabricatorOAuthService.java
@@ -25,14 +25,12 @@
 import com.github.scribejava.core.model.Verb;
 import com.github.scribejava.core.oauth.OAuth20Service;
 import com.google.common.base.CharMatcher;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Inject;
@@ -40,6 +38,9 @@
 import com.google.inject.ProvisionException;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderExternalIdScheme;
 import java.io.IOException;
 import java.net.URI;
 import java.util.concurrent.ExecutionException;
@@ -48,20 +49,19 @@
 import org.slf4j.LoggerFactory;
 
 @Singleton
+@OAuthServiceProviderConfig(name = PhabricatorOAuthService.PROVIDER_NAME)
 public class PhabricatorOAuthService implements OAuthServiceProvider {
   private static final Logger log = LoggerFactory.getLogger(PhabricatorOAuthService.class);
-  public static final String CONFIG_SUFFIX = "-phabricator-oauth";
-  private static final String PHABRICATOR_PROVIDER_PREFIX = "phabricator-oauth:";
+  public static final String PROVIDER_NAME = "phabricator";
   private static final String PROTECTED_RESOURCE_URL = "%s/api/user.whoami";
   private final String rootUrl;
   private final OAuth20Service service;
+  private final String extIdScheme;
 
   @Inject
   PhabricatorOAuthService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     String canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/";
     rootUrl = cfg.getString(InitOAuth.ROOT_URL);
     if (!URI.create(rootUrl).isAbsolute()) {
@@ -75,6 +75,7 @@
     if (log.isDebugEnabled()) {
       log.debug("OAuth2: canonicalWebUrl={}", canonicalWebUrl);
     }
+    extIdScheme = OAuthServiceProviderExternalIdScheme.create(PROVIDER_NAME);
   }
 
   @Override
@@ -116,11 +117,7 @@
           login = username.getAsString();
         }
         return new OAuthUserInfo(
-            PHABRICATOR_PROVIDER_PREFIX + id.getAsString(),
-            login,
-            asString(email),
-            asString(name),
-            null);
+            extIdScheme + ":" + id.getAsString(), login, asString(email), asString(name), null);
       }
     } catch (ExecutionException | InterruptedException e) {
       throw new RuntimeException("Cannot retrieve user info resource", e);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/tuleap/TuleapOAuthService.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/tuleap/TuleapOAuthService.java
index c7423d8..b24aac8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/tuleap/TuleapOAuthService.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/tuleap/TuleapOAuthService.java
@@ -25,20 +25,22 @@
 import com.github.scribejava.core.model.Verb;
 import com.github.scribejava.core.oauth.OAuth20Service;
 import com.google.common.base.CharMatcher;
-import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.ProvisionException;
+import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderConfig;
+import com.googlesource.gerrit.plugins.oauth.OAuthServiceProviderExternalIdScheme;
 import java.io.IOException;
 import java.net.URI;
 import java.util.concurrent.ExecutionException;
@@ -46,21 +48,21 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
+@OAuthServiceProviderConfig(name = TuleapOAuthService.PROVIDER_NAME)
 public class TuleapOAuthService implements OAuthServiceProvider {
   private static final Logger log = LoggerFactory.getLogger(TuleapOAuthService.class);
-  private static final String TULEAP_PROVIDER_PREFIX = "tuleap-oauth:";
-  public static final String CONFIG_SUFFIX = "-tuleap-oauth";
+  public static final String PROVIDER_NAME = "tuleap";
   private static final String PROTECTED_RESOURCE_URL = "%s/oauth2/userinfo";
   private final OAuth20Service service;
   private final String serviceName;
   private final String rootUrl;
+  private final String extIdScheme;
 
   @Inject
   TuleapOAuthService(
-      PluginConfigFactory cfgFactory,
-      @PluginName String pluginName,
-      @CanonicalWebUrl Provider<String> urlProvider) {
-    PluginConfig cfg = cfgFactory.getFromGerritConfig(pluginName + CONFIG_SUFFIX);
+      OAuthPluginConfigFactory cfgFactory, @CanonicalWebUrl Provider<String> urlProvider) {
+    PluginConfig cfg = cfgFactory.create(PROVIDER_NAME);
     String canonicalWebUrl = CharMatcher.is('/').trimTrailingFrom(urlProvider.get()) + "/";
 
     rootUrl = cfg.getString(InitOAuth.ROOT_URL);
@@ -75,6 +77,7 @@
             .callback(canonicalWebUrl + "oauth")
             .defaultScope("openid profile email")
             .build(new TuleapApi(rootUrl));
+    extIdScheme = OAuthServiceProviderExternalIdScheme.create(PROVIDER_NAME);
   }
 
   @Override
@@ -104,7 +107,7 @@
       JsonElement email = jsonObject.get("email");
       JsonElement name = jsonObject.get("name");
       return new OAuthUserInfo(
-          TULEAP_PROVIDER_PREFIX + id.getAsString(),
+          extIdScheme + ":" + id.getAsString(),
           asString(username),
           asString(email),
           asString(name),
diff --git a/src/test/java/com/googlesource/gerrit/plugins/oauth/cognito/CognitoOAuthServiceTest.java b/src/test/java/com/googlesource/gerrit/plugins/oauth/cognito/CognitoOAuthServiceTest.java
index d2b6edf..60ee64b 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/oauth/cognito/CognitoOAuthServiceTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/oauth/cognito/CognitoOAuthServiceTest.java
@@ -25,9 +25,9 @@
 import com.google.gerrit.extensions.auth.oauth.OAuthToken;
 import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.inject.Provider;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
 import java.lang.reflect.Field;
 import javax.servlet.http.HttpServletResponse;
 import org.junit.Before;
@@ -40,7 +40,7 @@
 public class CognitoOAuthServiceTest {
 
   // Mocks for constructor dependencies of CognitoOAuthService
-  @Mock private PluginConfigFactory mockConfigFactory;
+  @Mock private OAuthPluginConfigFactory mockConfigFactory;
   @Mock private PluginConfig mockPluginConfig;
   @Mock private Provider<String> mockUrlProvider;
 
@@ -48,7 +48,6 @@
   @Mock private OAuth20Service mockScribeOAuthService;
 
   // Constants for configuration values
-  private static final String TEST_PLUGIN_NAME = "gerrit-oauth-provider-cognito";
   private static final String TEST_CANONICAL_WEB_URL = "http://gerrit.example.com";
   private static final String TEST_COGNITO_ROOT_URL =
       "https://cognito-idp.us-east-1.amazonaws.com/USER_POOL_ID";
@@ -68,9 +67,7 @@
   @Before
   public void setUp() throws Exception {
     // Mock the PluginConfigFactory to return our mockPluginConfig
-    when(mockConfigFactory.getFromGerritConfig(
-            TEST_PLUGIN_NAME + CognitoOAuthService.CONFIG_SUFFIX))
-        .thenReturn(mockPluginConfig);
+    when(mockConfigFactory.create(CognitoOAuthService.PROVIDER_NAME)).thenReturn(mockPluginConfig);
 
     // Mock the CanonicalWebUrl provider
     when(mockUrlProvider.get()).thenReturn(TEST_CANONICAL_WEB_URL);
@@ -99,7 +96,7 @@
         .thenReturn(linkExistingGerritAccounts);
 
     CognitoOAuthService serviceInstance =
-        new CognitoOAuthService(mockConfigFactory, TEST_PLUGIN_NAME, mockUrlProvider);
+        new CognitoOAuthService(mockConfigFactory, mockUrlProvider);
 
     // Replace the internal OAuth20Service with our mock using reflection.
     Field serviceField = CognitoOAuthService.class.getDeclaredField("service");
diff --git a/src/test/java/com/googlesource/gerrit/plugins/oauth/github/GithubApiUrlTest.java b/src/test/java/com/googlesource/gerrit/plugins/oauth/github/GithubApiUrlTest.java
index 142e27c..73cbce1 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/oauth/github/GithubApiUrlTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/oauth/github/GithubApiUrlTest.java
@@ -21,9 +21,9 @@
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
 import com.google.gerrit.server.config.PluginConfig;
-import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.inject.Provider;
 import com.googlesource.gerrit.plugins.oauth.InitOAuth;
+import com.googlesource.gerrit.plugins.oauth.OAuthPluginConfigFactory;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import org.eclipse.jgit.lib.Config;
@@ -38,23 +38,21 @@
   private static final String CANONICAL_URL = "https://localhost";
   private static final String TEST_CLIENT_ID = "test_client_id";
 
-  @Mock private PluginConfigFactory pluginConfigFactoryMock;
   @Mock private Provider<String> urlProviderMock;
+  @Mock OAuthPluginConfigFactory oauthPluginConfigFactoryMock;
 
   private OAuthServiceProvider getGithubOAuthProvider(String rootUrl) {
-    PluginConfig.Update pluginConfig =
-        PluginConfig.Update.forTest(PLUGIN_NAME + GitHubOAuthService.CONFIG_SUFFIX, new Config());
+    String configSection = PLUGIN_NAME + "-" + GitHubOAuthService.PROVIDER_NAME + "-oauth";
+    PluginConfig.Update pluginConfig = PluginConfig.Update.forTest(configSection, new Config());
     if (!Strings.isNullOrEmpty(rootUrl)) {
       pluginConfig.setString(InitOAuth.ROOT_URL, rootUrl);
     }
     pluginConfig.setString(InitOAuth.CLIENT_ID, TEST_CLIENT_ID);
     pluginConfig.setString(InitOAuth.CLIENT_SECRET, "secret");
-    when(pluginConfigFactoryMock.getFromGerritConfig(
-            PLUGIN_NAME + GitHubOAuthService.CONFIG_SUFFIX))
-        .thenReturn(pluginConfig.asPluginConfig());
     when(urlProviderMock.get()).thenReturn(CANONICAL_URL);
-
-    return new GitHubOAuthService(pluginConfigFactoryMock, PLUGIN_NAME, urlProviderMock);
+    when(oauthPluginConfigFactoryMock.create(GitHubOAuthService.PROVIDER_NAME))
+        .thenReturn(pluginConfig.asPluginConfig());
+    return new GitHubOAuthService(oauthPluginConfigFactoryMock, urlProviderMock);
   }
 
   private String getExpectedUrl(String rootUrl) throws Exception {