Set OAuth external ID on CreateAccount call

To create an account you either have to login via the UI or an admin has to
create it via REST API. The former worked without issue, since OAuth is
directly involved in account creation, i.e. the data returned by the
identity provider is being used to set up the account. However, it was not
possible to create an account via REST API and then log into it via OAuth.
The reason was that the account created via REST API didn't get an external
ID on creation that linked it to the IDP. On authentication Gerrit would
thus complain that the account exists and that the provided secret didn't
match the stored password instead of contacting the OAuth server.

Gerrit provides an extension point where plugins can provide additional
external IDs on account creation. This extension point is being implemented
by this change.

Change-Id: Ie643503f4e1c2247afd1674036aeaf3b33ca7665
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 683602d..0d3ed1d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/oauth/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/Module.java
@@ -14,11 +14,42 @@
 
 package com.googlesource.gerrit.plugins.oauth;
 
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.server.account.AccountExternalIdCreator;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
+import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import java.util.List;
+import org.eclipse.jgit.lib.Config;
 
 public class Module extends AbstractModule {
+  private final List<String> configuredProviders;
+  private final ExternalIdFactory externalIdFactory;
+
+  @Inject
+  public Module(
+      @GerritServerConfig Config config,
+      @PluginName String pluginName,
+      ExternalIdFactory externalIdFactory) {
+    configuredProviders =
+        config.getSubsections("plugin").stream()
+            .filter(s -> s.startsWith(pluginName))
+            .map(s -> s.substring(pluginName.length() + 1, s.length() - 6))
+            .toList();
+    this.externalIdFactory = externalIdFactory;
+  }
+
   @Override
   protected void configure() {
     bind(OAuthPluginConfigFactory.class);
+    for (String provider : configuredProviders) {
+      bind(AccountExternalIdCreator.class)
+          .annotatedWith(Exports.named(provider))
+          .toInstance(
+              new OAuthExternalIdCreator(
+                  externalIdFactory, OAuthServiceProviderExternalIdScheme.create(provider)));
+    }
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuthExternalIdCreator.java b/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuthExternalIdCreator.java
new file mode 100644
index 0000000..b690258
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/oauth/OAuthExternalIdCreator.java
@@ -0,0 +1,38 @@
+// 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.entities.Account;
+import com.google.gerrit.server.account.AccountExternalIdCreator;
+import com.google.gerrit.server.account.externalids.ExternalId;
+import com.google.gerrit.server.account.externalids.ExternalIdFactory;
+import java.util.List;
+
+public class OAuthExternalIdCreator implements AccountExternalIdCreator {
+  private final ExternalIdFactory extIdFactory;
+  private final String oauthProviderExtIdScheme;
+
+  public OAuthExternalIdCreator(ExternalIdFactory extIdFactory, String oauthProviderExtIdScheme) {
+    this.extIdFactory = extIdFactory;
+    this.oauthProviderExtIdScheme = oauthProviderExtIdScheme;
+  }
+
+  @Override
+  public List<ExternalId> create(Account.Id id, String username, String email) {
+    return List.of(
+        extIdFactory.createWithEmail(oauthProviderExtIdScheme, username, id, email),
+        extIdFactory.create(ExternalId.SCHEME_EXTERNAL, username, id));
+  }
+}