I am trying to use a URL shortener, with Scribe, based on this example.
However, I want to make sure I can track the visits which my short URL gets, which means it must be unique. To create unique links, I need to authenticate with Google, as per this.
Signed in
Your links are automatically added to goo.gl where you can track their use.
A unique short URL is created each time a long URL is shortened.
Signed out
Your links won’t show up on your goo.gl page.
The same short URL is reused each time a long URL is shortened by you or someone else.
In the example, the oAthRequest is not signed with the oAuthService. I have updated this so that it can sign the request and send it (as a signed in user).
Here is my code:
private static final String API_KEY = "XXXXXXXX";
private static final String API_URL = "https://www.googleapis.com/urlshortener/v1/url";
private static final String API_URL_WITH_KEY = API_URL + "?key=" + API_KEY;
public TrackableLink createTrackableLink(String longUrl) {
OAuthService oAuthService = new ServiceBuilder()
//Google Api Provider - Google's URL Shortener API is part of Google Platform APIs
.provider(GoogleApi.class)
/*
Using "anonymous" as API Key & Secret because Google's URL Shortener service
does not necessarily requires App identification and/or User Information Access
*/
.apiKey("anonymous")
.apiSecret("anyonymous")
//OAuth 2.0 scope for the Google URL Shortener API
.scope("https://www.googleapis.com/auth/urlshortener")
//build it!
.build();
Token requestToken = oAuthService.getRequestToken();
OAuthRequest request = new OAuthRequest(Verb.POST, API_URL_WITH_KEY);
request.addHeader("Content-Type", "application/json");
request.addPayload(new JSONObject().put(RESPONSE_LONG_URL, longUrl)
.toString());
oAuthService.signRequest(requestToken, request);
Response response = request.send();
JSONObject json = new JSONObject(response.getBody());
String shortUrl = json.getString(RESPONSE_SHORT_URL);
TrackableLink tl = new TrackableLink(longUrl, shortUrl);
return tl;
}
I replace the "anonymous" details with my values from the Google API website, and I get this exception:
Can't extract token and secret from this: 'Consumer is not registered: 7629638329XXXXXXXXXXX.apps.googleusercontent.com
I'm not sure exactly what I am doing wrong here. I have tried almost every combination of values for the key/secret from the various keys the google console gives me, could this be caused by something else other than something up with the API key?
Any ideas why I am getting the consumer is not registered error? On my Google account, I have enabled the API.
Related
What I'm trying to do is generate a JWT for calling the HTTP trigger on a GCP Cloud Function I've deployed.
I've already deployed my function with 'allUsers' and verified it works, but I want it to be more secure, so I need to attach a JWT to my HTTP request.
I'm following this code and the following snippets are mostly from that.
I think I am close, but not quite there yet. In all of the samples etc below I've changed my project name to PROJECT_NAME.
First I created a service account named testharness-sa and downloaded its key file. I have an env var GOOGLE_APPLICATION_CREDENTIALS pointing to that file when I run my tests. Then I ran the following command:
gcloud functions add-iam-policy-binding gopubsub \
--member='serviceAccount:testharness-sa#PROJECT_NAME.iam.gserviceaccount.com' \
--role='roles/cloudfunctions.invoker'
This gave me a confirmation by listing all the current bindings on my cloud function, including testharness-sa.
The core of my code is this:
private String getSignedJwt() {
GoogleCredentials credentials = GoogleCredentials
.getApplicationDefault()
.createScoped(Collections.singleton(IAM_SCOPE));
long now = System.currentTimeMillis();
RSAPrivateKey privateKey = (RSAPrivateKey) credentials.getPrivateKey();
Algorithm algorithm = Algorithm.RSA256(null, privateKey);
return JWT.create()
.withKeyId(credentials.getPrivateKeyId())
.withIssuer(credentials.getClientEmail())
.withSubject(credentials.getClientEmail())
.withAudience(OAUTH_TOKEN_AUDIENCE)
.withIssuedAt(new Date(now))
.withExpiresAt(new Date(now + EXPIRATION_TIME_IN_MILLIS))
.withClaim("target_audience", clientId)
.sign(algorithm);
}
This gives me a signed JWT. As I understand things, this is used to call GCP to get me a final JWT I can use to call my cloud function.
Once I generate the signed JWT I use it like this:
String jwt = getSignedJwt();
final GenericData tokenRequest = new GenericData()
.set("grant_type", JWT_BEARER_TOKEN_GRANT_TYPE)
.set("assertion", jwt);
final UrlEncodedContent content = new UrlEncodedContent(tokenRequest);
final HttpRequestFactory requestFactory = httpTransport.createRequestFactory();
final HttpRequest request = requestFactory
.buildPostRequest(new GenericUrl(OAUTH_TOKEN_URI), content)
.setParser(new JsonObjectParser(JacksonFactory.getDefaultInstance()));
HttpResponse response = request.execute();
...
So it gets the signed JWT and makes up a request to get the final JWT and then... fails. The error is "Invalid JWT: Failed audience check"
It looks like I have a bad parameter, so let's look at my parameters (they are actually constants, not parameters):
private static final String IAM_SCOPE = "https://www.googleapis.com/auth/iam";
//"https://www.googleapis.com/auth/cloud-platform";
private static final String OAUTH_TOKEN_URI = "https://oauth2.googleapis.com/token";
//"https://www.googleapis.com/oauth2/v4/token";
private static final String OAUTH_TOKEN_AUDIENCE = "https://us-central1-PROJECT_NAME.cloudfunctions.net/gopubsub";
//"https://www.googleapis.com/token";
private static final String JWT_BEARER_TOKEN_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
private static final long EXPIRATION_TIME_IN_MILLIS = 3600 * 1000L;
I've added other variants as comments against the constants.
Based on the error message it looks like the audience value I'm using is wrong.
This page suggests the audience should be the service account email, and I think I've seen elsewhere that it ought to be the URL of the cloud function, but neither of those work for me.
I do know this can work because I can issue this command:
gcloud auth print-identity-token
which gives me the final JWT (as long as I have GOOGLE_APPLICATION_CREDENTIALS pointing at my json file).
I can paste that JWT into a curl command and invoke the HTTP trigger successfully, and if I leave the JWT out it fails, so I know the JWT is being checked.
But so far I don't know how to do the equivalent of gcloud auth print-identity-token from my Java code.
Anyone know?
I found the answer was not related to scope, despite the error message. It is actually related to the clientId value I was passing (and didn't really mention in my question, though it is there in the code). I was using a value I found in the service account json file, a really long string of digits. That was the problem. It needed to be the URL of my HTTP trigger. So these are the constants I ended up with:
private static final String IAM_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
private static final String OAUTH_TOKEN_URI = "https://www.googleapis.com/oauth2/v4/token";
private static final String OAUTH_TOKEN_AUDIENCE = "https://www.googleapis.com/oauth2/v4/token";
private static final String JWT_BEARER_TOKEN_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer";
private static final long EXPIRATION_TIME_IN_MILLIS = 3600 * 1000L;
In fact it still works if I remove all references to IAM_SCOPE, so I conclude that doesn't matter. The piece of code that pulls it together now looks like this:
return JWT.create()
.withKeyId(credentials.getPrivateKeyId())
.withIssuer(credentials.getClientEmail())
.withSubject(credentials.getClientEmail())
.withAudience(OAUTH_TOKEN_AUDIENCE)
.withIssuedAt(new Date(now))
.withExpiresAt(new Date(now + EXPIRATION_TIME_IN_MILLIS))
.withClaim("target_audience", targetURL)
.sign(algorithm);
Specifically the change is the withClaim() call which now contains the URL I want to call. With that in place the code returns the JWT I was hoping for and which does, in fact, work when I call the secured HTTP trigger URL. Hope that helps someone else.
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.
I am new to OAuth and I'm developing an Android app that requires it and I'm using the signpost library.
I believe I have it all working up to the point where I can get the token using
String token = uri.getQueryParameter("oauth_token");
Which returns correctly however the very next line
String verifier = uri.getQueryParameter("oauth_verifier");
returns null. I have debugged and inspected uri which is initialized as
Uri uri = this.getIntent().getData();
And it does not appear to have a key "oauth_verifier"
The verifier being null causes an OAuthExpectationFailedException when I try the ofllowing line
provider.retrieveAccessToken(consumer, verifier);
Can anyone help me figure out why the verifier is null I am using a callback not OOB.
I have seen in other questions and guides the likes of this in the response:
dat=myapp://twitter?oauth_token=tJpJHOOwoTGMwdvHyYbfX2tyHKOp0Y2kdRRZf3sM&
oauth_verifier=xc49oM8eVVmK46ZSLz2RMT2uqXn3SxrMxf5ZAMXaD2Y
Mine is similar but without the ouath_verifier key.
Here is where I send the initial intent which works and I get a return
String clientKey = "xxxxxxx";
String clientSecret = "yyyyyyyyy";
CommonsHttpOAuthConsumer consumer = new CommonsHttpOAuthConsumer(clientKey,clientSecret);
provider = new CommonsHttpOAuthProvider(URL_Request_Token,
URL_Access_Token,
URL_Authorize";
provider.setOAuth10a(true);
try {
String authUrl = provider.retrieveRequestToken(consumer,CALLBACK_URI.toString());
String token = consumer.getToken();
String secret = consumer.getTokenSecret();
this.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl)));
}
Any help is much appreciated
A problem I had while developing a twitter interaction sample is that I forgot to set a callback url from the twitter app console.
If you don't do that, your application won't be redirected and so it won't be able to grap the verifier token.
PROMO MODE ON
However, if you feel particularly brave, I've been working on this library PostManLib lately and I am looking for beta testers. It should handle all the async oauth interaction using the well know scribe library under the hood.
PROMO MODE OFF
I am writing a web application and have just implemented that a user can sign in via Twitter, using spring-social-(core/twitter).
However, Twitter behaves strangely. After the initial authentication/authorization, every time I'm sending a user to Twitter for authentication, Twitter prompts to authorize my application again. I've looked into the connected Twitter profile. My app is there and authorized correctly (in my case for read access).
I don't have a case of requesting additional permissions. All my application needs is read access (the authorization dialog confirms this).
I am using the OAuth1Operations (returned by the TwitterConnectionFactory) to do the OAuth dance and save the resulting connection in a database. My front-end is written with Wicket 1.5.
I can work around this behavior by just re-authorizing my app again and again when I want to sign in via Twitter, but this is a big nuisance. Anyone knows what I'm missing here?
Here is my code:
TwitterConnectionFactory connectionFactory = (TwitterConnectionFactory) connectionFactoryLocator.getConnectionFactory(Twitter.class);
String callbackUrl = [...];
if (pageParameters.get("oauth_token").isNull() || pageParameters.get("oauth_verifier").isNull()) {
MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
params.add("x_auth_access_type", "read");
OAuthToken token = connectionFactory.getOAuthOperations().fetchRequestToken(callbackUrl, params);
String url = connectionFactory.getOAuthOperations().buildAuthorizeUrl(token.getValue(), OAuth1Parameters.NONE);
getSession().setAttribute("twitter_token", token);
setResponsePage(new RedirectPage(url));
} else {
String token = pageParameters.get("oauth_token").toString();
String verifier = pageParameters.get("oauth_verifier").toString();
OAuthToken previousToken = (OAuthToken) getSession().getAttribute("twitter_token");
if (previousToken.getValue().equals(token)) {
AuthorizedRequestToken authorizedRequestToken = new AuthorizedRequestToken(previousToken, verifier);
OAuthToken accessToken = connectionFactory.getOAuthOperations().exchangeForAccessToken(authorizedRequestToken, null);
Connection<Twitter> connection = connectionFactory.createConnection(accessToken);
}
}
I've found the solution! It is also detailed here: Simple Twitter Oauth authorization asking for credentials every time
The problem was that I specifically requested Twitter to authorize my app every time. Replacing:
String url = connectionFactory.getOAuthOperations().buildAuthorizeUrl(token.getValue(), OAuth1Parameters.NONE);
with
String url = connectionFactory.getOAuthOperations().buildAuthenticateUrl(token.getValue(), OAuth1Parameters.NONE);
solves the issue!
Calling the URL for authentication does only ask for authorization if the app hasn't been authorized yet.
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