Remove to-be-deprecated OOB OAuth flow in nomulus login (#1625)

This commit is contained in:
Lai Jiang 2022-05-09 17:17:05 -04:00 committed by GitHub
parent bb27feebd6
commit 17d0080742
2 changed files with 93 additions and 10 deletions

View file

@ -0,0 +1,72 @@
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
//
// 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 google.registry.tools;
import com.google.api.client.extensions.java6.auth.oauth2.VerificationCodeReceiver;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import java.io.IOException;
/**
* A thin wrapper around {@link LocalServerReceiver} which points the redirect URI to a different
* port (the forwarding port) while still listening on the random unused port (the remote port)
* nomulus itself picks. This allows us to run the nomulus tool on a remote host (which one can SSH
* into) while performing the OAuth 3-legged login flow in a local browser (from the lost host where
* the SSH client resides).
*
* <p>When performing the login flow, an HTTP server will be listening on the remote port and have a
* redirect_uri of <code>http://localhost:remote_port</code>, which is only accessible from the
* remote host. By changing the redirect_uri to <code>http://localhost:forwarding_port</code>, it
* becomes accessible from the local host, if <code>local_host:forwarding_port</code> is forwarded
* to <code>remote_host:remote_port</code>.
*
* <p>Note that port forwarding is <b>required</b>. We cannot use the remote host's IP or reverse
* DNS address in the redirect URI, even if they are directly accessible from the local host,
* because the only allowed redirect URI scheme for desktops apps when sending a request to the
* Google OAuth server is the loopback address with a port.
*
* @see <href
* a=https://developers.google.com/identity/protocols/oauth2/native-app#request-parameter-redirect_uri>
* redirect_uri values </href>
*/
final class ForwardingServerReceiver implements VerificationCodeReceiver {
private final int forwarding_port;
private final LocalServerReceiver localServerReceiver = new LocalServerReceiver();
ForwardingServerReceiver(int forwarding_port) {
this.forwarding_port = forwarding_port;
}
@Override
public String getRedirectUri() throws IOException {
String redirect_uri = localServerReceiver.getRedirectUri();
return redirect_uri.replace("localhost:" + getRemotePort(), "localhost:" + forwarding_port);
}
@Override
public String waitForCode() throws IOException {
return localServerReceiver.waitForCode();
}
@Override
public void stop() throws IOException {
localServerReceiver.stop();
System.out.println("You can now exit from the SSH session created for port forwarding.");
}
int getRemotePort() {
return localServerReceiver.getPort();
}
}

View file

@ -19,7 +19,7 @@ import com.beust.jcommander.Parameters;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.extensions.java6.auth.oauth2.GooglePromptReceiver;
import java.net.InetAddress;
import javax.inject.Inject;
/** Authorizes the nomulus tool for OAuth 2.0 access to remote resources. */
@ -30,24 +30,35 @@ final class LoginCommand implements Command {
@Inject @AuthModule.ClientScopeQualifier String clientScopeQualifier;
@Parameter(
names = "--remote",
names = "--port",
description =
"Whether the command is run on a remote host where access to a browser is not available. "
+ "If set to true, a URL will be given and a code is expected to be entered after "
+ "the user completes authorization by visiting that URL.")
private boolean remote = false;
"A free port on the local host. When set, it is assumed that the nomulus tool runs on a"
+ " remote host whose browser is not accessible locally. i. e. if you SSH to a"
+ " machine and run `nomulus` there, the ssh client is on the local host and nomulus"
+ " runs on a remote host. You will need to forward the local port specified here to"
+ " a remote port that nomulus randomly picks. Follow the instruction when prompted.")
private int port = 0;
@Override
public void run() throws Exception {
AuthorizationCodeInstalledApp app;
if (remote) {
if (port != 0) {
String remote_host = InetAddress.getLocalHost().getHostName();
ForwardingServerReceiver forwardingServerReceiver = new ForwardingServerReceiver(port);
app =
new AuthorizationCodeInstalledApp(
flow,
new GooglePromptReceiver(),
forwardingServerReceiver,
url -> {
System.out.println("Please open the following address in your browser:");
System.out.println(" " + url);
int remote_port = forwardingServerReceiver.getRemotePort();
System.out.printf(
"Please first run the following command in a separate terminal on your local "
+ "host:\n\n ssh -L %s:localhost:%s %s\n\n",
port, remote_port, remote_host);
System.out.printf(
"Please then open the following URL in your local browser and follow the"
+ " instructions:\n\n %s\n\n",
url);
});
} else {
app = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver());