Oauth2 timeout with google API - java

I'm using Oauth2 for authentication on Google Calendar API based based on the introductory quick start guide
I'm now running this on a web server and have set the callback port to 9999, which I have also entered in the GCP console.
I get the link in the log file and I am able to authenticate on the server and the application works fine. However, this seems timeout after around 20 mins and when I access the site the log file then gives me another URL to authenticate against. I am creating a calendar widget so once this is live I obviously do not want this to expire and I just want to set this up once.
The StoredCredential file is being created correctly on the server. I am running tomcat 7 so this file is user /usr/share/tomcat7/.credentials/
The app works fine when accessed from different IP addresses before it appears to 'timeout'
The code I am using for authenticating is below. According to the docs, the creation of the GoogleAuthorizationCodeFlow object with the DataStoreFactory for the FileCredentials should pick up that we have already authenticated.
The docs also state that "An access token typically has an expiration date of 1 hour, after which you will get an error if you try to use it. GoogleCredential takes care of automatically 'refreshing' the token, which simply means getting a new access token."
I have also tried setting the timeout manually on the credential object. Google docs state that setExpiresInSeconds() "Sets the lifetime in seconds of the access token (for example 3600 for an hour) or null for none." I have tried this both null, LONG.MAX, and 1 year. All of these result in the message "Please open the following address in your browser:" being shown in the logs after a period of about 45 minutes. Using the getExpiresInSeconds() method I can confirm that the timeout was set to my expected value.
The documentation also states that access_type should be set to offline in the GoogleAuthorizationCodeFlow, which I have shown I am doing below.
public static Credential authorize() throws IOException {
InputStream in = GoogleCalendarService.class.getClassLoader().getResourceAsStream("/client_secret.json");
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(DATA_STORE_DIR))
.setAccessType("offline")
.build();
Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver.Builder().setHost(REDIRECT_URI).setPort(9999).build()).authorize("user");
return credential;
}

For anyone who comes across this, my refresh token was actually Null. I revoked my token and had to set the approval prompt to be 'force' as below:
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(DATA_STORE_DIR))
.setAccessType("offline")
.setApprovalPrompt("force")
.build();
After creating the Credential using:
Credential credential = new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver.Builder().setHost(REDIRECT_URI).setPort(9999).build()).authorize("user");
I was then able to do:
credential.getRefreshToken()
Which showed that the token was non-null. The expiration time as default seems to be 3600 seconds and when the token has <60s left then the API automatically refreshes this. The app has now been running for 24 hours and the token has been refreshed successfully

Related

redirect_uri_mismatch and open with default uri with random port

I am working on a Spring-MVC application running on tomcat in which I would like to use Google Business Profile API functionality. I tried with an OAuth 2.0 Client ID on my local machine and I'm getting a redirect_uri_mismatch.
I don't understand one thing, I am setting the redirect URL in google console and in secret JSON, why on earth is it getting the redirect_url with random port numbers. If I change the port number in the browser URL, it works fine. I have set the port number
return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver.Builder().setPort(8083).build()).authorize("user");
But get an error Address already binded.
I have 2 to 3 days but no luck till yet.
// Creates an InputStream to hold the client ID and secret.
InputStream secrets =GoogleBusinessMsgsRestService.class.getClassLoader().getResourceAsStream("client_secret.json");
// Uses the InputStream to create an instance of GoogleClientSecrets.
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
new InputStreamReader(secrets));
// Sets up the authorization code flow.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
httpTransport, JSON_FACTORY, clientSecrets,
Collections.singleton("https://www.googleapis.com/auth/business.manage"))
.setDataStoreFactory(dataStoreFactory).build();
// Returns the credential.
return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver.Builder().build()).authorize("user");

Gmail API for JAVA - Eclipse project - Read new mails and download attachments

I work on student project where I have to use GMAIL API (Java), to connect to mail server, get new messages and download attachments if there is any. I already done this with JavaMail API, but mail server that should use app doesn't accept IMAP and POP3 protocols, it has own web service.
I have two problems.
I have troubles even with starting project in eclipse.
First, I have no idea how to set up connection with mail server (for example gmail account) and how to provide username and password. I saw that gmail api for authorization uses Oauth2.
Second one is maybe easier to solve. When I have established connection to mail server, how to fetch unseen mails and download attachments?
I guess that second part I could do on my own, but without
connection its even useless to try.
I was reading official documentation for few days and I am quite confused.
(Maybe if you have some code sample or similar example it would be nice)
EDIT 1:
Done that, now I have this error
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
.setAccessType("offline")
.build();
setAccessType is not found in com.google.api.client.auth.oauth2.AuthorizationCodeFlow.Builder
All Imports from Quickstart are there.
EDIT 2:
I am quite confused now
Exception in thread "main" java.io.FileNotFoundException: Resource not found: /resources/credentials.json
Line:
private static final String CREDENTIALS_FILE_PATH = "/resources/credentials.json";
File&Folder organization
When I check System.out.print(System.getProperty("user.dir"));
I get F:\NBS\eclipse\FetchMail
EDIT 3:
Deleted all files and started over again but eclipse still looks blind
File not found
EDIT 4:
Tested method .getResourceAsStream(path) inside Main method and it finds credential.json inside \eclipse\FetchMail or any other file i want
Then moved file to \eclipse\FetchMail\resources and call .getResourceAsStream("/resources/credentials.json") also finds file.
But when try this in getCredentials method from Quickstart, there is FileNotFound exception.
private static final String CREDENTIALS_FILE_PATH = "/resources/credentials.json";
private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
// Load client secrets.
InputStream in = Main.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
if (in == null) {
throw new FileNotFoundException("Resource not found: " + CREDENTIALS_FILE_PATH);
}
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(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
.setAccessType("offline")
.build();
LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
}
Google uses Oauth2 to authorize and authenticate accounts in order to use the Google APIs. To do so, follow the next steps:
In your Google Developer Console, click on Credentials.
Click on CREATE CREDENTIALS and Select Oauth Client Id
In this case, choose Desktop App
Set any Name and click on Create
You will get a screen with the Client ID and the Client Secret. These keys will authorize your account to use the APIs. They are stored in the credentials file. You don't need to do anything with them, so close the window.
Download the credential's JSON file.
In the Quickstart code, you will notice this line:
private static final String CREDENTIALS_FILE_PATH = "/credentials.json";
You have to set the path of your credentials file. Change the name of the file to yours.
When running the code for the first time, you will get a link to accept the permissions specified in the Scopes. This will generate an Access token which allows the application to access the API without asking for human interaction until it expires.
Finally solved, changed .getResourceAsStream with FileInputStream(path) constructor and it works.

Create Google Calendar Event from Java Backend from OAuth authenticated Users only

Working on creating a Google Calendar Event on a specific Calendar from backend. Like when someone generate a meeting in a web application, it should be added to the shared google calendar from backend (running using JAVA - Spring boot).
User who is generating event must be an authorized user to do so.
I have followed this docs for initial set up in java.
JAVA docs - initial setup
Dependancies that I've used,
compile 'com.google.api-client:google-api-client:1.23.0'
compile 'com.google.oauth-client:google-oauth-client-jetty:1.23.0'
compile 'com.google.apis:google-api-services-calendar:v3-rev305-1.23.0'
Then to create a event I've tried this one,
public String createCalendarEvent() throws GeneralSecurityException, IOException {
String calendarId = "primary";
// Build a new authorized API client service.
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
Calendar service = new Calendar.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
.setApplicationName(APPLICATION_NAME)
.build();
Event event = new Event()
.setSummary("Testing Event Creation 1")
.setLocation("India")
.setDescription("Testing Event Creation");
EventDateTime start = new EventDateTime()
.setDate(new DateTime("2019-02-26"));
event.setStart(start);
EventDateTime end = new EventDateTime()
.setDate(new DateTime("2019-02-28"));
event.setEnd(end);
event = service.events().insert(calendarId, event).execute();
log.debug("Event is created and HTML link is: " + event.getHtmlLink());
return event.getHtmlLink();
}
getCredential() method is taking care of Authorization as below:
private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
// Load client secrets.
InputStream in = CalendarEventService.class.getClassLoader().getResourceAsStream(CREDENTIALS_FILE_PATH);
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(new FileDataStoreFactory(new File(TOKENS_DIRECTORY_PATH)))
.setAccessType("offline")
.setApprovalPrompt("force")
.build();
LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();
Credential credential = new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
return credential;
}
The required OAuth ClientID and ClientSecret are located at CREDENTIALS_FILE_PATH. While running it generates AUTH_TOKEN inside TOKENS_DIRECTORY_PATH and create an event in Calendar.
Now subsequent execution execute without any issue If user changes.
I require to create Event in Calendar by the User who is currently logged in (Should be checked for OAuth) and from backend as this one.
server to server communication without consent
You need to understand that private user data will always require you to have access. You cant access a users account without that users permission. Oauth2 gives us as developers a way of requesting consent of users to access their data. There is no way around that you will always have to have permission of the user to access their data.
Now if you have a gsuite account you can use a service account and set up domain wide delegation to allow the service account access to all of the users on the domain then it will be able to preform actions on behalf of the users without having to prompt anyone.
If you are only accessing a single account that you personally have control over then you could also use a service account and share the calendar with the service account and then the service account will have access to that account and be able to preform actions upon it.
Credential storage
new FileDataStoreFactory(new File(TOKENS_DIRECTORY_PATH)
denotes where the credentials are stored "user" is a string denoting the user who you are storing credentials for.

Authorize scopes after renewing credentials and app access to google account

Here is my problem.
I'm developping a web app using google app engine in JAVA.
I've searched the web and saw a lot of different answers to my problem, giving various solutions but none of them worked for me I still get:
{
"code" : 403,
"errors" : [ {
"domain" : "global",
"message" : "Insufficient Permission",
"reason" : "insufficientPermissions"
} ],
"message" : "Insufficient Permission"
}
I'm trying to send a mail from my account to other email adresses (from me to me as a test).
I've implemented successfully the sheets API but remembered I had to go first through a consent Screen but now I really don't know how to popup this screen again to validate scopes permanently, Let me explain myself.
I have two credentials files.
for retrieving users infos to confirm sign in through Google, here I use an openID connect URL and there I user get the consent screen.
use Sheets API and Gmail API as "me" to first retrieve datas from my spreadsheets and send mail from my email adress. As told before I've validated the Sheets API but can't get Gmail to work with the same credentials.
I've tested various scopes add another credential file without any success. Please tell me if you need some code.
EDIT
Just deleted my Sheets API credentials also removed my app fto be linked to my account and now I cannot get the consent screen to popup. and I get:
com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request
{
"error" : "invalid_grant",
"error_description" : "Token has been expired or revoked."
}
I'm using this code to get credentials
private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
// Load client secrets.
InputStreamReader in = new InputStreamReader(new FileInputStream(new File("WEB-INF/credentials.json")));
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, in);
// Build flow and trigger user authorization request.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
.setAccessType("offline")
.build();
LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
}
but google documentation says:
Authorization information is stored on the file system, so subsequent
executions will not prompt for authorization.
Where can I delete the stored file system?
Any help is greatly appreciated
After messing around hours and hours I've finally came with an unexplainable solution.
I remove my app from this page
then change my code to
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
to this
return new AuthorizationCodeInstalledApp(flow, receiver).authorize(null);
to make appear consent screen.
then I after validating my app I get a NullPointerException error so I change back the same line to this
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("myemail#gmail.com");
and if it doesn't work (I guess that's a cache problem)
to this
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("me");
don't ask me why but that did work.
Still any more detailed infos about this behaviour is welcome.

How to upload videos in Youtube using refreshtoken in java

I am trying to upload videos to Youtube with authorization code.
public Credential authorize(List scopes, String credentialDatastore) throws IOException, URISyntaxException {
// Load client secrets.
URI filePath = new URI (GOOGLE_APIKEY);
Reader clientSecretReader =new InputStreamReader(new FileInputStream(filePath.toString()));
//Reader clientSecretReader = new InputStreamReader(Auth.class.getResourceAsStream(GOOGLE_APIKEY));
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, clientSecretReader);
// Checks that the defaults have been replaced (Default = "Enter X here").
if (clientSecrets.getDetails().getClientId().startsWith("Enter")
|| clientSecrets.getDetails().getClientSecret().startsWith("Enter ")) {
System.out.println(
"Enter Client ID and Secret from https://console.developers.google.com/project/_/apiui/credential "
+ "into src/main/resources/client_secrets.json");
System.exit(1);
}
// This creates the credentials datastore at ~/.oauth-credentials/${credentialDatastore}
FileDataStoreFactory fileDataStoreFactory = new FileDataStoreFactory(new File(System.getProperty("user.home") + "/" + CREDENTIALS_DIRECTORY));
DataStore datastore = fileDataStoreFactory.getDataStore(credentialDatastore);
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, scopes).setCredentialDataStore(datastore).build();
// Build the local server and bind it to port 8080
LocalServerReceiver localReceiver = new LocalServerReceiver.Builder().setPort(8080).build();
// Authorize.
return new AuthorizationCodeInstalledApp(flow, localReceiver).authorize("user");
}
This is working and the user has to authenticate everytime when the video will be uploaded.
Now I want the to upload videos using the accesstoken generated from the refreshtoken which I already have .
But need to integrate in my Auth file which has the LocalServerReceiver as uses Jetty server internally.
I have written the code to get the accesstoken from refresh token .Please help me to integrate it .
public GoogleCredential getCredentials(String clientId,String clientSecret,JsonFactory jsonFactory,HttpTransport transport,String refreshToken) throws IOException{
GoogleCredential credential = new GoogleCredential.Builder()
.setClientSecrets(clientId, clientSecret)
.setTransport(transport)
.setJsonFactory(jsonFactory)
.build();
credential.setRefreshToken(refreshToken);
// Do a refresh so we can fail early rather than return an unusable credential
credential.refreshToken();
String authCode=credential.getAccessToken();
return credential;
}
There was specifically two problems that I was facing during Youtube video upload using google-java-api
An instance of jetty server instance which will be listening constantly until the response is coming from Google as mentioned in the redirect url.
Though there is a function called setHost() inside new LocalServerReceiver.Builder() class which responsible for creating a local jetty server instance, was throughing a Cannot assign requested address error everytime a host name was given irrespective of the port which did not matter.
The whole authorisation process is done in the AuthorizationCodeInstalledApp class's authorize method whose primary functions are as follows
Create an url that will ask the user to give access to the app .
After successful authentication a code will be received (An instance of jetty server continuously listens untill the code is received ).
Exchange the code just received with the accesstoken and refreshtoken for offline upload.
Store the credentials that we just received from google.
To decouple the whole process I have created a new class ExtendedAuthorizationCodeInstalledApp which extends the original AuthorizationCodeInstalledApp and created each method for each functions in the class.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
The code implementation is here
Thanks #KENdi for your suggestion...

Categories

Resources