diff --git a/spring-vault-core/pom.xml b/spring-vault-core/pom.xml
index 4117f36f..df4377f7 100644
--- a/spring-vault-core/pom.xml
+++ b/spring-vault-core/pom.xml
@@ -140,6 +140,28 @@
+
+ com.google.apis
+ google-api-services-iam
+ true
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+
+
+
+ com.google.auth
+ google-auth-library-oauth2-http
+ true
+
+
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpComputeAuthentication.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpComputeAuthentication.java
new file mode 100644
index 00000000..b98f94ba
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpComputeAuthentication.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2018 the original author or authors.
+ *
+ * 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 org.springframework.vault.authentication;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.Assert;
+import org.springframework.vault.VaultException;
+import org.springframework.vault.authentication.AuthenticationSteps.HttpRequest;
+import org.springframework.vault.support.VaultToken;
+import org.springframework.web.client.HttpStatusCodeException;
+import org.springframework.web.client.RestOperations;
+
+import static org.springframework.vault.authentication.AuthenticationSteps.HttpRequestBuilder.get;
+
+/**
+ * GCP GCE (Google Compute Engine)-based login implementation using GCE's metadata service
+ * to create signed JSON Web Token.
+ *
+ * This authentication method uses Googles GCE's metadata service in combination with the
+ * default/specified service account to obtain an identity document as JWT using a HTTP
+ * client. Credentials and authenticity are implied from the runtime itself and are not
+ * required to be configured.
+ *
+ * @author Mark Paluch
+ * @since 2.1
+ * @see GcpComputeAuthenticationOptions
+ * @see Auth Backend: gcp
+ * (IAM)
+ * @see Google
+ * Compute Engine: Verifying the Identity of Instances
+ */
+public class GcpComputeAuthentication extends GcpJwtAuthenticationSupport implements
+ ClientAuthentication, AuthenticationStepsFactory {
+
+ public static final String COMPUTE_METADATA_URL_TEMPLATE = "http://metadata/computeMetadata/v1/instance/service-accounts/{serviceAccount}/identity"
+ + "?audience={audience}&format={format}";
+
+ private final GcpComputeAuthenticationOptions options;
+
+ private final RestOperations googleMetadataRestOperations;
+
+ /**
+ * Create a new {@link GcpComputeAuthentication} instance given
+ * {@link GcpComputeAuthenticationOptions} and {@link RestOperations} for Vault and
+ * Google API use.
+ *
+ * @param options must not be {@literal null}.
+ * @param vaultRestOperations must not be {@literal null}.
+ */
+ public GcpComputeAuthentication(GcpComputeAuthenticationOptions options,
+ RestOperations vaultRestOperations) {
+ this(options, vaultRestOperations, vaultRestOperations);
+ }
+
+ /**
+ * Create a new {@link GcpComputeAuthentication} instance given
+ * {@link GcpComputeAuthenticationOptions} and {@link RestOperations} for Vault and
+ * Google API use.
+ *
+ * @param options must not be {@literal null}.
+ * @param vaultRestOperations must not be {@literal null}.
+ * @param googleMetadataRestOperations must not be {@literal null}.
+ */
+ public GcpComputeAuthentication(GcpComputeAuthenticationOptions options,
+ RestOperations vaultRestOperations,
+ RestOperations googleMetadataRestOperations) {
+
+ super(vaultRestOperations);
+
+ Assert.notNull(options, "GcpGceAuthenticationOptions must not be null");
+ Assert.notNull(googleMetadataRestOperations,
+ "Google Metadata RestOperations must not be null");
+
+ this.options = options;
+ this.googleMetadataRestOperations = googleMetadataRestOperations;
+ }
+
+ /**
+ * Creates a {@link AuthenticationSteps} for GCE authentication given
+ * {@link GcpComputeAuthenticationOptions}.
+ *
+ * @param options must not be {@literal null}.
+ * @return {@link AuthenticationSteps} for cubbyhole authentication.
+ */
+ public static AuthenticationSteps createAuthenticationSteps(
+ GcpComputeAuthenticationOptions options) {
+
+ Assert.notNull(options, "CubbyholeAuthenticationOptions must not be null");
+
+ String serviceAccount = options.getServiceAccount();
+ String audience = getAudience(options.getRole());
+
+ HttpRequest jwtRequest = get(COMPUTE_METADATA_URL_TEMPLATE,
+ serviceAccount, audience, "full") //
+ .with(getMetadataHttpHeaders()) //
+ .as(String.class);
+
+ return AuthenticationSteps.fromHttpRequest(jwtRequest)
+ //
+ .map(jwt -> createRequestBody(options.getRole(), jwt))
+ .login("auth/{mount}/login", options.getPath());
+ }
+
+ @Override
+ public VaultToken login() throws VaultException {
+
+ String signedJwt = signJwt();
+
+ return doLogin("GCP-GCE", signedJwt, this.options.getPath(),
+ this.options.getRole());
+ }
+
+ @Override
+ public AuthenticationSteps getAuthenticationSteps() {
+ return createAuthenticationSteps(options);
+ }
+
+ protected String signJwt() {
+
+ try {
+ Map urlParameters = new LinkedHashMap<>();
+ urlParameters.put("serviceAccount", this.options.getServiceAccount());
+ urlParameters.put("audience", getAudience(this.options.getRole()));
+ urlParameters.put("format", "full");
+
+ HttpHeaders headers = getMetadataHttpHeaders();
+ HttpEntity entity = new HttpEntity<>(headers);
+
+ ResponseEntity response = googleMetadataRestOperations.exchange(
+ COMPUTE_METADATA_URL_TEMPLATE, HttpMethod.GET, entity, String.class,
+ urlParameters);
+
+ return response.getBody();
+ }
+ catch (HttpStatusCodeException e) {
+ throw new VaultException("Cannot obtain signed identity", e);
+ }
+ }
+
+ private static HttpHeaders getMetadataHttpHeaders() {
+
+ HttpHeaders headers = new HttpHeaders();
+
+ headers.set("Metadata-Flavor", "Google");
+
+ return headers;
+ }
+
+ private static String getAudience(String role) {
+ return String.format("https://localhost:8200/vault/%s", role);
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpComputeAuthenticationOptions.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpComputeAuthenticationOptions.java
new file mode 100644
index 00000000..b3c1a7af
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpComputeAuthenticationOptions.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2018 the original author or authors.
+ *
+ * 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 org.springframework.vault.authentication;
+
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * Authentication options for {@link GcpComputeAuthentication}.
+ *
+ * Authentication options provide the path, role and an optional service account
+ * identifier. Instances of this class are immutable once constructed.
+ *
+ * @author Mark Paluch
+ * @see GcpComputeAuthentication
+ * @see #builder()
+ * @since 2.1
+ */
+public class GcpComputeAuthenticationOptions {
+
+ public static final String DEFAULT_GCP_AUTHENTICATION_PATH = "gcp";
+
+ /**
+ * Path of the gcp authentication backend mount.
+ */
+ private final String path;
+
+ /**
+ * GCE service account identifier.
+ */
+ private final String serviceAccount;
+
+ /**
+ * Name of the role against which the login is being attempted. If role is not
+ * specified, the friendly name (i.e., role name or username) of the IAM principal
+ * authenticated. If a matching role is not found, login fails.
+ */
+ private final String role;
+
+ private GcpComputeAuthenticationOptions(String path, String serviceAccount,
+ String role) {
+
+ this.path = path;
+ this.serviceAccount = serviceAccount;
+ this.role = role;
+ }
+
+ /**
+ * @return a new {@link GcpComputeAuthenticationOptionsBuilder}.
+ */
+ public static GcpComputeAuthenticationOptionsBuilder builder() {
+ return new GcpComputeAuthenticationOptionsBuilder();
+ }
+
+ /**
+ * @return the path of the gcp authentication backend mount.
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * @return the GCE service account identifier.
+ */
+ public String getServiceAccount() {
+ return serviceAccount;
+ }
+
+ /**
+ * @return name of the role against which the login is being attempted.
+ */
+ public String getRole() {
+ return role;
+ }
+
+ /**
+ * Builder for {@link GcpComputeAuthenticationOptions}.
+ */
+ public static class GcpComputeAuthenticationOptionsBuilder {
+
+ private String path = DEFAULT_GCP_AUTHENTICATION_PATH;
+
+ @Nullable
+ private String role;
+
+ private String serviceAccount = "default";
+
+ GcpComputeAuthenticationOptionsBuilder() {
+ }
+
+ /**
+ * Configure the mount path, defaults to {@literal aws}.
+ *
+ * @param path must not be empty or {@literal null}.
+ * @return {@code this} {@link GcpComputeAuthenticationOptionsBuilder}.
+ */
+ public GcpComputeAuthenticationOptionsBuilder path(String path) {
+
+ Assert.hasText(path, "Path must not be empty");
+
+ this.path = path;
+ return this;
+ }
+
+ /**
+ * Configure the service account identifier. Uses the {@code default} service
+ * account if left unconfigured.
+ *
+ * @param serviceAccount must not be empty or {@literal null}.
+ * @return {@code this} {@link GcpComputeAuthenticationOptionsBuilder}.
+ */
+ public GcpComputeAuthenticationOptionsBuilder serviceAccount(String serviceAccount) {
+
+ Assert.hasText(serviceAccount, "Service account must not be null");
+
+ this.serviceAccount = serviceAccount;
+ return this;
+ }
+
+ /**
+ * Configure the name of the role against which the login is being attempted.
+ *
+ * @param role must not be empty or {@literal null}.
+ * @return {@code this} {@link GcpComputeAuthenticationOptionsBuilder}.
+ */
+ public GcpComputeAuthenticationOptionsBuilder role(String role) {
+
+ Assert.hasText(role, "Role must not be null or empty");
+
+ this.role = role;
+ return this;
+ }
+
+ /**
+ * Build a new {@link GcpComputeAuthenticationOptions} instance.
+ *
+ * @return a new {@link GcpComputeAuthenticationOptions}.
+ */
+ public GcpComputeAuthenticationOptions build() {
+
+ Assert.notNull(role, "Role must not be null");
+
+ return new GcpComputeAuthenticationOptions(path, serviceAccount, role);
+ }
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpCredentialSupplier.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpCredentialSupplier.java
new file mode 100644
index 00000000..1a121349
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpCredentialSupplier.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018 the original author or authors.
+ *
+ * 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 org.springframework.vault.authentication;
+
+import java.io.IOException;
+import java.util.function.Supplier;
+
+import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
+
+/**
+ * Interface to obtain a {@link GoogleCredential} for GCP IAM authentication.
+ * Implementations are used by {@link GcpIamAuthentication}.
+ *
+ * @author Mark Paluch
+ * @since 2.1
+ * @see GcpIamAuthentication
+ */
+@FunctionalInterface
+public interface GcpCredentialSupplier extends Supplier {
+
+ /**
+ * Exception-safe helper to get {@link GoogleCredential} from {@link #getCredential}.
+ *
+ * @return the GoogleCredential for JWT signing.
+ */
+ @Override
+ default GoogleCredential get() {
+
+ try {
+ return getCredential();
+ }
+ catch (IOException e) {
+ throw new IllegalStateException("Cannot obtain GoogleCredential", e);
+ }
+ }
+
+ /**
+ * Get a {@link GoogleCredential} for GCP IAM authentication via JWT signing.
+ *
+ * @return the {@link GoogleCredential}.
+ * @throws IOException if the credential lookup fails.
+ */
+ GoogleCredential getCredential() throws IOException;
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpIamAuthentication.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpIamAuthentication.java
new file mode 100644
index 00000000..d601b35e
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpIamAuthentication.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2018 the original author or authors.
+ *
+ * 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 org.springframework.vault.authentication;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.time.Instant;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.google.api.client.googleapis.apache.GoogleApacheHttpTransport;
+import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
+import com.google.api.client.http.HttpTransport;
+import com.google.api.client.json.JsonFactory;
+import com.google.api.client.json.jackson2.JacksonFactory;
+import com.google.api.services.iam.v1.Iam;
+import com.google.api.services.iam.v1.Iam.Builder;
+import com.google.api.services.iam.v1.Iam.Projects.ServiceAccounts.SignJwt;
+import com.google.api.services.iam.v1.model.SignJwtRequest;
+import com.google.api.services.iam.v1.model.SignJwtResponse;
+import com.google.auth.oauth2.GoogleCredentials;
+
+import org.springframework.util.Assert;
+import org.springframework.vault.VaultException;
+import org.springframework.vault.support.VaultToken;
+import org.springframework.web.client.RestOperations;
+
+/**
+ * GCP IAM login implementation using GCP IAM service accounts to legitimate its
+ * authenticity via JSON Web Token.
+ *
+ * This authentication method uses Googles IAM API to obtain a signed token for a specific
+ * {@link com.google.api.client.auth.oauth2.Credential}. Project and service account
+ * details are obtained from a {@link GoogleCredential} that can be retrieved either from
+ * a JSON file or the runtime environment (GAE, GCE).
+ *
+ * {@link GcpIamAuthentication} uses Google Java API that uses synchronous API.
+ *
+ * @author Mark Paluch
+ * @since 2.1
+ * @see GcpIamAuthenticationOptions
+ * @see HttpTransport
+ * @see GoogleCredential
+ * @see GoogleCredentials#getApplicationDefault()
+ * @see RestOperations
+ * @see Auth Backend: gcp
+ * (IAM)
+ * @see GCP:
+ * projects.serviceAccounts.signJwt
+ */
+public class GcpIamAuthentication extends GcpJwtAuthenticationSupport implements
+ ClientAuthentication {
+
+ private static final JsonFactory JSON_FACTORY = new JacksonFactory();
+
+ private final GcpIamAuthenticationOptions options;
+
+ private final HttpTransport httpTransport;
+
+ private final GoogleCredential credential;
+
+ /**
+ * Create a new instance of {@link GcpIamAuthentication} given
+ * {@link GcpIamAuthenticationOptions} and {@link RestOperations}. This constructor
+ * initializes {@link GoogleApacheHttpTransport} for Google API usage.
+ *
+ * @param options must not be {@literal null}.
+ * @param restOperations HTTP client for for Vault login, must not be {@literal null}.
+ * @throws GeneralSecurityException thrown by
+ * {@link GoogleApacheHttpTransport#newTrustedTransport()}.
+ * @throws IOException thrown by
+ * {@link GoogleApacheHttpTransport#newTrustedTransport()}.
+ */
+ public GcpIamAuthentication(GcpIamAuthenticationOptions options,
+ RestOperations restOperations) throws GeneralSecurityException, IOException {
+ this(options, restOperations, GoogleApacheHttpTransport.newTrustedTransport());
+ }
+
+ /**
+ * Create a new instance of {@link GcpIamAuthentication} given
+ * {@link GcpIamAuthenticationOptions}, {@link RestOperations} and
+ * {@link HttpTransport}.
+ *
+ * @param options must not be {@literal null}.
+ * @param restOperations HTTP client for for Vault login, must not be {@literal null}.
+ * @param httpTransport HTTP client for Google API use, must not be {@literal null}.
+ */
+ public GcpIamAuthentication(GcpIamAuthenticationOptions options,
+ RestOperations restOperations, HttpTransport httpTransport) {
+
+ super(restOperations);
+
+ Assert.notNull(options, "GcpIamAuthenticationOptions must not be null!");
+ Assert.notNull(restOperations, "RestOperations must not be null!");
+ Assert.notNull(httpTransport, "HttpTransport must not be null!");
+
+ this.options = options;
+ this.httpTransport = httpTransport;
+ this.credential = options.getCredentialSupplier().get();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public VaultToken login() throws VaultException {
+
+ String signedJwt = signJwt();
+
+ return doLogin("GCP-IAM", signedJwt, this.options.getPath(),
+ this.options.getRole());
+ }
+
+ protected String signJwt() {
+
+ String projectId = credential.getServiceAccountProjectId();
+ String serviceAccount = credential.getServiceAccountId();
+ Map jwtPayload = getJwtPayload(options, serviceAccount);
+
+ Iam iam = new Builder(httpTransport, JSON_FACTORY, credential)
+ .setApplicationName("Spring Vault/" + getClass().getName()).build();
+
+ try {
+
+ String payload = JSON_FACTORY.toString(jwtPayload);
+ SignJwtRequest request = new SignJwtRequest();
+ request.setPayload(payload);
+
+ SignJwt signJwt = iam
+ .projects()
+ .serviceAccounts()
+ .signJwt(
+ String.format("projects/%s/serviceAccounts/%s", projectId,
+ serviceAccount), request);
+
+ SignJwtResponse response = signJwt.execute();
+
+ return response.getSignedJwt();
+ }
+ catch (IOException e) {
+ throw new VaultException("Cannot sign JWT", e);
+ }
+ }
+
+ private static Map getJwtPayload(GcpIamAuthenticationOptions options,
+ String serviceAccount) {
+
+ Instant validUntil = options.getClock().instant().plus(options.getJwtValidity());
+
+ Map payload = new LinkedHashMap<>();
+
+ payload.put("sub", serviceAccount);
+ payload.put("aud", "vault/" + options.getRole());
+ payload.put("exp", validUntil.getEpochSecond());
+
+ return payload;
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpIamAuthenticationOptions.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpIamAuthenticationOptions.java
new file mode 100644
index 00000000..b9518430
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpIamAuthenticationOptions.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2018 the original author or authors.
+ *
+ * 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 org.springframework.vault.authentication;
+
+import java.time.Clock;
+import java.time.Duration;
+
+import com.amazonaws.auth.AWSCredentialsProvider;
+import com.google.api.client.auth.oauth2.Credential;
+import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
+
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * Authentication options for {@link GcpIamAuthentication}.
+ *
+ * Authentication options provide the path, a {@link GcpCredentialSupplier}, role and JWT
+ * expiry for GCP IAM authentication. Instances of this class are immutable once
+ * constructed.
+ *
+ * @author Mark Paluch
+ * @see GcpIamAuthentication
+ * @see #builder()
+ * @since 2.1
+ */
+public class GcpIamAuthenticationOptions {
+
+ public static final String DEFAULT_GCP_AUTHENTICATION_PATH = "gcp";
+
+ /**
+ * Path of the gcp authentication backend mount.
+ */
+ private final String path;
+
+ private final GcpCredentialSupplier credentialSupplier;
+
+ /**
+ * Name of the role against which the login is being attempted. If role is not
+ * specified, the friendly name (i.e., role name or username) of the IAM principal
+ * authenticated. If a matching role is not found, login fails.
+ */
+ private final String role;
+
+ /**
+ * JWT validity/expiration.
+ */
+ private final Duration jwtValidity;
+
+ /**
+ * {@link Clock} to calculate JWT expiration.
+ */
+ private final Clock clock;
+
+ private GcpIamAuthenticationOptions(String path,
+ GcpCredentialSupplier credentialSupplier, String role, Duration jwtValidity,
+ Clock clock) {
+
+ this.path = path;
+ this.credentialSupplier = credentialSupplier;
+ this.role = role;
+ this.jwtValidity = jwtValidity;
+ this.clock = clock;
+ }
+
+ /**
+ * @return a new {@link GcpIamAuthenticationOptionsBuilder}.
+ */
+ public static GcpIamAuthenticationOptionsBuilder builder() {
+ return new GcpIamAuthenticationOptionsBuilder();
+ }
+
+ /**
+ * @return the path of the gcp authentication backend mount.
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * @return the gcp {@link Credential} supplier.
+ */
+ public GcpCredentialSupplier getCredentialSupplier() {
+ return credentialSupplier;
+ }
+
+ /**
+ * @return name of the role against which the login is being attempted.
+ */
+ public String getRole() {
+ return role;
+ }
+
+ /**
+ * @return {@link Duration} of the JWT to generate.
+ */
+ public Duration getJwtValidity() {
+ return jwtValidity;
+ }
+
+ /**
+ * @return {@link Clock} used to calculate epoch seconds until the JWT expires.
+ */
+ public Clock getClock() {
+ return clock;
+ }
+
+ /**
+ * Builder for {@link GcpIamAuthenticationOptions}.
+ */
+ public static class GcpIamAuthenticationOptionsBuilder {
+
+ private String path = DEFAULT_GCP_AUTHENTICATION_PATH;
+
+ @Nullable
+ private String role;
+
+ @Nullable
+ private GcpCredentialSupplier credentialSupplier;
+
+ private Duration jwtValidity = Duration.ofMinutes(15);
+
+ private Clock clock = Clock.systemDefaultZone();
+
+ GcpIamAuthenticationOptionsBuilder() {
+ }
+
+ /**
+ * Configure the mount path, defaults to {@literal aws}.
+ *
+ * @param path must not be empty or {@literal null}.
+ * @return {@code this} {@link GcpIamAuthenticationOptionsBuilder}.
+ */
+ public GcpIamAuthenticationOptionsBuilder path(String path) {
+
+ Assert.hasText(path, "Path must not be empty");
+
+ this.path = path;
+ return this;
+ }
+
+ /**
+ * Configure static Google credentials, required to create a signed JWT. Either
+ * use static credentials or provide a
+ * {@link #credentialSupplier(GcpCredentialSupplier) credentials provider}.
+ *
+ * @param credential must not be {@literal null}.
+ * @return {@code this} {@link GcpIamAuthenticationOptionsBuilder}.
+ * @see #credentialSupplier(GcpCredentialSupplier)
+ */
+ public GcpIamAuthenticationOptionsBuilder credential(GoogleCredential credential) {
+
+ Assert.notNull(credential, "Credential must not be null");
+
+ return credentialSupplier(() -> credential);
+ }
+
+ /**
+ * Configure an {@link AWSCredentialsProvider}, required to create a signed JWT.
+ * Alternatively, configure static {@link #credential(GoogleCredential)
+ * credentials}.
+ *
+ * @param credentialSupplier must not be {@literal null}.
+ * @return {@code this} {@link GcpIamAuthenticationOptionsBuilder}.
+ * @see #credential(GoogleCredential)
+ */
+ public GcpIamAuthenticationOptionsBuilder credentialSupplier(
+ GcpCredentialSupplier credentialSupplier) {
+
+ Assert.notNull(credentialSupplier, "GcpCredentialSupplier must not be null");
+
+ this.credentialSupplier = credentialSupplier;
+ return this;
+ }
+
+ /**
+ * Configure the name of the role against which the login is being attempted.
+ *
+ * @param role must not be empty or {@literal null}.
+ * @return {@code this} {@link GcpIamAuthenticationOptionsBuilder}.
+ */
+ public GcpIamAuthenticationOptionsBuilder role(String role) {
+
+ Assert.hasText(role, "Role must not be null or empty");
+
+ this.role = role;
+ return this;
+ }
+
+ /**
+ * Configure the {@link Duration} for the JWT expiration. This defaults to 15
+ * minutes and cannot be more than a hour.
+ *
+ * @param jwtValidity must not be {@literal null}.
+ * @return {@code this} {@link GcpIamAuthenticationOptionsBuilder}.
+ */
+ public GcpIamAuthenticationOptionsBuilder jwtValidity(Duration jwtValidity) {
+
+ Assert.hasText(role, "JWT validity duration must not be null");
+
+ this.jwtValidity = jwtValidity;
+ return this;
+ }
+
+ /**
+ * Configure the {@link Clock} used to calculate epoch seconds until the JWT
+ * expiration.
+ *
+ * @param clock must not be {@literal null}.
+ * @return {@code this} {@link GcpIamAuthenticationOptionsBuilder}.
+ */
+ public GcpIamAuthenticationOptionsBuilder clock(Clock clock) {
+
+ Assert.hasText(role, "Clock must not be null");
+
+ this.clock = clock;
+ return this;
+ }
+
+ /**
+ * Build a new {@link GcpIamAuthenticationOptions} instance.
+ *
+ * @return a new {@link GcpIamAuthenticationOptions}.
+ */
+ public GcpIamAuthenticationOptions build() {
+
+ Assert.notNull(credentialSupplier, "GcpCredentialSupplier must not be null");
+ Assert.notNull(role, "Role must not be null");
+
+ return new GcpIamAuthenticationOptions(path, credentialSupplier, role,
+ jwtValidity, clock);
+ }
+ }
+}
diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpJwtAuthenticationSupport.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpJwtAuthenticationSupport.java
new file mode 100644
index 00000000..ac751cb8
--- /dev/null
+++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/GcpJwtAuthenticationSupport.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2018 the original author or authors.
+ *
+ * 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 org.springframework.vault.authentication;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.util.Assert;
+import org.springframework.vault.VaultException;
+import org.springframework.vault.client.VaultResponses;
+import org.springframework.vault.support.VaultResponse;
+import org.springframework.vault.support.VaultToken;
+import org.springframework.web.client.HttpStatusCodeException;
+import org.springframework.web.client.RestOperations;
+
+/**
+ * Base class for GCP JWT-based authentication. Used by framework components.
+ *
+ * @author Mark Paluch
+ * @since 2.1
+ */
+public abstract class GcpJwtAuthenticationSupport {
+
+ private static final Log logger = LogFactory
+ .getLog(GcpJwtAuthenticationSupport.class);
+
+ private final RestOperations restOperations;
+
+ GcpJwtAuthenticationSupport(RestOperations restOperations) {
+
+ Assert.notNull(restOperations, "Vault RestOperations must not be null");
+
+ this.restOperations = restOperations;
+ }
+
+ /**
+ * Perform the actual Vault login given {@code signedJwt}.
+ *
+ * @param authenticationName authentication name for logging.
+ * @param signedJwt the JSON web token.
+ * @param path GCP authentication mount path.
+ * @param role Vault role.
+ * @return the {@link VaultToken}.
+ */
+ VaultToken doLogin(String authenticationName, String signedJwt, String path,
+ String role) {
+
+ Map login = createRequestBody(role, signedJwt);
+
+ try {
+
+ VaultResponse response = this.restOperations.postForObject(
+ "auth/{mount}/login", login, VaultResponse.class, path);
+
+ Assert.state(response != null && response.getAuth() != null,
+ "Auth field must not be null");
+
+ if (logger.isDebugEnabled()) {
+
+ if (response.getAuth().get("metadata") instanceof Map) {
+
+ Map metadata = (Map) response
+ .getAuth().get("metadata");
+ logger.debug(String.format(
+ "Login successful using %s authentication for user id %s",
+ authenticationName, metadata.get("service_account_email")));
+ }
+ else {
+ logger.debug("Login successful using " + authenticationName
+ + " authentication");
+ }
+ }
+
+ return LoginTokenUtil.from(response.getAuth());
+ }
+ catch (HttpStatusCodeException e) {
+ throw new VaultException(String.format("Cannot login using %s: %s",
+ authenticationName,
+ VaultResponses.getError(e.getResponseBodyAsString())));
+ }
+ }
+
+ static Map createRequestBody(String role, String signedJwt) {
+
+ Map login = new HashMap<>();
+
+ login.put("role", role);
+ login.put("jwt", signedJwt);
+
+ return login;
+ }
+}
diff --git a/spring-vault-core/src/test/java/org/springframework/vault/authentication/CubbyholeAuthenticationUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/authentication/CubbyholeAuthenticationUnitTests.java
index 199ffcd3..258d9d4e 100644
--- a/spring-vault-core/src/test/java/org/springframework/vault/authentication/CubbyholeAuthenticationUnitTests.java
+++ b/spring-vault-core/src/test/java/org/springframework/vault/authentication/CubbyholeAuthenticationUnitTests.java
@@ -50,7 +50,7 @@ public class CubbyholeAuthenticationUnitTests {
private MockRestServiceServer mockRest;
@Before
- public void before() throws Exception {
+ public void before() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(new PrefixAwareUriTemplateHandler());
@@ -132,7 +132,7 @@ public class CubbyholeAuthenticationUnitTests {
}
@Test
- public void shouldLoginUsingStoredLogin() throws Exception {
+ public void shouldLoginUsingStoredLogin() {
mockRest.expect(requestTo("/cubbyhole/token")).andExpect(method(HttpMethod.GET))
.andExpect(header(VaultHttpHeaders.VAULT_TOKEN, "hello"))
@@ -153,7 +153,7 @@ public class CubbyholeAuthenticationUnitTests {
}
@Test
- public void shouldRetrieveRenewabulityUsingStoredLogin() throws Exception {
+ public void shouldRetrieveRenewabilityUsingStoredLogin() {
mockRest.expect(requestTo("/cubbyhole/token")).andExpect(method(HttpMethod.GET))
.andExpect(header(VaultHttpHeaders.VAULT_TOKEN, "hello"))
@@ -185,7 +185,7 @@ public class CubbyholeAuthenticationUnitTests {
}
@Test
- public void shouldFailUsingStoredLoginNoData() throws Exception {
+ public void shouldFailUsingStoredLoginNoData() {
mockRest.expect(requestTo("/cubbyhole/token")).andExpect(method(HttpMethod.GET))
.andExpect(header(VaultHttpHeaders.VAULT_TOKEN, "hello"))
@@ -208,7 +208,7 @@ public class CubbyholeAuthenticationUnitTests {
}
@Test
- public void shouldFailUsingStoredMultipleEntries() throws Exception {
+ public void shouldFailUsingStoredMultipleEntries() {
mockRest.expect(requestTo("/cubbyhole/token")).andExpect(method(HttpMethod.GET))
.andExpect(header(VaultHttpHeaders.VAULT_TOKEN, "hello"))
diff --git a/spring-vault-core/src/test/java/org/springframework/vault/authentication/GcpComputeAuthenticationUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/authentication/GcpComputeAuthenticationUnitTests.java
new file mode 100644
index 00000000..ac119f03
--- /dev/null
+++ b/spring-vault-core/src/test/java/org/springframework/vault/authentication/GcpComputeAuthenticationUnitTests.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2018 the original author or authors.
+ *
+ * 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 org.springframework.vault.authentication;
+
+import java.time.Duration;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.client.MockRestServiceServer;
+import org.springframework.vault.support.VaultToken;
+import org.springframework.web.client.RestTemplate;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.jsonPath;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+
+/**
+ * Unit tests for {@link GcpComputeAuthentication}.
+ *
+ * @author Mark Paluch
+ */
+public class GcpComputeAuthenticationUnitTests {
+
+ private RestTemplate restTemplate;
+ private MockRestServiceServer mockRest;
+
+ @Before
+ public void before() {
+
+ RestTemplate restTemplate = new RestTemplate();
+
+ this.mockRest = MockRestServiceServer.createServer(restTemplate);
+ this.restTemplate = restTemplate;
+ }
+
+ private void setupMocks() {
+
+ mockRest.expect(
+ requestTo("http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://localhost:8200/vault/dev-role&format=full"))
+ .andExpect(method(HttpMethod.GET))
+ .andRespond(
+ withSuccess().contentType(MediaType.TEXT_PLAIN).body("my-jwt"));
+
+ mockRest.expect(requestTo("/auth/gcp/login"))
+ .andExpect(method(HttpMethod.POST))
+ .andExpect(jsonPath("$.role").value("dev-role"))
+ .andExpect(jsonPath("$.jwt").value("my-jwt"))
+ .andRespond(
+ withSuccess()
+ .contentType(MediaType.APPLICATION_JSON)
+ .body("{"
+ + "\"auth\":{\"client_token\":\"my-token\", \"renewable\": true, \"lease_duration\": 10}"
+ + "}"));
+ }
+
+ @Test
+ public void shouldLogin() {
+
+ setupMocks();
+
+ GcpComputeAuthenticationOptions options = GcpComputeAuthenticationOptions
+ .builder().role("dev-role").build();
+
+ GcpComputeAuthentication authentication = new GcpComputeAuthentication(options,
+ restTemplate);
+
+ VaultToken login = authentication.login();
+
+ assertThat(login).isInstanceOf(LoginToken.class);
+ assertThat(login.getToken()).isEqualTo("my-token");
+
+ LoginToken loginToken = (LoginToken) login;
+ assertThat(loginToken.isRenewable()).isTrue();
+ assertThat(loginToken.getLeaseDuration()).isEqualTo(Duration.ofSeconds(10));
+ }
+
+ @Test
+ public void shouldLoginWithAuthenticationSteps() {
+
+ setupMocks();
+
+ GcpComputeAuthenticationOptions options = GcpComputeAuthenticationOptions
+ .builder().role("dev-role").build();
+
+ GcpComputeAuthentication authentication = new GcpComputeAuthentication(options,
+ restTemplate);
+
+ AuthenticationStepsExecutor executor = new AuthenticationStepsExecutor(
+ authentication.getAuthenticationSteps(), restTemplate);
+
+ VaultToken login = executor.login();
+
+ assertThat(login).isInstanceOf(LoginToken.class);
+ assertThat(login.getToken()).isEqualTo("my-token");
+
+ LoginToken loginToken = (LoginToken) login;
+ assertThat(loginToken.isRenewable()).isTrue();
+ assertThat(loginToken.getLeaseDuration()).isEqualTo(Duration.ofSeconds(10));
+ }
+}
diff --git a/spring-vault-core/src/test/java/org/springframework/vault/authentication/GcpIamAuthenticationUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/authentication/GcpIamAuthenticationUnitTests.java
new file mode 100644
index 00000000..c3e9c5aa
--- /dev/null
+++ b/spring-vault-core/src/test/java/org/springframework/vault/authentication/GcpIamAuthenticationUnitTests.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2018 the original author or authors.
+ *
+ * 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 org.springframework.vault.authentication;
+
+import java.security.PrivateKey;
+import java.time.Duration;
+
+import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
+import com.google.api.client.googleapis.auth.oauth2.GoogleCredential.Builder;
+import com.google.api.client.testing.http.MockHttpTransport;
+import com.google.api.client.testing.http.MockLowLevelHttpResponse;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.client.MockRestServiceServer;
+import org.springframework.vault.client.VaultClients.PrefixAwareUriTemplateHandler;
+import org.springframework.vault.support.VaultToken;
+import org.springframework.web.client.RestTemplate;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.jsonPath;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
+import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
+import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
+
+/**
+ * Unit tests for {@link GcpIamAuthentication}.
+ *
+ * @author Mark Paluch
+ */
+public class GcpIamAuthenticationUnitTests {
+
+ private RestTemplate restTemplate;
+ private MockRestServiceServer mockRest;
+ private MockHttpTransport mockHttpTransport;
+
+ @Before
+ public void before() {
+
+ RestTemplate restTemplate = new RestTemplate();
+ restTemplate.setUriTemplateHandler(new PrefixAwareUriTemplateHandler());
+
+ this.mockRest = MockRestServiceServer.createServer(restTemplate);
+ this.restTemplate = restTemplate;
+ }
+
+ @Test
+ public void shouldLogin() {
+
+ MockLowLevelHttpResponse response = new MockLowLevelHttpResponse();
+ response.setStatusCode(200);
+ response.setContent("{\"keyId\":\"keyid\", \"signedJwt\":\"my-jwt\"}");
+
+ mockHttpTransport = new MockHttpTransport.Builder().setLowLevelHttpResponse(
+ response).build();
+
+ mockRest.expect(requestTo("/auth/gcp/login"))
+ .andExpect(method(HttpMethod.POST))
+ .andExpect(jsonPath("$.role").value("dev-role"))
+ .andExpect(jsonPath("$.jwt").value("my-jwt"))
+ .andRespond(
+ withSuccess()
+ .contentType(MediaType.APPLICATION_JSON)
+ .body("{"
+ + "\"auth\":{\"client_token\":\"my-token\", \"renewable\": true, \"lease_duration\": 10}"
+ + "}"));
+
+ PrivateKey privateKeyMock = mock(PrivateKey.class);
+ GoogleCredential credential = new Builder().setServiceAccountId("hello@world")
+ .setServiceAccountProjectId("foobar")
+ .setServiceAccountPrivateKey(privateKeyMock)
+ .setServiceAccountPrivateKeyId("key-id").build();
+ credential.setAccessToken("foobar");
+
+ GcpIamAuthenticationOptions options = GcpIamAuthenticationOptions.builder()
+ .role("dev-role").credential(credential).build();
+ GcpIamAuthentication authentication = new GcpIamAuthentication(options,
+ restTemplate, mockHttpTransport);
+
+ VaultToken login = authentication.login();
+
+ assertThat(login).isInstanceOf(LoginToken.class);
+ assertThat(login.getToken()).isEqualTo("my-token");
+
+ LoginToken loginToken = (LoginToken) login;
+ assertThat(loginToken.isRenewable()).isTrue();
+ assertThat(loginToken.getLeaseDuration()).isEqualTo(Duration.ofSeconds(10));
+ }
+}
diff --git a/spring-vault-dependencies/pom.xml b/spring-vault-dependencies/pom.xml
index 1801b163..d4af056d 100644
--- a/spring-vault-dependencies/pom.xml
+++ b/spring-vault-dependencies/pom.xml
@@ -124,6 +124,21 @@
true
+
+
+ com.google.apis
+ google-api-services-iam
+ v1-rev233-1.23.0
+ true
+
+
+
+ com.google.auth
+ google-auth-library-oauth2-http
+ 0.9.0
+ true
+
+
diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc
index 581d2614..ff8b8856 100644
--- a/src/main/asciidoc/new-features.adoc
+++ b/src/main/asciidoc/new-features.adoc
@@ -1,6 +1,10 @@
[[new-features]]
== New & Noteworthy
+[[new-features.2-1-0]]
+=== What's new in Spring Vault 2.1
+* <> and <> authentication.
+
[[new-features.2-0-0]]
=== What's new in Spring Vault 2.0
diff --git a/src/main/asciidoc/reference/authentication.adoc b/src/main/asciidoc/reference/authentication.adoc
index e9641b70..b7ae16bd 100644
--- a/src/main/asciidoc/reference/authentication.adoc
+++ b/src/main/asciidoc/reference/authentication.adoc
@@ -375,6 +375,112 @@ See also:
* https://www.vaultproject.io/docs/auth/aws.html[Vault Documentation: Using the AWS auth backend]
* http://docs.aws.amazon.com/STS/latest/APIReference/API_GetCallerIdentity.html[AWS Documentation: STS GetCallerIdentity]
+[[vault.authentication.gcpgce]]
+== GCP-GCE authentication
+
+The https://www.vaultproject.io/docs/auth/gcp.html[gcp]
+auth backend allows Vault login by using existing GCP (Google Cloud Platform) IAM and GCE credentials.
+
+GCP GCE (Google Compute Engine) authentication creates a signature in the form of a
+JSON Web Token (JWT) for a service account. A JWT for a Compute Engine instance
+is obtained from the GCE metadata service using https://cloud.google.com/compute/docs/instances/verifying-instance-identity[Instance identification].
+This API creates a JSON Web Token that can be used to confirm the instance identity.
+
+Unlike most Vault authentication backends, this backend
+does not require first-deploying, or provisioning security-sensitive
+credentials (tokens, username/password, client certificates, etc.).
+Instead, it treats GCP as a Trusted Third Party and uses the
+cryptographically signed dynamic metadata information that uniquely
+represents each GCP service account.
+
+====
+[source,java]
+----
+@Configuration
+class AppConfig extends AbstractVaultConfiguration {
+
+ // …
+
+ @Override
+ public ClientAuthentication clientAuthentication() {
+
+ GcpComputeAuthenticationOptions options = GcpComputeAuthenticationOptions.builder()
+ .role(…).build();
+
+ GcpComputeAuthentication authentication = new GcpComputeAuthentication(options,
+ restOperations());
+ }
+
+ // …
+}
+----
+====
+
+`GcpIamAuthenticationOptions` requires the Google Cloud Java SDK dependency
+(`com.google.apis:google-api-services-iam` and `com.google.auth:google-auth-library-oauth2-http`)
+as the authentication implementation uses Google APIs for credentials and JWT signing.
+
+You can configure the authentication via `GcpIamAuthenticationOptions`.
+
+See also:
+
+* https://www.vaultproject.io/docs/auth/gcp.html[Vault Documentation: Using the GCP auth backend]
+* https://cloud.google.com/compute/docs/instances/verifying-instance-identity[GCP Documentation: Verifying the Identity of Instances]
+
+[[vault.authentication.gcpiam]]
+== GCP-IAM authentication
+
+The https://www.vaultproject.io/docs/auth/gcp.html[gcp]
+auth backend allows Vault login by using existing GCP (Google Cloud Platform) IAM and GCE credentials.
+
+GCP IAM authentication creates a signature in the form of a JSON Web Token (JWT)
+for a service account. A JWT for a service account is obtained by
+calling GCP IAM's https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts/signJwt[`projects.serviceAccounts.signJwt`] API. The caller authenticates against GCP IAM
+and proves thereby its identity. This Vault backend treats GCP as a Trusted Third Party.
+
+IAM credentials can be obtained from either the runtime environment
+or supplied externally as e.g. JSON. JSON is the preferred form as it
+carries the project id and service account identifier required for calling
+``projects.serviceAccounts.signJwt``.
+
+====
+[source,java]
+----
+@Configuration
+class AppConfig extends AbstractVaultConfiguration {
+
+ // …
+
+ @Override
+ public ClientAuthentication clientAuthentication() {
+
+ GcpIamAuthenticationOptions options = GcpIamAuthenticationOptions.builder()
+ .role(…).credential(GoogleCredentials.getApplicationDefault()).build();
+
+ GcpIamAuthentication authentication = new GcpIamAuthentication(options,
+ restOperations());
+ }
+
+ // …
+}
+----
+====
+
+`GcpIamAuthenticationOptions` requires the Google Cloud Java SDK dependency
+(`com.google.apis:google-api-services-iam` and `com.google.auth:google-auth-library-oauth2-http`)
+as the authentication implementation uses Google APIs for credentials and JWT signing.
+
+You can configure the authentication via `GcpIamAuthenticationOptions`.
+
+NOTE: Google credentials require an OAuth 2 token maintaining the token lifecycle. All API
+is synchronous therefore, `GcpIamAuthentication` does not support `AuthenticationSteps` which is
+required for reactive usage.
+
+See also:
+
+* https://www.vaultproject.io/docs/auth/gcp.html[Vault Documentation: Using the GCP auth backend]
+* https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts/signJwt[GCP Documentation: projects.serviceAccounts.signJwt][[vault.authentication.gcpiam]]
+
[[vault.authentication.clientcert]]
== TLS certificate authentication