Google oauth2 Java Contacts Service - Conflict in documentation - java

I have registered my web application for use with oath2 using the following instructions:
http://code.google.com/apis/accounts/docs/OAuth2.html
This means my client is created with a client ID, client secret and Redirect URI.
Once I have configured my web application as per
http://code.google.com/apis/accounts/docs/OAuth2WebServer.html
I recieve a code in a request parameter from google, which I can then use to request an access token, which comes in a JSON in a format along the lines of:
{
"access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in":3920,
"token_type":"Bearer"
}
Once this is done, I can use that access token to access a google api on behalf of the user:
GET https://www.googleapis.com/oauth2/v1/userinfo?access_token=1/fFBGRNJru1FQd44AzqT3Zg
This as documented is done by simply passing the access token as a request parameter.
However when I move onto using a Java API (In this case google contacts) I get the following in the documentation for HMAC-SHA1:
GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);
oauthParameters.setOAuthToken(ACCESS_TOKEN);
oauthParameters.setOAuthTokenSecret(TOKEN_SECRET);
DocsService client = new DocsService("yourCompany-YourAppName-v1");
client.setOAuthCredentials(oauthParameters, new OAuthHmacSha1Signer());
URL feedUrl = new URL("https://docs.google.com/feeds/default/private/full");
DocumentListFeed resultFeed = client.getFeed(feedUrl, DocumentListFeed.class);
for (DocumentListEntry entry : resultFeed.getEntries()) {
System.out.println(entry.getTitle().getPlainText());
}
Or the following for RSA-SHA1
GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);
oauthParameters.setOAuthToken(ACCESS_TOKEN);
PrivateKey privKey = getPrivateKey("/path/to/your/rsakey.pk8"); // See above for the defintion of getPrivateKey()
DocsService client = new DocsService("yourCompany-YourAppName-v1");
client.setOAuthCredentials(oauthParameters, new OAuthRsaSha1Signer(privKey));
URL feedUrl = new URL("https://docs.google.com/feeds/default/private/full");
DocumentListFeed resultFeed = client.getFeed(feedUrl, DocumentListFeed.class);
for (DocumentListEntry entry : resultFeed.getEntries()) {
System.out.println(entry.getTitle().getPlainText());
}
First off, it seems that if I was doing standard http rather than the java wrapper, all I would need to provide is an access token. Am I missing something or where have these additional parameters come from? Mainly TOKEN_SECRET, which there is no mention of in the docunentation. There is also no mention of having to provide CONSUMER_KEY and CONSUMER_SECRET. I am presuming they are alternative names for client id and client secret, but I do not understand why I am now having to provide them. Finally when setting up my application using the google API's console, there was no mention whatsoever of the two different encryption types, and which one I am going to be using, am I missing something here aswell?

The Java code examples you show are based on OAuth 1.0 (not OAuth 2.0) which has some crypto requirements which were simplified in OAuth 2.0. In some cases with the Google Contacts API you need OAuth 1.0. See: http://code.google.com/apis/contacts/docs/3.0/developers_guide.html#GettingStarted

Related

Pass Crendentials from dotNet client to Java Web Service

I have a dot net application that call a java web service. I am trying to implement authentication by passing credentials to the java service. Here is the dot net code setting the credentials. How can I get these credentials in my java application? They aren't set in the headers...
System.Net.NetworkCredential serviceCredentials = new NetworkCredential("user", "pass");
serviceInstance.Credentials = serviceCredentials;
serviceInstance is an instance of SoapHttpClientProtocol.
I've tried injecting the WebServiceContext like so
#Resource
WebServiceContext wsctx;
and pulling the crentials from the headers but they aren't there.
You are not passing the credentials to your service the correct way. In order to get the Authorize http request header do the following:
// Create the network credentials and assign
// them to the service credentials
NetworkCredential netCredential = new NetworkCredential("user", "pass");
Uri uri = new Uri(serviceInstance.Url);
ICredentials credentials = netCredential.GetCredential(uri, "Basic");
serviceInstance.Credentials = credentials;
// Be sure to set PreAuthenticate to true or else
// authentication will not be sent.
serviceInstance.PreAuthenticate = true;
Note: Be sure to set PreAuthenticate to true or else authentication will not be sent.
see this article for more information.
I had to dig-up some old code for this one :)
Update:
After inspecting the request/response headers using fiddler as suggested in the comments below a WWW-Authenticate header was missing at the Java Web Service side.
A more elegant way of implementing "JAX-WS Basic authentication" can be found in this article here using a SoapHeaderInterceptor (Apache CXF Interceptors)

Java and Google Spreadsheets API Authorization with OAuth 2.0

I want to read Google Spreadsheets using Java, and the recommended way to do this is using the Google Spreadsheets API.
The problem begins when you want to make procedures secure, so they encourage you to use OAuth 2.0. In the official page they show how to do this using only .NET and say that "the Java client library doesn't currently support OAuth 2.0", and they give alternatives like using OAuth 1.0 or Client Login using directly email and password.
Is this for sure?, isn't there a way to do OAuth 2.0 Authentication through Java, maybe not using directly the Java client library, but through requests with specific parameters.
Please I would appreciate any suggestions.
I also found it quite silly that the developer docs provided Java examples for everything except OAuth2. Here's some sample code that I used to get it working. For completeness it includes the retrieving spreadsheets example in the later section. Note also that you have to add the required scopes to the Java DrEdit example as shown below.
public class GSpreadsheets {
private static final String CLIENT_ID = "YOUR_CLIENT_ID";
private static final String CLIENT_SECRET = "YOUR_SECRET_ID";
private static final String REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob";
public static void main(String[] args) throws Exception {
if (CLIENT_ID.equals("YOUR_CLIENT_ID") || CLIENT_SECRET.equals("YOUR_SECRET_ID")) {
throw new RuntimeException(
"TODO: Get client ID and SECRET from https://cloud.google.com/console");
}
// get credentials similar to Java DrEdit example
// https://developers.google.com/drive/examples/java
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
httpTransport, jsonFactory, CLIENT_ID, CLIENT_SECRET,
Arrays.asList(DriveScopes.DRIVE,
"https://spreadsheets.google.com/feeds",
"https://docs.google.com/feeds"))
.setAccessType("online")
.setApprovalPrompt("auto").build();
String url = flow.newAuthorizationUrl().setRedirectUri(REDIRECT_URI).build();
System.out.println("Please open the following URL in your "
+ "browser then type the authorization code:");
System.out.println(" " + url);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String code = br.readLine();
GoogleTokenResponse response = flow.newTokenRequest(code).setRedirectUri(REDIRECT_URI).execute();
GoogleCredential credential = new GoogleCredential().setFromTokenResponse(response);
// create the service and pass it the credentials you created earlier
SpreadsheetService service = new SpreadsheetService("MyAppNameHere");
service.setOAuth2Credentials(credential);
// Define the URL to request. This should never change.
URL SPREADSHEET_FEED_URL = new URL(
"https://spreadsheets.google.com/feeds/spreadsheets/private/full");
// Make a request to the API and get all spreadsheets.
SpreadsheetFeed feed = service.getFeed(SPREADSHEET_FEED_URL, SpreadsheetFeed.class);
List<SpreadsheetEntry> spreadsheets = feed.getEntries();
// Iterate through all of the spreadsheets returned
for (SpreadsheetEntry spreadsheet : spreadsheets) {
// Print the title of this spreadsheet to the screen
System.out.println(spreadsheet.getTitle().getPlainText());
}
}
}
The Google Data Java Client Library now supports OAuth 2.0:
https://code.google.com/p/gdata-java-client/source/detail?r=505
Unfortunately, there are no complete samples in the library showing how to use it. I'd recommend checking these two links to put together the information to make it work:
https://code.google.com/p/google-oauth-java-client/wiki/OAuth2
https://code.google.com/p/google-api-java-client/wiki/OAuth2
[Edit]
Java OAuth2 code
Blog post on [google-spreadsheet-api] and OAuth2, with code
http://soatutorials.blogspot.co.at/2013/08/google-spreadsheet-api-connecting-with.html
Related question: OAuth2 authorization from Java/Scala using google gdata client API
[end edit]
I used: Google drive DrEdit tutorial, full example shows how to use OAuth 2.0 with Drive. The code works with google spreadsheets GData style API. (note: does not include refresh token, but the refresh token works as you would expect, so not hard too add.) -
Extra Note: A better documented API is Google-Apps-Script.

Google Drive API through Google App Engine

I'm trying to use the Google Drive API through the App Identity interface provided with Google App Engine. This basically allows my web application to communicate with Google's APIs from server to server.
I don't need my users to login, I simply need to display my own Google Drive documents.
However, after I set all the appropriate values and scopes, and enable all the right Google Drive knobs on the console page, I still get this for a simple GET request to https://www.googleapis.com/drive/v2/files:
{ "error": { "errors": [ { "domain": "usageLimits", "reason": "dailyLimitExceededUnreg", "message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.", "extendedHelp": "https://code.google.com/apis/console" } ], "code": 403, "message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup." }}
What's wrong? What am I missing? Here's the code that actually does the request - funny thing is that it works great if I use other APIs such as the URL shortener API:
var scopes = new java.util.ArrayList();
scopes.add("https://www.googleapis.com/auth/drive");
var appIdentity = AppIdentityServiceFactory.getAppIdentityService();
var accessToken = appIdentity.getAccessToken(scopes);
var url = new URL("https://www.googleapis.com/drive/v2/files");
var connection = url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("GET");
connection.addRequestProperty("Content-Type", "application/json");
connection.addRequestProperty("Authorization", "OAuth " + accessToken.getAccessToken());
EDIT
If I simply change the API to use the urlshortner API for example, it works:
var url = new URL("https://www.googleapis.com/urlshortener/v1/url/history");
And output:
{ "kind": "urlshortener#urlHistory", "totalItems": 0, "itemsPerPage": 30}
So there must be something not working with Google Drive and App Identity?
EDIT 2
I've found some help from the correct answer here: https://stackoverflow.com/a/12526286/50394
But it's talking about setting Client API scopes on Google Apps, and I'm not using Google Apps, I'm simply using Google App Engine's domain foo.appspot.com
The 403 error you are getting means that there was no Authorization header in your GET. The logic is that without an Authorization header, you are anonymous (you are legion blah blah :-)). The Drive quota for anonymous use is zero, hence the message. URL shortener has a higher quota for anonymous so it works.
I suggest you change the URL to point to an http server of your own, and check what headers you are actually sending.
AFAICT you should be using Bearer in the Authorization header.
Probably what's happening is, Drive API doesn't recognize the service account (because of the wrong header?) and thus taking it as an anonymous request since no key parameter wasn't provided either (see common query params).
Try this:
connection.addRequestProperty("Authorization", "Bearer " + accessToken.getAccessToken());
Or you could try adding the token as access_token query param.
I think you should at least setup an API console entry with Drive API enabled at https://code.google.com/apis/console
Once you create this you'll get an ID you can use in your GoogleCredential object. From the GoogleCredential object you can get the access token which you can than add to your request.
What I read here (Google drive via service accounts) was that you use a slightly different style that uses an API KEY that you retrieve from the Developer Console.
The pertinent parts for me were to generate a "Key for Server Applications", then use this technique, which I hadn't read anywhere else!
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
AppIdentityCredential credential =
new AppIdentityCredential.Builder(DriveScopes.DRIVE).build();
// API_KEY is from the Google Console as a server API key
GoogleClientRequestInitializer keyInitializer =
new CommonGoogleClientRequestInitializer(API_KEY);
Drive service = new Drive.Builder(httpTransport, jsonFactory, null)
.setHttpRequestInitializer(credential)
.setGoogleClientRequestInitializer(keyInitializer)
.build();
This answer claims that:
Service Accounts are not supported by the Drive SDK due to its
security model.
If that's still true, one workaround is to perform a regular OAuth dance once with a regular Google Account, and persist the access and refresh token in the datastore.

2-legged OAuth with google-api-java-client

Does anyone know how to use 2-legged OAuth with google-api-java-client?
I'm trying to access the Google Apps Provisioning API to get the list of users for a particular domain.
The following does not work
HttpTransport transport = GoogleTransport.create();
GoogleHeaders headers = (GoogleHeaders) transport.defaultHeaders;
headers.setApplicationName(APPLICATION_NAME);
headers.gdataVersion = GDATA_VERSION;
OAuthHmacSigner signer = new OAuthHmacSigner();
signer.clientSharedSecret = CONSUMER_SECRET;
OAuthParameters oauthParameters = new OAuthParameters();
oauthParameters.version = OAUTH_VERSION;
oauthParameters.consumerKey = CONSUMER_KEY;
oauthParameters.signer = signer;
oauthParameters.signRequestsUsingAuthorizationHeader(transport);
I get the "com.google.api.client.http.HttpResponseException: 401 Unknown authorization header".
The header looks something like this
OAuth oauth_consumer_key="...", oauth_nonce="...", oauth_signature="...", oauth_signature_method="HMAC-SHA1", oauth_timestamp="...", oauth_version="1.0"
I also tried following without success
GoogleOAuthDomainWideDelegation delegation = new GoogleOAuthDomainWideDelegation();
delegation.requestorId = REQUESTOR_ID;
delegation.signRequests(transport, oauthParameters);
Any ideas?
Thanks in advance.
It seems that there was nothing wrong with the code. It actually works.
The problem was with the our Google Apps setup.
When you visit the "Manage OAuth key and secret for this domain" page (https://www.google.com/a/cpanel/YOUR-DOMAIN/SetupOAuth),
and enable "Two-legged OAuth access control" and select
"Allow access to all APIs", it doesn't actually allow access to all APIs.
If you visit the "Manage API client access" page after that
(https://www.google.com/a/cpanel/YOUR-DOMAIN/ManageOauthClients),
you'll see that there is an entry like:
YOR-DOMAIN/CONSUMER-KEY "This client has access to all APIs"
It seems that this doesn't include Provisioning API.
Only after we explicitly added the Provisioning API, the code started to work.
So to enable Provisioning API, you should also have something like the following entry in your list:
YOR-DOMAIN/CONSUMER-KEY Groups Provisioning (Read only) https://apps-apis.google.com/a/feeds/group/#readonly
User Provisioning (Read only) https://apps-apis.google.com/a/feeds/user/#readonly
Somone else had the same problem:
http://www.gnegg.ch/2010/06/google-apps-provisioning-two-legged-oauth/
Sasa
Presumably you are trying to get an unauthorised request token here? I Haven't used the Google implementation, but the OAuth 1.0a spec says you need a callback URL, which you don't have. This might be a red herring as the spec says a missing param should return HTTP code 400 not 401.
See http://oauth.net/core/1.0a/#auth_step1

Invalid parameter exception on client.auth_getSession() in Facebook java API

I want to connect to a my facebook application using the facebook java api 2.1.1(http://code.google.com/p/facebook-java-api/). My application is in "Desktop" mode so I should be able to access it outside of a web application. I have not defined any callback url for it as well. My code looks something like this.
FacebookJsonRestClient client = new FacebookJsonRestClient( FB_APP_API_KEY, FB_APP_SECRET );
String token = client.auth_createToken();
HttpClient http = new HttpClient();
http.setParams(new HttpClientParams());
http.setState(new HttpState());
final String LOGIN = "https://login.facebook.com/login.php";
GetMethod get = new GetMethod(LOGIN + "?api_key=" + FB_APP_API_KEY + "&v=1.0&auth_token=" + token );
http.executeMethod(get);
PostMethod post = new PostMethod(LOGIN);
post.addParameter(new NameValuePair("api_key", FB_APP_API_KEY));
post.addParameter(new NameValuePair("v", "1.0"));
post.addParameter(new NameValuePair("auth_token", token));
post.addParameter(new NameValuePair("email", "my-email"));
post.addParameter(new NameValuePair("pass", "my-password"));
http.executeMethod(post);
String session = client.auth_getSession(token);
However instead of returning the session the API throws an exception:
com.google.code.facebookapi.FacebookException: Invalid parameter
at com.google.code.facebookapi.FacebookJsonRestClient.parseCallResult(FacebookJsonRestClient.java:354)
at com.google.code.facebookapi.ExtensibleClient.callMethod(ExtensibleClient.java:535)
at com.google.code.facebookapi.ExtensibleClient.callMethod(ExtensibleClient.java:472)
at com.google.code.facebookapi.FacebookJsonRestClient.auth_getSession(FacebookJsonRestClient.java:278)
Can anyone please tell me whats wrong with this code? And what is the correct way to access a facebook application in desktop mode using the java api (v. 2.1.1).
Thanks for your help.
Regards
Nabeel Mukhtar
As far as I understand FB's API, you're not supposed to provide username and password manually but instead let the user input them manually and then allow the Facebook Login to redirect the user back to your application. This means that instead of providing "email" and "pass" you provide "next" and "cancel" URL:s instead.
This is purely a security feature of FB API and while the theory behind it is alright, the execution is far from optimal.
See this discussion thread on the Google Code site. There's a link in the that thread to a wiki page which explains how to do desktop auth.

Categories