TokenResponseException: 401 Unauthorized - java

I want to implement Google Sheet API request using service account. I created this code:
HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory JSON_FACTORY = new JacksonFactory();
ClassLoader classLoader = this.getClass().getClassLoader();
java.io.File path = new java.io.File(classLoader.getResource("i-6dc0c917ee63.p12").getFile());
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId("test221#sonora-project.iam.gserviceaccount.com")
.setServiceAccountPrivateKeyFromP12File(path)
.setServiceAccountScopes(Arrays.asList(SheetsScopes.SPREADSHEETS))
.setServiceAccountUser("sonoraw#gmail.com")
.build();
Sheets service = new Sheets.Builder(httpTransport, JSON_FACTORY, null)
.setApplicationName("project")
.setHttpRequestInitializer(credential).build();
Sheets.Spreadsheets spreadsheets = service.spreadsheets();
Spreadsheet includeGridData = spreadsheets.get(spreadsheetId).execute();
But I get this error:
com.google.api.client.auth.oauth2.TokenResponseException: 401 Unauthorized
at this method .execute();
Do you know how I can fix this issue?

I was having the same issue and it was driving me insane. I had 2 different copies of the same app, using the same credentials.json, but I'd get a successful response in one, but a 401 unauthorized in the other. I finally solved it by deleting the StoredCredential. So next time I ran my app I was taken to Google's authentication page, and it worked.
TLDR:
delete StoredCredential
One way to find it is running find . -name 'StoredCredential' from the root of your application.

Based from Standard Error Responses, an error 401 is due to invalidCredentials and this indicates that the auth token is invalid or has expired.
Recommended Action:
Do not retry without fixing the problem. You need to get a new auth token.
With this, you may want to check Token Expiration which mentioned that you must write your code to anticipate the possibility that a granted token might no longer work. It also gives these possible reasons why a token might stop working:
The user has revoked access.
The token has not been used for six months.
The user changed passwords and the token contains Gmail scopes.
The user account has exceeded a certain number of token requests.
Hope that helps!

Related

Java : Google AppEngine : OAuth : Google Drive API to access user's Google Drive contents created by Web Application

Scenario: I am working on a Web Application on Google AppEngine where users have to access files from their own Google Drive. I have looked for online help and this is what I have figured out.
I have used this link for help that worked fine while testing with local machine
https://github.com/gsuitedevs/java-samples/blob/master/drive/quickstart/src/main/java/DriveQuickstart.java
Step 1 (Seemed simple): Enable Google Drive API and Setup necessary credential, i.e. OAuth 2.0 Client ID for Web Application. (This is done while enabling IAP on Google AppEngine, so I did not do it again. Whenever anyone opens the web application, he/she is asked to authenticate via iap.googleapis.com and details saved. This works fine). Also, I have added Google Drive Scopes to the OAuth consent screen (../auth/drive.appdata & ../auth/drive.file) that don't need verification by Google.
Step 2: Downloaded the credentials.json from OAuth Client ID and stored inside "resources" folder created in the root of application package (inside main folder, next to java and webapp folders)
Step 3: I have created a testing class (GoogleDriveServiceTest.class) that includes following code:
String USER_ID1 = UserServiceFactory.getUserService().getCurrentUser().getUserId();
List<String> SCOPES = Collections.singletonList(DriveScopes.DRIVE_METADATA_READONLY);
NetHttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
InputStream inputStream =
GoogleDriveServiceTest.class.getResourceAsStream("/credentials.json");
if (inputStream == null)
throw new FileNotFoundException("Required credentials file not found");
GoogleClientSecrets googleClientSecrets =
GoogleClientSecrets.load(jsonFactory, new InputStreamReader(inputStream));
AppEngineDataStoreFactory appEngineDataStoreFactory =
AppEngineDataStoreFactory.getDefaultInstance();
//IS SOMETHING MISSING HERE???
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow
.Builder(httpTransport, jsonFactory, googleClientSecrets, SCOPES)
.setDataStoreFactory(appEngineDataStoreFactory)
.setAccessType("offline")
.build();
Now I am trying to create the credential to be used for accessing Google Drive with this line:
Credential credential = flow.loadCredential(USER_ID1);
that is returning null.
In my opinion I am missing to assign the credentials to AppEngineDataStoreFactory based on what I have seen in the example from the github link above. However, I am not sure if this is the issue, and if it is, how do I resolve it.
Is there a straight forward way to assign credentials using logged in userID obtained from
UserServiceFactory.getUserService().getCurrentUser().getUserId() ? Or should I be obtaining accetoken and create the credential? if so, how?
(I don't want to use javascript as the same does not seem suitable for web application)
Any help would be great!!!!
PS: I also wanted to add a point that user needs to access only files added by the same web application either via web or from android
Update #1 responding to #Aerials:
Here is the code I was trying with to get the TokenResponse:
VerificationCodeReceiver receiver = new GooglePromptReceiver();
(I know above one is not the right option, but I am not able to find any other)
AuthorizationCodeRequestUrl authorizationUrl =
flow.newAuthorizationUrl().setRedirectUri(receiver.getRedirectUri());
String code = receiver.waitForCode();
(Above line returns: java.util.NoSuchElementException: No line found)
TokenResponse tokenResponse =
flow.newTokenRequest(code).setRedirectUri(redirectUri).execute();
Update #2 Code that worked in getting the TokenResponse and rest of the tasks of creating a Credential and connect to Google Drive successfully:
GenericUrl genericUrl = new GenericUrl(request.getRequestURL().toString());
genericUrl.setRawPath("/googleDriveTest");
String redirectUri = genericUrl.build();
(redirectUri should match with authorised redirect URI inside OAuth ClientID under GCP API Credentials. If you added it now, you need to redownload the credentials.json file)
String redirectUrl = authorizationCodeFlow
.newAuthorizationUrl()
.setRedirectUri(redirectUri)
.build();
String authorizationCode = request.getParameter("code");
if (authorizationCode == null || authorizationCode.isEmpty())
response.sendRedirect(redirectUrl);
TokenResponse tokenResponse = authorizationCodeFlow
.newTokenRequest(authorizationCode)
.setRedirectUri(redirectUri).execute();
authorizationCodeFlow.createAndStoreCredential(tokenResponse, USER_ID1);
Credential credential = authorizationCodeFlow.loadCredential(USER_ID1);
Drive service = new Drive.Builder(httpTransport, jsonFactory, credential)
.setApplicationName("myapplicationname")
.build();
You need to first create and store the credentials in the flow's credential store:
createAndStoreCredential(TokenResponse response, String userId)
to be able to load them with loadCredential(String userId)
The loadCredential() method will return credential found in the credential store of the given user ID or null for none found.

Oauth2 timeout with google API

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

How to get started with OAuth2 for Google Contacts API

I am trying to make use of Google Contacts API with OAuth 2 authentication. But some of the old sample codes are now not working for this API.I found a sample code which is makes request for Access Token and in response Google servers would give me the Access Token for ~1 hour limit and a Refresh Token with.
BUT this code has some issue
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
String APPLICATION_NAME = "PROJECT_NAME";
String SERVICE_ACCOUNT_EMAIL = "NUMERCALS-ALPHANUMERICALS#developer.gserviceaccount.com";
java.io.File p12File = new java.io.File("PROJECT_NAME-NUMERICALS.p12");
GoogleCredential credential =
new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(
Collections.singleton("https://www.google.com/m8/feeds"))
.setServiceAccountPrivateKeyFromP12File(p12File)
.setServiceAccountUser("user#example.com")
.build();
if (!credential.refreshToken()) {
throw new RuntimeException("Failed OAuth to refresh the token");
}
ContactsService service = new ContactsService(APPLICATION_NAME);
service.setOAuth2Credentials(credential);
Query gQuery = new Query(new java.net.URL("https://www.google.com/m8/feeds/groups/user#example.com/full"));
gQuery.setMaxResults(32767);
ContactGroupFeed groupFeed = service.query(gQuery, ContactGroupFeed.class);
for (ContactGroupEntry group : groupFeed.getEntries()) {
And I am getting some issues with it
com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request
{
"error" : "invalid_grant"
}
at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:105)
at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:287)
at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:307)
at com.google.api.client.googleapis.auth.oauth2.GoogleCredential.executeRefreshToken(GoogleCredential.java:269)
at com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:489)
at javaCode.FinalCode.main(FinalCode.java:68)
I am new to use OAuth2 for Google APIs so guide me if I am mistaken somewhere or provide a working code snippet.It would be great help.Thanks in advance.
In you example you are trying to use Service account to impersonate the user.
While this is possible, it can only be achieved under Google for work or Google for education accounts.
It would not work for normal gmail accounts.
If this is the case for your use case, then remember that the domain administrator should grant access to your application and also should grant Domain wide delegation of authority.
But if you are just trying to make some tests with the API, then it would be better if you use normal OAuth.
Check this tutorial, I know is for Drive, but there you can see the basic steps to get a authorization token. After you get it, the call to the different Google Apis is very similar.
I would recommend to check the OAuth Playground. There you can check the complete OAuth process and you can play with the API.

OAuth 2.0 access with Google Plus API in java

I've been trying to write a web application using Google Plus API and i need to set up OAuth access with java , I searched a lot and found google java starter and other examples and they were very confusing, I can't figure out what the code that I should write to get the token
I hope if there is someone who can tell me how to get the OAuth access with java in straight forward steps, I saw other questions on stackoverflow.com but they weren't very helpful for me
so any help would be very appreciated :)
The latest Google+ Java Quickstart is pretty straightforward, perhaps you found an older project when searching? Also, the documentation for getting started on Google+ with Java should help to get you going.
The following snippet shows you the relevant code for exchanging the authorization code for an access token when using the hybrid client/server flow:
GoogleTokenResponse tokenResponse =
new GoogleAuthorizationCodeTokenRequest(TRANSPORT, JSON_FACTORY,
CLIENT_ID, CLIENT_SECRET, code, "postmessage").execute();
// Create a credential representation of the token data.
GoogleCredential credential = new GoogleCredential.Builder()
.setJsonFactory(JSON_FACTORY)
.setTransport(TRANSPORT)
.setClientSecrets(CLIENT_ID, CLIENT_SECRET).build()
.setFromTokenResponse(tokenResponse);
I'm removing the lines performing the requisite checks discussed in this thread for simplicity.
// Store the token in the session for later use.
request.session().attribute("token", tokenResponse.toString());
It's worth noting here that you want to persist these credentials unless the user disconnects your app. The sample is using a session because in production environments, the session can be DB-backed and will be restored after the server restarts.
After you have the access / refresh token and expiration time, build the credentials for the OAuth v2 token and then the library will internally refresh the access token appropriately. The following code shows how this is done on the quickstart by retrieving the token data from the user's session and also includes an API call performed by the client, proving the server's Java client is working:
// Build credential from stored token data.
GoogleCredential credential = new GoogleCredential.Builder()
.setJsonFactory(JSON_FACTORY)
.setTransport(TRANSPORT)
.setClientSecrets(CLIENT_ID, CLIENT_SECRET).build()
.setFromTokenResponse(JSON_FACTORY.fromString(
tokenData, GoogleTokenResponse.class));
// Create a new authorized API client.
Plus service = new Plus.Builder(TRANSPORT, JSON_FACTORY, credential)
.setApplicationName(APPLICATION_NAME)
.build();
// Get a list of people that this user has shared with this app.
PeopleFeed people = service.people().list("me", "visible").execute();
If you wanted to do this differently, you could explicitly construct the tokenData object from the access token, refresh token, and so on, before constructing the Plus service object.

Authenticate programmatically to Google with OAuth2

How can I authenticate programmatically to Google?
Now that ClientLogin (https://developers.google.com/accounts/docs/AuthForInstalledApps)
is deprecated, how can we perform a programmatic authentication to Google with OAuth2?
With ClientLogin we could perform a post to
https://www.google.com/accounts/ClientLogin
with email and password parameters and obtain the authentication token.
With OAuth2 i can't find a solution!
#
My app is a java background process.
I saw, following this link: developers.google.com/accounts/docs/OAuth2InstalledApp#refresh, how to obtain a new access token using a refreshed token.
The problem is that I can't find a java example about how to instantiate an Analytics object (for example) to perform a query when I have a new valid access token
This is my code that returns a 401 Invalid credentials when invoke the "execute()":
public class Test {
static final String client_id = "MY_CLIENT_ID";
static final String client_secret = "MY_SECRET";
static final String appName = "MY_APP";
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
static String access_token = "xxxx";
static String refreshToken = "yyyyy";
public static void main (String args[]){
try {
GoogleCredential credential =
new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setClientSecrets(client_id, client_secret).build();
credential.setAccessToken(access_token);
credential.setRefreshToken(refreshToken);
//GoogleCredential
Analytics analytics = Analytics.builder(HTTP_TRANSPORT, JSON_FACTORY)
.setApplicationName(appName)
.setHttpRequestInitializer(credential)
.build();
Accounts accounts = analytics.management().accounts().list().execute();
} catch (Exception e) {
e.printStackTrace();
}
}
What is the problem?
Check the OAuth 2 flow for Installed Application:
https://developers.google.com/accounts/docs/OAuth2InstalledApp
It still requires the user to authenticate with a browser the first time, but then you can store the refresh token and use it for subsequent requests.
For alternative solutions, check the Device flow or Service Accounts, they are explained in the same documentation set.
I found the Google Java client to be overly complex and poorly documented. Here's plain and simple Servlet example with Google Oauth2. For a background process you'll need to request access_type=offline. As others have mentioned you need the user to do a one time authorization. After that you can request refresh tokens as google tokens expire in an hour.
Although I appreciate that the OP was originally targeting the OAuth2InstalledApp approach, I would like to point out a working solution using the OAuth2WebServer approach. They don't differ significantly and this worked for me. I have found the google OAuth library to be pretty good as it will handle most of the OAuth dance for you and it makes it easy to refresh the access token. The solution below depends on using a pre-obtained refresh token.
As the accepted answer states, to get OAuth authentication working (even for a Java background process) where the request relies upon access to user data
requires the user to authenticate with a browser the first time, but then you can store the refresh token and use it for subsequent requests.
From previous comments by the OP I see the following
So I followed OAuth2 for Web Server Applications (here offline access is documented) but I have still problems.
1) I perform the first request via browser and I obtain autenticaton code for offline access
2) I perform a java post of the authentication code and obtain acces token and refresh token
The approach I used is more like
1) I perform the first request via a browser and obtain the refresh token for offline access
2) In java I provide the refresh token to the library and the library will obtain the access token etc
specifically, using the google-api-java-client library the code is quite straightforward and note that I haven't set an access token as the OP did, as I am calling credential.refreshToken(); elsewhere. (I check if I have a valid access token already and if not call refresh prior to the API call)
private Credential generateCredentialWithUserApprovedToken() throws IOException,
GeneralSecurityException {
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
InputStreamReader inputStreamReader =
new InputStreamReader(jsonFileResourceForClient.getInputStream());
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(jsonFactory, inputStreamReader);
return new GoogleCredential.Builder().setTransport(httpTransport).setJsonFactory(jsonFactory)
.setClientSecrets(clientSecrets).build().setRefreshToken(REFRESH_TOKEN);
}
Note this covers step 2 of my approach, and the REFRESH_TOKEN mentioned in step 1 can be obtained as explained below.
First there is a prior set up of a web app creating an OAuth 2.0 client ID on the Google console for Credentials where you end up with a downloaded json file which will be read into the GoogleClientSecrets object.
i.e.
Make sure you add the Google playground callback uri into Authorized redirect URIs
Then you have your client id and the client secret ready for the playground and you can also download the json which you can pull into your Java code.
The REFRESH_TOKEN is obtained by sending a request to the google oauth playground with the following configuration. Note that prior to Step 1 and selecting your scope you should go to settings to check that you are providing you own credentials and add your client id and secret just below that
Note that the Access type is Offline, which corresponds to this.
There is also a nice explanation on grabbing the refresh token here https://www.youtube.com/watch?v=hfWe1gPCnzc
That is enough to get going and is a one time set up!
Regarding refresh tokens you should be aware of their lifecycle as discussed in the docs here
In the oauthplayground you will see this
but on point 4 of the docs here it says this
Hmmm.
Also for reference see How do I authorise an app (web or installed) without user intervention? (canonical ?)
For applications that authenticate on behalf of themselves (i.e., to another application, traditionally by signing into a role account using a shared password), the OAuth2 alternative to ClientLogin offered by Google is Service Accounts:
https://developers.google.com/accounts/docs/OAuth2ServiceAccount

Categories

Resources