Google API authentication in Java without using environment variable - java

I have set up a simple test app to interact with Google's Natural Language API. I created a service account, and downloaded the JSON credentials. I am running on a local development machine, so I set the GOOGLE_APPLICATION_CREDENTIALS environment variable to point to the JSON file. To be clear, this works: the app successfully makes some API calls and displays the results.
I would like to remove the dependence on the environment variable. How can I use the known location of the JSON file (or any other approach) in the application to create the LanguageServiceClient with those credentials?

You can register is like this:
DatastoreOptions options = DatastoreOptions.newBuilder()
.setProjectId(PROJECT_ID)
.setAuthCredentials(AuthCredentials.createForJson(
new FileInputStream(PATH_TO_JSON_KEY))).build();
Does that help?

You can always pass the full json file as String as follows:
CredentialsProvider credentialsProvider;
String credentials = "[YOUR JSON FILE CONTENT]";
try {
credentialsProvider
= FixedCredentialsProvider.create(
ServiceAccountCredentials.fromStream(new ByteArrayInputStream(credentials.getBytes())));
} catch (IOException ex) {
Logger.getLogger(GoogleNLPService.class.getName()).log(Level.SEVERE, null, ex);
}
LanguageServiceSettings.Builder languageServiceSettingsBuilder
= LanguageServiceSettings.newBuilder();
LanguageServiceSettings languageServiceSettings = languageServiceSettingsBuilder.setCredentialsProvider(credentialsProvider).build();
List<NamedEntity> entities = new ArrayList<>();
try (LanguageServiceClient language = LanguageServiceClient.create(languageServiceSettings)) {
...
}
Alternatively, you can place your json file in resources folder and then read it as:
credentialsProvider
= FixedCredentialsProvider.create(
ServiceAccountCredentials.fromStream(new FileInputStream("./src/main/resources/FILENAME.json")));
However, this relative path didn't work when I uploaded my app in Heroku. So, I have decided to use the String solution.

We use a service account + GoogleCredential.Builder -- (note that this example uses a credential file in p12 format); example follows:
private GoogleCredential authorize() throws IOException, GeneralSecurityException
{
return new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(serviceAccount)
.setServiceAccountScopes(SCOPES)
.setServiceAccountUser(serviceAccountUser)
// variable p12File is a String w/ path to the .p12 file name
.setServiceAccountPrivateKeyFromP12File(new java.io.File(p12File))
.build();
}

Following the advice from tokyohans answer above, I can confirm this works for LanguageServiceClient:
// old-school Google Authentication
GoogleCredential credential = null;
credential = GoogleCredential.fromStream(new FileInputStream("google.json"));
Collection<String> scopes = Collections.singleton("https://www.googleapis.com/auth/cloud-language");
if (credential.createScopedRequired()) {
credential = credential.createScoped(scopes);
}
// copy over key values, note the additional "s", set some expiry
// com.google.auth.oauth2.GoogleCredentials
GoogleCredentials sac = ServiceAccountCredentials.newBuilder()
.setPrivateKey(credential.getServiceAccountPrivateKey())
.setPrivateKeyId(credential.getServiceAccountPrivateKeyId())
.setClientEmail(credential.getServiceAccountId())
.setScopes(scopes)
.setAccessToken(new AccessToken(credential.getAccessToken(), new LocalDate().plusYears(1).toDate()))
.build();
// Latest generation Google libs, GoogleCredentials extends Credentials
CredentialsProvider cp = FixedCredentialsProvider.create(sac);
LanguageServiceSettings settings = (LanguageServiceSettings) LanguageServiceSettings.newBuilder().setCredentialsProvider(cp).build();
return LanguageServiceClient.create(settings);

This looks like an older thread but sharing our findings for what it's worth.
This example is for the Google ImageAnnotatorClient, but I am pretty sure it is very similar for LanguageServiceClient.
Old-school google libraries (P12 file era) use GoogleCredential vs. the new GoogleCredentials. They look very similar. Digging into the Type hierarchy I found a FixedCredentialsProvider which seems to do the trick.
This worked for us, we got the Google Vision API up and running with an existing P12 file without environment variable. It does look like Google wants us to migrate away from this so not recommending this approach long-term.
// old-school Google Authentication
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
// Spring code
String pemFile = "yourPemFile.p12";
Resource r = new ClassPathResource(pemFile);
String serviceAccountEmail = "xxxx#xxxx.iam.gserviceaccount.com";
// com.google.api.client.googleapis.auth.oauth2.GoogleCredential.Builder
Builder credentialBuilder = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(serviceAccountEmail)
.setServiceAccountPrivateKeyFromP12File(r.getFile());
// Cloud API endpoints, make sure that the API is enabled
Collection<String> scopes = Collections.singleton("https://www.googleapis.com/auth/cloud-vision");
GoogleCredential credential = credentialBuilder
.setServiceAccountScopes(scopes).build();
// copy over key values, note the additional "s", set some expiry
// com.google.auth.oauth2.GoogleCredentials
GoogleCredentials sac = ServiceAccountCredentials.newBuilder()
.setPrivateKey(gc.getServiceAccountPrivateKey())
.setPrivateKeyId(gc.getServiceAccountPrivateKeyId())
.setClientEmail(gc.getServiceAccountId())
.setScopes(scopes)
.setAccessToken(new AccessToken(gc.getAccessToken(), new LocalDate().plusYears(1).toDate()))
.build();
// Latest generation Google libs, GoogleCredentials extends Credentials
CredentialsProvider cp = FixedCredentialsProvider.create(sac);
ImageAnnotatorSettings settings = ImageAnnotatorSettings.newBuilder().setCredentialsProvider(cp).build();
ImageAnnotatorClient googleApi = ImageAnnotatorClient.create(settings);

Related

How do you access a Google Sheet with a service account from Java?

I'm trying to access a Google Sheet from a service account (the service account has access to the sheet). But I have a problem, all the documentation including the official docs point to use GoogleCredential that is deprecated since 1.7. The deprecation says that you sould use library google-auth-library and this is what I'm doing in:
val googleCredentials = ServiceAccountCredentials
.fromStream(new FileInputStream(CREDENTIALS_FILE_PATH))
.createScoped(SCOPES)
googleCredentials.refresh()
but the variable is of type GoogleCredentials and the function Sheets.Builder doesn't accept this as a valid HttpRequestInitializer. So I had to hack my way around using the deprecated class with:
val token = googleCredentials.getAccessToken.getTokenValue
val credential = new GoogleCredential().setAccessToken(token)
With this workaround I'm able to access the Google Sheet but I'm sure that there is a proper way to do it without using deprecated libraries.
Does anyone know what's the proper way to do it?
Leaving here the full snippet as a reference.
package drivers
import java.io.FileInputStream
import java.util.Collections
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport
import com.google.api.client.json.jackson2.JacksonFactory
import com.google.api.services.sheets.v4.{Sheets, SheetsScopes}
import com.google.auth.oauth2.ServiceAccountCredentials
import com.typesafe.config.ConfigFactory
object TestDriver {
def main(args: Array[String]): Unit = {
val config = ConfigFactory.load()
val spreadsheetId = config.getString("spreadsheetId")
val CREDENTIALS_FILE_PATH = config.getString("google_application_credentials")
val SCOPES = Collections.singletonList(SheetsScopes.SPREADSHEETS_READONLY)
val JSON_FACTORY = JacksonFactory.getDefaultInstance
val APPLICATION_NAME = "my-app"
val googleCredentials = ServiceAccountCredentials
.fromStream(new FileInputStream(CREDENTIALS_FILE_PATH))
.createScoped(SCOPES)
googleCredentials.refresh()
val token = googleCredentials.getAccessToken.getTokenValue
val credential = new GoogleCredential().setAccessToken(token)
val httpTransport = GoogleNetHttpTransport.newTrustedTransport
val service = new Sheets.Builder(httpTransport, JSON_FACTORY, credential)
.setApplicationName(APPLICATION_NAME)
.build()
val range = "Sheet1!A1:D1"
val response = service.spreadsheets().values().get(spreadsheetId, range).execute()
val values = response.getValues
values.forEach(println)
}
The information is confusing, the Google Auth Library that is recommended to use since the deprecation includes the google-auth-library-oauth2 library
In other words, you can still use GoogleCredential, and it is still the method recommended in the documentation:
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream(CREDENTIALS_FILE_PATH))
.createScoped(Collections.singleton(SCOPES));
Alternatively, you can also build your credential from a .p12 file:
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(emailAddress)
.setServiceAccountPrivateKeyFromP12File(new File("MyProject.p12"))
.setServiceAccountScopes(Collections.singleton(YOUR SCOPE))
.build();
However, if you do want to use GoogleCredentials and have problems with the HttpRequestInitializer you can use the HttpCredentialsAdapter

How to explicitly point my service account file in code

In other Google Services such as Storage, BigQuery you can define what service account you are going to use in the JAVA code:
// You can specify a credential file by providing a path to GoogleCredentials.
// Otherwise credentials are read from the GOOGLE_APPLICATION_CREDENTIALS environment variable.
GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream(jsonPath))
.createScoped(Lists.newArrayList("https://www.googleapis.com/auth/cloud-platform"));
Storage storage = StorageOptions.newBuilder().setCredentials(credentials).build().getService();
Using Google Secret Manager it seems not possibile. Why?
The only way is to set an environment variable on VM?
I tried as suggested to use Credentials Provider
GoogleCredentials credentials = ServiceAccountCredentials.fromStream(credentialsInputStream);
CredentialsProvider credentialsProvider = FixedCredentialsProvider.create(credentials);
SecretManagerServiceSettings settings = SecretManagerServiceSettings.newBuilder().setCredentialsProvider(credentialsProvider).build();
client = SecretManagerServiceClient.create(settings);
but it doesn't work
Caused by: java.lang.NoSuchMethodError: com.google.common.base.Preconditions.checkArgument(ZLjava/lang/String;CLjava/lang/Object;)V
at io.grpc.Metadata$Key.validateName(Metadata.java:742)
at io.grpc.Metadata$Key.<init>(Metadata.java:750)
at io.grpc.Metadata$Key.<init>(Metadata.java:668)
at io.grpc.Metadata$AsciiKey.<init>(Metadata.java:959)
at io.grpc.Metadata$AsciiKey.<init>(Metadata.java:954)
at io.grpc.Metadata$Key.of(Metadata.java:705)
at io.grpc.Metadata$Key.of(Metadata.java:701)
at com.google.api.gax.grpc.GrpcHeaderInterceptor.<init>(GrpcHeaderInterceptor.java:60)
at com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.createSingleChannel(InstantiatingGrpcChannelProvider.java:239)
at com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.access$1600(InstantiatingGrpcChannelProvider.java:71)
at com.google.api.gax.grpc.InstantiatingGrpcChannelProvider$1.createSingleChannel(InstantiatingGrpcChannelProvider.java:210)
at com.google.api.gax.grpc.ChannelPool.create(ChannelPool.java:72)
at com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.createChannel(InstantiatingGrpcChannelProvider.java:217)
at com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.getTransportChannel(InstantiatingGrpcChannelProvider.java:200)
at com.google.api.gax.rpc.ClientContext.create(ClientContext.java:156)
at com.google.cloud.secretmanager.v1.stub.GrpcSecretManagerServiceStub.create(GrpcSecretManagerServiceStub.java:237)
at com.google.cloud.secretmanager.v1.stub.SecretManagerServiceStubSettings.createStub(SecretManagerServiceStubSettings.java:226)
at com.google.cloud.secretmanager.v1.SecretManagerServiceClient.<init>(SecretManagerServiceClient.java:154)
at com.google.cloud.secretmanager.v1.SecretManagerServiceClient.create(SecretManagerServiceClient.java:135)
because of an Exception in the class com.google.cloud.secretmanager.v1.SecretManagerServiceClient
/**
* Constructs an instance of SecretManagerServiceClient, using the given settings. This is
* protected so that it is easy to make a subclass, but otherwise, the static factory methods
* should be preferred.
*/
protected SecretManagerServiceClient(SecretManagerServiceSettings settings) throws IOException {
this.settings = settings;
this.stub = ((SecretManagerServiceStubSettings) settings.getStubSettings()).createStub();
}
To customize credentials, you can create a custom secretManagerServiceSettings:
SecretManagerServiceSettings secretManagerServiceSettings =
SecretManagerServiceSettings.newBuilder()
.setCredentialsProvider(FixedCredentialsProvider.create(credentials))
.build();
SecretManagerServiceClient client =
SecretManagerServiceClient.create(secretManagerServiceSettings);

How to access Team Drive using service account with Google Drive API v3 Java

Good afternoon!
I am having trouble accessing google Team Drive.
I need to create folders and upload files there from the local storage, and all this should be done by my application.
At the moment, I have learned to connect to my personal Google Drive and upload files there. My code for connecting to personal storage:
Drive service = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential) //
.setApplicationName(APPLICATION_NAME).build();
// Print the names and IDs for up to 10 files.
FileList result = service.files().list().setPageSize(10).setFields("nextPageToken, files(id, name)").execute();
List<File> files = result.getFiles();
if (files == null || files.isEmpty()) {
System.out.println("No files found.");
} else {
System.out.println("Files:");
for (File file : files) {
System.out.printf("%s (%s)\n", file.getName(), file.getId());
}
}
Tell me, how can I connect to Google Team Drive in java and upload files there?
Thank you:)
Connecting to the team drive with a service account in java
Assuming that you already fulfilled the prerequisites, which are
Creating a Google Service account
Downloading its credentials
Either share the team drive with the service account OR enable domain-wide delegation to impersonate a user who has access to the team drive
Your code should look something like this:
private static final String APPLICATION_NAME = "YOUR APPLICATION";
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
private static final List < String > SCOPES = Collections.singletonList("XXXINSERTHEREYOURSCOPEXXXX");
public static void main(String...args) throws IOException, GeneralSecurityException {
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
File pk12 = new File("quickstartserv.p12");
String serviceAccount = "EMAIL FO YOUR SERVICE ACCOUNT.iam.gserviceaccount.com";
// Build service account credential.Builder necessary for the ability to refresh tokens
GoogleCredential getCredentials = new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(serviceAccount)
.setServiceAccountPrivateKeyFromP12File(pk12)
.setServiceAccountScopes(SCOPES)
.setServiceAccountUser("xxx") //IF YOU WANT TO IMPERSONATE A USER
.build();
// Build a new authorized API client service.
Drive service = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials)
.setApplicationName(APPLICATION_NAME)
.build();
FileList result = service.files().list().setPageSize(10).setQ('"ID OF THE SHARED DRIVE" in parents').setIncludeTeamDriveItems(true).setSupportsTeamDrives(true).setFields("nextPageToken, files(id, name)").execute();
...
}
Mind that setIncludeTeamDriveItems(true) and setSupportsTeamDrives(true) are necessary to retrieve files from the shared drive.

azure java sdk authentication

I would like to list available IP VM's in the new Azure portal using Java SDK.
Couple of years back in the good old classic portal, I had followed the usual management certificate procedure to access vm's,create vm's and work with Azure Endpoints.
Fast fwd now I see that they have used a new portal and new mechanisms to interact with Java SDK. I read somewhere in the above link that with the old way with certificates, I can manage only the class portal resources.
I'm trying to code a simple program which authenticates and lists the vm's of the new portal as a start. Seems like they have complicated it a lot.
I followed the below link to "Create service principal with password"
https://azure.microsoft.com/en-us/documentation/articles/resource-group-authenticate-service-principal/
Then I went to this link
https://azure.microsoft.com/en-us/documentation/samples/resources-java-manage-resource-group/
which asked me go the "See how to create an Auth file" link in above page
(mine is not a webapp and when I try to create the AD as a native client application, it is not allowing me to save keys in configure tab, so I had to create a web app)
After doing all this, I got stuck with this below error
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
'authority' Uri should have at least one segment in the path (i.e.https://<host>/<path>/...)
java.lang.IllegalArgumentException: 'authority' Uri should have at least one segment in the path (i.e. https://<host>/<path>/...)
at com.microsoft.aad.adal4j.AuthenticationAuthority.detectAuthorityType(AuthenticationAuthority.java:190)
at com.microsoft.aad.adal4j.AuthenticationAuthority.<init>(AuthenticationAuthority.java:73)
When I checked it says that the error is because I don't have a valid client application id in your Azure Active Directory.
Is there any simple way to authenticate and start using the API's?
#Vikram, I suggest that you can try to refer to the article to create an application on AAD.
Then you can follow the code below to get the access token for authentication.
// The parameters include clientId, clientSecret, tenantId, subscriptionId and resourceGroupName.
private static final String clientId = "<client-id>";
private static final String clientSecret = "<key>";
private static final String tenantId = "<tenant-id>";
private static final String subscriptionId = "<subscription-id>";
// The function for getting the access token via Class AuthenticationResult
private static AuthenticationResult getAccessTokenFromServicePrincipalCredentials()
throws ServiceUnavailableException, MalformedURLException, ExecutionException, InterruptedException {
AuthenticationContext context;
AuthenticationResult result = null;
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(1);
// TODO: add your tenant id
context = new AuthenticationContext("https://login.microsoftonline.com/" + tenantId, false, service);
// TODO: add your client id and client secret
ClientCredential cred = new ClientCredential(clientId, clientSecret);
Future<AuthenticationResult> future = context.acquireToken("https://management.azure.com/", cred, null);
result = future.get();
} finally {
service.shutdown();
}
if (result == null) {
throw new ServiceUnavailableException("authentication result was null");
}
return result;
}
String accessToken = getAccessTokenFromServicePrincipalCredentials().getAccessToken();
If you want to list the VMs on new portal, you can try to use the REST API List the resources in a subscription to get all resources and filter the VMs via the resource type Microsoft.Compute/virtualMachines.
Hope it helps.

How do I access a Google Spreadsheet from App Engine using a Service Account?

I have an application on Google App Engine (hosted on appspot.com) which is going to be used by a few users to grab some stuff off the internet, save it in the Datastore and then write it to a Google Spreadsheet. I'm looking to use a Service Account to access the spreadsheet but I run into a 400 OK { "error" : "invalid_grant" } problem when I try to do anything, regardless of whether I try to create a file using the Drive API or access an existing file using Spreadsheets API.
I've turned on both Drive API and Drive SDK in the APIs Console and generated a P12 key. This is my method for building a GoogleCredential:
private GoogleCredential getGoogleCredential() throws IOException, GeneralSecurityException {
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(TRANSPORT)
.setJsonFactory(FACTORY)
.setServiceAccountId("1511XXX90247.apps.googleusercontent.com")
.setServiceAccountScopes(scopes)
.setServiceAccountPrivateKeyFromP12File(
new File("05a605b0fXXXd2a6be864be15d81a2bd629d3bd6-privatekey.p12"))
.setServiceAccountUser("XXX#gmail.com") // My personal e-mail address which I used to create the project
.build();
return credential;
}
The ServiceAccountID comes from the APIs Console. I've tried using 1511XXX90247#developer.gserviceaccount.com as my ServiceAccountUser as well as myappname#appspot.gserviceaccount.com. The scopes I'm using are as follows:
https://spreadsheets.google.com/feeds
https://docs.google.com/feeds
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.file
Here is the code that fails when I try to use Spreadsheets API:
SpreadsheetService service = new SpreadsheetService("name of my app");
service.setOAuth2Credentials(getGoogleCredential());
FeedURLFactory factory = FeedURLFactory.getDefault();
SpreadsheetQuery query = new SpreadsheetQuery(factory.getSpreadsheetsFeedUrl());
query.setTitleQuery("test");
SpreadsheetFeed feed = service.query(query, SpreadsheetFeed.class);
The code fails when the service tries execute the query.
Here's the Drive code I tried that also fails:
Drive drive = new Drive.Builder(TRANSPORT, FACTORY,
getGoogleCredential()).build();
com.google.api.services.drive.model.File file = new com.google.api.services.drive.model.File();
file.setTitle("test");
file.setMimeType("application/vnd.google-apps.spreadsheet");
drive.files().insert(file).execute(); // This is where the code fails
No matter what I try, I always seem to end up with the same ìnvalid grant` error. Here's a partial stacktrace:
com.google.gdata.util.AuthenticationException: Failed to refresh access token: 400 OK { "error" : "invalid_grant" }
at com.google.gdata.client.GoogleAuthTokenFactory$OAuth2Token.refreshToken(GoogleAuthTokenFactory.java:260)
at com.google.gdata.client.GoogleAuthTokenFactory.handleSessionExpiredException(GoogleAuthTokenFactory.java:702)
at com.google.gdata.client.GoogleService.handleSessionExpiredException(GoogleService.java:738) at com.google.gdata.client.GoogleService.getFeed(GoogleService.java:680)
at com.google.gdata.client.Service.query(Service.java:1237)
at com.google.gdata.client.Service.query(Service.java:1178)
at spam.gwt.scraper.server.spreadsheets.APIConnector.doGet(APIConnector.java:66)
I've tried to look for answers everywhere, including but not limited to the Google Developers Spreadsheets Guide, this Service Account-related Stack Overflow question and this Google App Engine-specific Stack Overflow question. What with the developments (eg. Docs -> Drive) it's hard to find up-to-date info, never mind info that pertains specifically to GAE.
I use the builtin Service Account of the App Engine application rather than creating my own; this way the private key is save (not stored locally, only on app engine).
For Drive:
protected Drive createDriveService() throws BookingSheetException {
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
GoogleClientRequestInitializer keyInitializer =
new CommonGoogleClientRequestInitializer(API_KEY);
AppIdentityCredential credential =
new AppIdentityCredential.Builder(Arrays.asList(DriveScopes.DRIVE)).build();
Drive service = new Drive.Builder(httpTransport, jsonFactory, null)
.setHttpRequestInitializer(credential)
.setGoogleClientRequestInitializer(keyInitializer)
.setApplicationName(appname).build();
return service;
}
For Spreadsheet:
private Credential creds;
/**
* Keep the expiration time of the access token to renew it before expiry
*/
private Calendar expirationTime = Calendar.getInstance();
protected void initCredentials() {
List<String> scopes = Arrays.asList("https://spreadsheets.google.com/feeds");
AppIdentityService appIdentity = AppIdentityServiceFactory.getAppIdentityService();
AppIdentityService.GetAccessTokenResult accessToken = appIdentity.getAccessToken(scopes);
expirationTime.setTime(accessToken.getExpirationTime());
expirationTime.add(Calendar.MINUTE,-REFRESH_MINUTES); //refresh some time before expiry
creds = new Credential(BearerToken.authorizationHeaderAccessMethod());
creds.setAccessToken(accessToken.getAccessToken());
}
public SpreadsheetUtil(String appname) {
myService = new SpreadsheetService(appname);
myService.setOAuth2Credentials(cred);
}
Make sure to creds.refreshToken() or reinitialize when they expire.

Categories