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)); + } +}