
com.vendasta.common.v1.GoogleCredentialsManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of common.v1 Show documentation
Show all versions of common.v1 Show documentation
Common objects for java sdk
The newest version!
package com.vendasta.common.v1;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.util.Date;
import org.apache.commons.codec.binary.Base64;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.UrlEncodedContent;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.JsonObjectParser;
import com.google.api.client.json.JsonParser;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.json.webtoken.JsonWebSignature;
import com.google.api.client.json.webtoken.JsonWebToken;
import com.google.api.client.json.webtoken.JsonWebToken.Payload;
import com.google.api.client.util.Charsets;
import com.google.api.client.util.GenericData;
import com.google.auth.oauth2.ServiceAccountJwtAccessCredentials;
import com.google.common.io.CharStreams;
public class GoogleCredentialsManager implements CredentialsManager {
private String serviceAccount;
private String audience;
private String scope;
private String currentToken;
private Date currentTokenExpiry;
public GoogleCredentialsManager(InputStream serviceAccount, String audience, String scope) throws IOException {
this.serviceAccount = CharStreams.toString(new InputStreamReader(serviceAccount, Charsets.UTF_8));
this.audience = audience;
this.scope = scope;
this.currentTokenExpiry = null;
this.currentToken = null;
}
/**
* Talks to the Google IAM service to obtain a JWT using a Service Account.
* The result is cached until invalidated or expired.
*
* @throws CredentialsException
* when an error occurs when attempting to get an authorization
* token.
* @return an id token
*/
public String getAuthorization() throws CredentialsException {
Date currentTime = new Date();
if (currentTokenExpiry != null && currentTime.after(currentTokenExpiry)) {
refreshToken();
}
if (currentToken == null) {
refreshToken();
}
return currentToken;
}
private void refreshToken() throws CredentialsException {
URI audienceUri;
String jwtAccess;
try {
jwtAccess = this.getJWTAccessFromServiceAccount();
audienceUri = new URI(this.audience);
} catch (URISyntaxException e) {
throw new CredentialsException("Malformed URI prevented acquisition of a JWT", e);
} catch (IOException e) {
throw new CredentialsException("Something went wrong with reading credentials back", e);
}
GenericData tokenRequest = new GenericData();
tokenRequest.set("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");
tokenRequest.set("assertion", jwtAccess);
UrlEncodedContent content = new UrlEncodedContent(tokenRequest);
HttpRequestFactory requestFactory = new NetHttpTransport().createRequestFactory();
HttpRequest request;
try {
request = requestFactory.buildPostRequest(new GenericUrl(audienceUri), content);
} catch (IOException e) {
throw new CredentialsException("Error building credentials request", e);
}
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
request.setParser(new JsonObjectParser(jsonFactory));
HttpResponse response;
try {
response = request.execute();
} catch (IOException e) {
throw new CredentialsException("Error getting access token for service account", e);
}
try {
// Extract the JWT token from the response
GenericData responseData = response.parseAs(GenericData.class);
String jwtToken = (String) responseData.get("id_token");
// Decode the JWT to get the expiry
String[] tokens = jwtToken.split("\\.");
String claims = new String(this.Base64Decode(tokens[1]));
Payload payload = new JsonWebToken.Payload();
JsonParser parser = JacksonFactory.getDefaultInstance().createJsonParser(claims);
parser.parse(payload);
System.out.println(payload);
// Save the expiry and the token
currentToken = "Bearer " + jwtToken;
currentTokenExpiry = new Date(payload.getExpirationTimeSeconds() * 1000);
} catch (IOException e) {
throw new CredentialsException("Error parsing JSON response from google Credentials Server", e);
}
}
/**
* This is a helper function that uses the Service Account, audience, and
* scope to build a JWT used to request a token from Google's IAM service.
*
* @throws IOException
* Really, this should never happen because converting a string
* to an InputStream and reading it shouldn't fail :/
* @throws URISyntaxException
* Only happens if an invalid Audience URI is used.
*/
private String getJWTAccessFromServiceAccount() throws URISyntaxException, IOException {
InputStream is = new ByteArrayInputStream(serviceAccount.getBytes(Charsets.UTF_8));
ServiceAccountJwtAccessCredentials creds = ServiceAccountJwtAccessCredentials.fromStream(is);
URI audienceUri = new URI(this.audience);
JsonWebSignature.Header header = new JsonWebSignature.Header();
header.setAlgorithm("RS256");
header.setType("JWT");
header.setKeyId(creds.getPrivateKeyId());
JsonWebToken.Payload payload = new JsonWebToken.Payload();
long currentTime = System.currentTimeMillis();
// Both copies of the email are required
payload.setIssuer(creds.getClientEmail());
payload.setSubject(creds.getClientEmail());
payload.setAudience(audienceUri.toString());
payload.setIssuedAtTimeSeconds(currentTime / 1000);
payload.setExpirationTimeSeconds(currentTime / 1000 + 3600);
payload.put("scope", this.scope);
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
String jwtAccess;
try {
jwtAccess = JsonWebSignature.signUsingRsaSha256(creds.getPrivateKey(), jsonFactory, header, payload);
} catch (GeneralSecurityException e) {
throw new IOException("Error signing service account JWT access header with private key:", e);
}
return jwtAccess;
}
/**
* Used by clients to invalidate the current token for any reason other than
* expiry, which is handled by this CredentialManager.
*/
public void invalidateAuthorization() {
currentToken = null;
}
private String Base64Decode(String encoded) throws UnsupportedEncodingException {
byte[] decoded = Base64.decodeBase64(encoded.getBytes("UTF-8"));
return new String(decoded, "UTF-8");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy