diff --git a/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java index 15ce8947d..34e7f11ff 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java @@ -109,6 +109,68 @@ public class ComputeEngineCredentials extends GoogleCredentials static final int MAX_COMPUTE_PING_TRIES = 3; static final int COMPUTE_PING_CONNECTION_TIMEOUT_MS = 500; + /** + * Experimental Feature. + * + *

{@link GoogleAuthTransport} specifies how to authenticate to Google APIs. + * + *

Behavior of setting {@link GoogleAuthTransport} / {@link BindingEnforcement}: + * + *

MTLS-bound token where binding enforcement depends on IAM policy: MTLS / {}, {} / + * IAM_POLICY, MTLS / IAM_POLICY + * + *

MTLS-bound token where bindings are always enforced: {} / ON, MTLS / ON + * + *

DirectPath bound token: ALTS / {} + */ + public enum GoogleAuthTransport { + // Authenticating to Google APIs via DirectPath + ALTS("alts"), + // Authenticating to Google APIs via GFE + MTLS("mtls"); + + private final String label; + + private GoogleAuthTransport(String label) { + this.label = label; + } + + public String getLabel() { + return label; + } + } + + /** + * Experimental Feature. + * + *

{@link BindingEnforcement} specifies how binding info in tokens will be enforced. + * + *

Behavior of setting {@link GoogleAuthTransport} / {@link BindingEnforcement}: + * + *

MTLS-bound token where binding enforcement depends on IAM policy: MTLS / {}, {} / + * IAM_POLICY, MTLS / IAM_POLICY + * + *

MTLS-bound token where bindings are always enforced: {} / ON, MTLS / ON + * + *

DirectPath bound token: ALTS / {} + */ + public enum BindingEnforcement { + // Binding enforcement will always happen, irrespective of the IAM policy. + ON("on"), + // Binding enforcement will depend on IAM policy. + IAM_POLICY("iam-policy"); + + private final String label; + + private BindingEnforcement(String label) { + this.label = label; + } + + public String getLabel() { + return label; + } + } + private static final String METADATA_FLAVOR = "Metadata-Flavor"; private static final String GOOGLE = "Google"; private static final String WINDOWS = "windows"; @@ -122,6 +184,9 @@ public class ComputeEngineCredentials extends GoogleCredentials private final Collection scopes; + private final GoogleAuthTransport transport; + private final BindingEnforcement bindingEnforcement; + private transient HttpTransportFactory transportFactory; private transient String serviceAccountEmail; @@ -152,6 +217,8 @@ private ComputeEngineCredentials(ComputeEngineCredentials.Builder builder) { scopeList.removeAll(Arrays.asList("", null)); this.scopes = ImmutableSet.copyOf(scopeList); } + this.transport = builder.getGoogleAuthTransport(); + this.bindingEnforcement = builder.getBindingEnforcement(); } @Override @@ -191,7 +258,10 @@ public final Collection getScopes() { } /** - * If scopes is specified, add "?scopes=comma-separated-list-of-scopes" to the token url. + * If scopes is specified, add "?scopes=comma-separated-list-of-scopes" to the token url. If + * transport is specified, add "?transport=xyz" to the token url; xyz is one of "alts" or "mtls". + * If bindingEnforcement is specified, add "?binding-enforcement=xyz" to the token url; xyz is one + * of "iam-policy" or "on". * * @return token url with the given scopes */ @@ -200,6 +270,12 @@ String createTokenUrlWithScopes() { if (!scopes.isEmpty()) { tokenUrl.set("scopes", Joiner.on(',').join(scopes)); } + if (transport != null) { + tokenUrl.set("transport", transport.getLabel()); + } + if (bindingEnforcement != null) { + tokenUrl.set("binding-enforcement", bindingEnforcement.getLabel()); + } return tokenUrl.toString(); } @@ -643,6 +719,9 @@ public static class Builder extends GoogleCredentials.Builder { private Collection scopes; private Collection defaultScopes; + private GoogleAuthTransport transport; + private BindingEnforcement bindingEnforcement; + protected Builder() { setRefreshMargin(COMPUTE_REFRESH_MARGIN); setExpirationMargin(COMPUTE_EXPIRATION_MARGIN); @@ -684,6 +763,28 @@ public Builder setQuotaProjectId(String quotaProjectId) { return this; } + /** + * Set the {@code GoogleAuthTransport} type. + * + * @param transport the transport type over which to authenticate to Google APIs + */ + @CanIgnoreReturnValue + public Builder setGoogleAuthTransport(GoogleAuthTransport transport) { + this.transport = transport; + return this; + } + + /** + * Set the {@code BindingEnforcement} type. + * + * @param bindingEnforcement the token binding enforcement policy. + */ + @CanIgnoreReturnValue + public Builder setBindingEnforcement(BindingEnforcement bindingEnforcement) { + this.bindingEnforcement = bindingEnforcement; + return this; + } + public HttpTransportFactory getHttpTransportFactory() { return transportFactory; } @@ -696,6 +797,24 @@ public Collection getDefaultScopes() { return defaultScopes; } + /** + * Get the {@code GoogleAuthTransport} type. + * + * @return the transport type over which to authenticate to Google APIs + */ + public GoogleAuthTransport getGoogleAuthTransport() { + return transport; + } + + /** + * Get the {@code BindingEnforcement} type. + * + * @return the token binding enforcement policy. + */ + public BindingEnforcement getBindingEnforcement() { + return bindingEnforcement; + } + @Override public ComputeEngineCredentials build() { return new ComputeEngineCredentials(this); diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java index 10975d874..9f32d8277 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/ComputeEngineCredentialsTest.java @@ -190,6 +190,110 @@ public void buildTokenUrlWithScopes_defaultScopes() { assertEquals("bar", scopes.toArray()[1]); } + @Test + public void buildTokenUrl_nullTransport() { + ComputeEngineCredentials credentials = + ComputeEngineCredentials.newBuilder() + .setGoogleAuthTransport(null) + .setBindingEnforcement(ComputeEngineCredentials.BindingEnforcement.ON) + .build(); + String softBoundTokenUrl = credentials.createTokenUrlWithScopes(); + + assertEquals(TOKEN_URL + "?binding-enforcement=on", softBoundTokenUrl); + } + + @Test + public void buildTokenUrl_nullBindingEnforcement() { + ComputeEngineCredentials credentials = + ComputeEngineCredentials.newBuilder() + .setGoogleAuthTransport(ComputeEngineCredentials.GoogleAuthTransport.MTLS) + .setBindingEnforcement(null) + .build(); + String softBoundTokenUrl = credentials.createTokenUrlWithScopes(); + + assertEquals(TOKEN_URL + "?transport=mtls", softBoundTokenUrl); + } + + @Test + public void buildTokenUrl_nullTransport_nullBindingEnforcement() { + ComputeEngineCredentials credentials = + ComputeEngineCredentials.newBuilder() + .setGoogleAuthTransport(null) + .setBindingEnforcement(null) + .build(); + String softBoundTokenUrl = credentials.createTokenUrlWithScopes(); + + assertEquals(TOKEN_URL, softBoundTokenUrl); + } + + @Test + public void buildTokenUrlSoftMtlsBound_mtls_transport() { + ComputeEngineCredentials credentials = + ComputeEngineCredentials.newBuilder() + .setGoogleAuthTransport(ComputeEngineCredentials.GoogleAuthTransport.MTLS) + .build(); + String softBoundTokenUrl = credentials.createTokenUrlWithScopes(); + + assertEquals(TOKEN_URL + "?transport=mtls", softBoundTokenUrl); + } + + @Test + public void buildTokenUrlSoftMtlsBound_iam_enforcement() { + ComputeEngineCredentials credentials = + ComputeEngineCredentials.newBuilder() + .setBindingEnforcement(ComputeEngineCredentials.BindingEnforcement.IAM_POLICY) + .build(); + String softBoundTokenUrl = credentials.createTokenUrlWithScopes(); + + assertEquals(TOKEN_URL + "?binding-enforcement=iam-policy", softBoundTokenUrl); + } + + @Test + public void buildTokenUrlSoftMtlsBound_mtls_transport_iam_enforcement() { + ComputeEngineCredentials credentials = + ComputeEngineCredentials.newBuilder() + .setGoogleAuthTransport(ComputeEngineCredentials.GoogleAuthTransport.MTLS) + .setBindingEnforcement(ComputeEngineCredentials.BindingEnforcement.IAM_POLICY) + .build(); + String softBoundTokenUrl = credentials.createTokenUrlWithScopes(); + + assertEquals(TOKEN_URL + "?transport=mtls&binding-enforcement=iam-policy", softBoundTokenUrl); + } + + @Test + public void buildTokenUrlHardMtlsBound_always_enforced() { + ComputeEngineCredentials credentials = + ComputeEngineCredentials.newBuilder() + .setBindingEnforcement(ComputeEngineCredentials.BindingEnforcement.ON) + .build(); + String softBoundTokenUrl = credentials.createTokenUrlWithScopes(); + + assertEquals(TOKEN_URL + "?binding-enforcement=on", softBoundTokenUrl); + } + + @Test + public void buildTokenUrlHardMtlsBound_mtls_transport_always_enforced() { + ComputeEngineCredentials credentials = + ComputeEngineCredentials.newBuilder() + .setGoogleAuthTransport(ComputeEngineCredentials.GoogleAuthTransport.MTLS) + .setBindingEnforcement(ComputeEngineCredentials.BindingEnforcement.ON) + .build(); + String softBoundTokenUrl = credentials.createTokenUrlWithScopes(); + + assertEquals(TOKEN_URL + "?transport=mtls&binding-enforcement=on", softBoundTokenUrl); + } + + @Test + public void buildTokenUrlHardDirectPathBound_alts_transport() { + ComputeEngineCredentials credentials = + ComputeEngineCredentials.newBuilder() + .setGoogleAuthTransport(ComputeEngineCredentials.GoogleAuthTransport.ALTS) + .build(); + String softBoundTokenUrl = credentials.createTokenUrlWithScopes(); + + assertEquals(TOKEN_URL + "?transport=alts", softBoundTokenUrl); + } + @Test public void buildScoped_scopesPresent() throws IOException { ComputeEngineCredentials credentials =