Access google photos API via Java - java

I am very new to google API and I am having troubles with it. I red documentation Google photos API for Java, then I created OAuth credentials in google API console and downloaded it (credentials.json file).
After that I tried to access google photos. Here is code from documentation:
// Set up the Photos Library Client that interacts with the API
PhotosLibrarySettings settings =
PhotosLibrarySettings.newBuilder()
.setCredentialsProvider(
FixedCredentialsProvider.create(/* Add credentials here. */))
.build();
try (PhotosLibraryClient photosLibraryClient =
PhotosLibraryClient.initialize(settings)) {
// Create a new Album with at title
Album createdAlbum = photosLibraryClient.createAlbum("My Album");
// Get some properties from the album, such as its ID and product URL
String id = album.getId();
String url = album.getProductUrl();
} catch (ApiException e) {
// Error during album creation
}
But I don't understand how to create Credentials object to pass it to the FixedCredentialsProvider.create() method
Could you please provide me with some explanation/links about it?

You can create a UserCredentials Object and pass it
UserCredentials.newBuilder()
.setClientId("your client id")
.setClientSecret("your client secret")
.setAccessToken("Access Token")
.build()
Go through this answer
https://stackoverflow.com/a/54533855/6153171
Or check out this complete project on github
https://github.com/erickogi/AndroidGooglePhotosApi

The FixedCredentialsProvider.create(..) call takes in a com.google.auth.Credentials object. For the Google Photos Library API, this should be a UserCredentials object, that you can create UserCredentials.Builder that is part of the Google OAuth library. There you set the refresh token, client ID, client secret, etc. to initialise the credentials. Getting a refresh token requires your app to complete the
GoogleAuthorizationCodeFlow that prompts the user for authorization and approval.
You can check out the sample implementation on GitHub, but this is the relevant code:
GoogleAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow.Builder(
GoogleNetHttpTransport.newTrustedTransport(),
JSON_FACTORY,
clientSecrets,
selectedScopes)
.setDataStoreFactory(new FileDataStoreFactory(DATA_STORE_DIR))
.setAccessType("offline")
.build();
LocalServerReceiver receiver =
new LocalServerReceiver.Builder().setPort(LOCAL_RECEIVER_PORT).build();
Credential credential = new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
return UserCredentials.newBuilder()
.setClientId(clientId)
.setClientSecret(clientSecret)
.setRefreshToken(credential.getRefreshToken())
.build();
There are a few moving parts involved, but the Google Photos Library API client library works with the Google Authentication library to handle OAuth authentication.

You need to understand about OAuth 2 first:
https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2
Then you can look this answer https://stackoverflow.com/a/54533855/6153171
P/s:
1. Grant Type: Authorization Code
Google developer console : credential is web client.
GoogleApiClient -> addScope, requestServerCode -> grand permission -> get account -> getServerAuthenCode -> get AccessToken (https://stackoverflow.com/a/54533855/6153171)
-> I tested and it works well. You can follow this way
Grant Type: Implicit
Google developer console : credential is android or ios app.
GoogleApiClient -> addScope, requestTokenID -> grand permission -> get account -> getTokenID -> get AccessToken. I didn't try successfully to grant authorization for google photo api. But with firebase authentication, we can use this way because firebase support class util for us.

Related

Use access token from Keycloak social login to consume Google API in Java

I have 2 pieces of code I want to integrate.
I have a working Google API (Youtube) integration that gets some user related information using the correct scopes while asking for authentication.
public YouTube getService() throws GeneralSecurityException, IOException {
final NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
return new YouTube.Builder(httpTransport, JSON_FACTORY, getCredentials())
.setApplicationName(APPLICATION_NAME)
.build();
}
private Credential getCredentials() throws IOException {
// With the GoogleAuthorizationCodeFlow you can create a Credential
Credential credential = authorizationCodeFlow.loadCredential(userService.getUserWithAuthorities().get().getId().toString());
if (credential == null) {
GoogleAuthorizationCodeRequestUrl authorizationUrl = authorizationCodeFlow.newAuthorizationUrl();
Map<String, String> state = Map.of("userId", userService.getUserWithAuthorities().get().getId().toString());
authorizationUrl.setState(new ObjectMapper().writeValueAsString(state));
authorizationUrl.setRedirectUri(CALLBACK_URL);
System.out.println("REDIRECTURL: " + authorizationUrl); //ToDo: remove
throw new YtUnauthorizedException(authorizationUrl.toString());
}
return credential;
}
I have a spring security / keycloack integration with social login using jhipster generated code.
Now I want to use the Google API code with the access token from Spring Security / Keycloak but none of the options I tried worked.
Following this article
I tried this code:
GoogleCredential credential = new GoogleCredential().setAccessToken(getAccessToken());
private String getAccessToken(){
Authentication authentication =
SecurityContextHolder
.getContext()
.getAuthentication();
OAuth2AuthenticationToken oauthToken =
(OAuth2AuthenticationToken) authentication;
OAuth2AuthorizedClient client =
clientService.loadAuthorizedClient(
oauthToken.getAuthorizedClientRegistrationId(),
oauthToken.getName());
return client.getAccessToken().getTokenValue();
}
But it doesn't work. I suspect I'm getting the access token of Keycloak but not Google. Any idea how I can use Google API with this setup?
Google does not know about your Keycloak instance and won't accept the access-tokens it emitted.
When you "login with Google" against a Keycloak instance, it checks an ID token emitted by Google and creates its own set of tokens (access, refresh and ID). Just open one of this tokens in https://jwt.io (or introspect it if it is opaque) and check the iss and aud claim values. This new tokens are to be used on your own applications (ID & refresh on clients, access on resource-servers), not on Google API.
To query Google API, two cases:
the request is not done on behalf of a specific user (you registered a Google client app that sends requests in its own name) => configure the REST client in your back-end with client-credentials so that it gets a fresh access-token before emitting requests to Google
the request is made on behalf of the end-user => you need an access-token for that user => request it from your client app (this could even be silent as your user already identified against Google) and send the request from the client directly or send Google access-token in a request body to your resource-server so that it can set its own request to with this "Google" token as authorization header

Status code 403 when trying to access DoubleClick Bid Manager Partner specific private buckets from google cloud java project

Hi all,
The current situation is as follows:
I currently have a google cloud project. The account that I log into the google cloud project with can also log into a DoubleClick bid Manager account. My aim is to use the DoubleClick Bid Manager api to retrieve certain buckets stored by DBM and save them in my separate google cloud project.
So far i can access the public buckets (gdbm-public) and pull and download the data, however when I try to access the partner specific buckets the same way, i.e. (gdbm-201032-201843) I get a status code 403.
Upon Reading the documentation here, I have discovered that I need to add a google group to the DBM partner information On DBM itself. However when i try to add a google group and save the changes i get an error saying the changes cannot be saved.
This is where i authenticate:
return new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId("<service_account_i_cant_show_here>")
.setServiceAccountScopes(Collections.singleton(StorageScopes.DEVSTORAGE_READ_ONLY))
.setServiceAccountPrivateKeyFromP12File(new File("secret-privatekey.p12"))
.build();
I then try to access the bucket like this:
String bucketName = "gdbm-201032-201843";
GoogleCredential credentials = getCredentials();
Storage storage = new Storage(HTTP_TRANSPORT, JSON_FACTORY, credentials);
Storage.Objects.List listObjects = storage.objects().list(bucketName);
Objects objects;
do {
objects = listObjects.execute();
for (StorageObject object : objects.getItems()) {
System.out.println(object);
}
listObjects.setPageToken(objects.getNextPageToken());
} while (null != objects.getNextPageToken());
More specifically, listObjects.execute() is where the 403 is thrown.
The areas I am trying to edit are Log Read Google Group and Log Management Google Group in the partner itself.
Any help greatly appreciated, thanks!
I think i have a solution, I used a different means of authentication as found here.
Using the linked class i entered in my google cloud project client credentials and the user that i log into both the google cloud project, and DBM.
I also changed the scope to "https://www.googleapis.com/auth/cloud-platform" however i am not 100% sure this had an effect on the outcome.
Code example:
// Scopes for the OAuth token
private static final Set<String> SCOPES =
ImmutableSet.of("https://www.googleapis.com/auth/cloud-platform");
public static Credential getUserCredential() throws Exception {
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(
JSON_FACTORY,
new InputStreamReader(SecurityUtilities.class.
getResourceAsStream("/client_secret.json")));
dataStoreFactory = new FileDataStoreFactory(DATA_STORE_DIR);
// set up authorization code flow.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(dataStoreFactory)
.build();
// authorize and get credentials.
return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver())
.authorize("<Personal user account>");
So instead of using a service account i used a personal account.

(Android/Java) Google Sheets v4 isUserRecoverableError status: NEED_PERMISSION

I am currently trying to simply delete a row off my Google Spreadsheet using the Google Sheets v4 API.
Here is the code I am using:
private void deleteRow()
{
List<Request> requests = new ArrayList<>();
DeleteDimensionRequest deleteDimensionRequest = new DeleteDimensionRequest();
DimensionRange dimensionRange = new DimensionRange();
dimensionRange.setStartIndex(14);
dimensionRange.setEndIndex(15);
deleteDimensionRequest.setRange(dimensionRange);
requests.add(new Request()
.setDeleteDimension(deleteDimensionRequest)
);
BatchUpdateSpreadsheetRequest batchUpdateRequest = new BatchUpdateSpreadsheetRequest()
.setRequests(requests);
try
{
mService.spreadsheets().batchUpdate("Spreadsheet_ID", batchUpdateRequest).execute();
}
catch(IOException e)
{
e.printStackTrace();
}
}
The error this function gives me is:
08-14 15:47:10.818 26956-27285/com.xxx.xxxxx.xxxxxxxx
W/GoogleAuthUtil: isUserRecoverableError status: NEED_PERMISSION
In my other class file, I've already indicated the scopes of permissions including drive and spreadsheets.
Here is the picture of the error:
In the java quickstart...
public static Credential authorize() throws IOException {
// Load client secrets.
InputStream in =
SheetsQuickstart.class.getResourceAsStream("/client_secret.json");
GoogleClientSecrets clientSecrets =
GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
// Build flow and trigger user authorization request.
GoogleAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(DATA_STORE_FACTORY)
.setAccessType("offline")
.build();
Credential credential = new AuthorizationCodeInstalledApp(
flow, new LocalServerReceiver()).authorize("user");
System.out.println(
"Credentials saved to " + DATA_STORE_DIR.getAbsolutePath());
return credential;
}
Is this the sort of oath credential in addition to the one provided by the android quickstart that I need to include?
Based from the given JSON response, encountered error is due to insufficient authentication scope. You can try to check required OAuth 2.0 scope information for the Google Sheets API as given in Authorizing requests with OAuth 2.0.
Please note that requests to the Google Sheets API for non-public user data must be authorized by an authenticated user. Likewise, if an application needs to create spreadsheets, or otherwise manipulate their metadata, then the application must also request a Google Drive API scope.
Additionally, the solution given in this SO post - 403 Forbidden error when accessing Google Drive API downloadURL regarding error code 403 might also help.
The problem is likely that you are loaded stored credentials with the insufficient scopes, and you need to delete your stored credentials and authorize access again.
You code appears to be adapted from the Java quickstart, which requests a readonly scope. Trying to use that scope for a write operation would fail with this error.
Since there is not one single question relating to managing sheets with an android application, here is the way to request an additional OATH 2.0 from scratch, which took me a few hours to understand:
Strategy to get the right additional authorization...
https://developers.google.com/identity/protocols/OAuth2InstalledApp#libraries
Diagram:
All of these requests can be handled with some imports such as HttpUrlConnection... within AsyncTask.
The list of scopes...
https://developers.google.com/sheets/guides/authorizing

Upload videos to Youtube from my web server in Java

My goal is to upload videos that are uploaded to my web server to Youtube on my own channel, not the users' Youtube account (my web server is acting as a proxy).
I found the sample code for uploading video to Youtube here with the credential acquired this way. The problem that I have with this sample is that it writes to disk the credential, and it opens an http server. Since my web server can potentially have a lot of users uploading their videos concurrently, the credential file location has to be dynamic, and multiple binding to the same http port is not possible. Further more, after searching through other writing about uploading to Youtube, I think this approach is for users uploading to their Youtube account.
Could you share your experiences/code sample/solutions for my scenario? In short I am just trying to automate the process of me opening up Youtube dashboard, and uploading videos to a channel in my Youtube.
In general, starting at API V3, Google prefers OAuth2 over other mechanism, and uploading a video (or any other action that modifies user data) requires OAuth2.
Fortunately, there is a special kind of token called refresh token to the rescue. Refresh token does not expire like normal access token, and is used to generate normal access token when needed. So, I divided my application into 2 parts:
The 1st part is for generating refresh token, which is a Java desktop app, meant to be run by a user on a computer. See here for sample code from Google.
The 2nd part is is part of my web application, which uses a given refresh token to create a credential object.
Here is my implementation in Scala, which you can adapt to Java version easily:
For generating a refresh token, you should set the accessType to offline for the authorization flow. Note: if a token already exists on your system, it won't try to get new token, even if it does not have refresh token, so you also have to set approval prompt to force:
def authorize(dataStoreName: String, clientId: String, clientSecret: String): Credential = {
val builder = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT,
JSON_FACTORY,
clientId,
clientSecret,
Seq(YouTubeScopes.YOUTUBE_UPLOAD)
)
val CREDENTIAL_DIRECTORY = s"${System.getProperty("user.home")}/.oauth-credentials"
val fileDataStoreFactory = new FileDataStoreFactory(new java.io.File(CREDENTIAL_DIRECTORY))
val dataStore: DataStore[StoredCredential] = fileDataStoreFactory.getDataStore(dataStoreName)
builder.setCredentialDataStore(dataStore).setAccessType("offline").setApprovalPrompt("force")
val flow = builder.build()
val localReceiver = new LocalServerReceiver.Builder().setPort(8000).build()
new AuthorizationCodeInstalledApp(flow, localReceiver).authorize("user")
}
val credential = authorize(dataStore, clientId, clientSecret)
val refreshToken = credential.getRefreshToken
For using the refresh token on the server, you can build a credential from a refresh token:
def getCredential = new GoogleCredential.Builder()
.setJsonFactory(JSON_FACTORY)
.setTransport(HTTP_TRANSPORT)
.setClientSecrets(clientId, clientSecret)
.build()
.setRefreshToken(refreshToken)
I have have bypassed the whole AuthorizationCodeInstalledApp authorize() method and created a new subclass which bypasses the jetty server implementation process.
The methods are as follows
getAuthorizationFromStorage : Get access token from stored credentials.
getAuthorizationFromGoogle : Get the authentication with the credentials from Google creates the url that will lead the user to the authentication page and creating a custom defined name-value pair in the state parameter. The value should be encoded with base64 encoder so we can receive the same code redirected from google after authentication.
saveAuthorizationFromGoogle : Save the credentials that we get from google.
Create the GoogleAuthorizationCodeFlow object from the credentialDatastorfrom the response received from the google after authentication.
Hit google to get the permanent refresh-token that can be used to get the accesstoken of the user any time .
Store the tokens like accesstoken and refreshtoken in the filename as userid
Checkout the code Implementation here

NoLinkedYoutubeAccount error 401 when uploading videos on Youtube with java

i would like to upload videos on youtube from my java web application:
I want to receive (server side) my authToken and form-action so to upload a video.
I've created a new google/youtube account
I've created a youtube channel in my account
i've created a new project in my google console api as a Service Account
Now i'm trying to implement my code with oAuth 2.0.
I've got the accessToken but, when i try to call the service getFormUploadToken(url, object) the response is always the same "NoLinkedYoutubeAccount error 401".
i've also verified the account by the google support page http://www.youtube.com/my_account_unlink but it's seems ok.
Does anybody have an idea about this problem?
This is my code:
HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
JsonFactory JSON_FACTORY = new JacksonFactory();
String accessToken = "";
GoogleCredential credential = null;
credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(CLIENT_EMAIL)
.setServiceAccountScopes("http://gdata.youtube.com")
.setServiceAccountPrivateKeyFromP12File(new File(PRIVATE_KEY_PATH))
.build();
credential.refreshToken();
accessToken = credential.getAccessToken();
YouTubeService service = new YouTubeService(CLIENT_ID, DEV_KEY);
service.setAuthSubToken(accessToken, null);
VideoEntry newEntry = new YouTubeMediaGroup mg = newEntry.getOrCreateMediaGroup();
mg.setTitle(new MediaTitle());
mg.getTitle().setPlainTextContent("My Test Movie");
URL uploadUrl = new URL("http://gdata.youtube.com/action/GetUploadToken");
FormUploadToken token = service.getFormUploadToken(uploadUrl, newEntry);
Thanks a lot,
Albert III
As the error say, you have not linked the account credential to youtube.
You can solve that problem:
- Connecting with the account used on the app in www.youtube.com
- Trying to upload a video
- Setting name and surname when youtube ask, and process to link to gmail.
Done!!! I tried before using my app to upload the video using Firefox.
You can't associate a YouTube channel with a Service Account, and you're trying to upload under the context of the Service Account.
Instead of using Service Accounts, you should go through the OAuth 2 flow and authorize access while logged in to the browser using the account that you actually want to upload into.
I understand the benefits of using Service Accounts, but YouTube is not set up to work with them at this time.
If you have created youtube account and if has been linked to your google account, then make sure you create your own channel in your youtube.
This will allow the API to upload videos to your created channel.
Hope it helps.

Categories