Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Google OAuth 2 API support. #210

Closed
wants to merge 5 commits into from

10 participants

@lsolova

Hello Pablo,

I have to support Google OAuth 2 login in my project. I added this API to the apis, and there are some additional changes too. First of all the grant_type is a required parameter in OAuth 2, so it could be set via API objects yet. The Google API requires some changes in request parameter handling, because they wanna use POST instead of GET. So I merged body parameters and querystring parameters, and only the Verb defines the request method. The Google access_token answer contains whitespaces between the name and the colon - I changed the regex pattern of JsonTokenExtractor to tolerate this.

Please, check my changes, and if you find them useful, then pull it back please in the main project. I tested the new version against Facebook, Live and Google via OAuth 2 interfaces.

Thanks for this great library!

Regards,
Laszlo

@netbrain

I wrote a patch myself for Oauth 2 and sent it to pablo right before xmas. Code looks pretty identical. guess i should have made a pull request like you so it would have been publicly available. sorry for that :)

@pledbrook

Will this make it into Scribe?

@santiagopoli

It would be nice if this functionality make its way into scribe.

@yincrash

For you guys who want it before it gets included into scribe, I wrote a version that is self contained and requires no modification to the library (so you can include it in your project now). This doesn't handle the "state" parameter, because it's optional and requires, well, state. In my app, it's handled outside the library.

https://gist.github.com/2465453

Wrote it w/o looking at Isolova's version. Ideally, the library itself should be updated to take into account the current state of the OAuth 2.0 spec (v25), such as the "grant_type" parameter which is required to be "authorization_code" for the authorization code grant flow (4.1.3), and handling the POST verb for the access token request. See my extension of OAuth20ServiceImpl at the bottom of the gist.

@mebibou

I've taken the Google2Api code from yincrash, and tried to retrieve the Google Contacts with it, but I always get an error 401 like 'There was an error in your request'. Has anyone been able to make the Google Contacts work? I was trying to access https://www.google.com/m8/feeds/contacts/default/full with my freshly retrieve oauth 2.0 tokens, but no luck. I've also been trying using the gdata library but I have a 'No authentication header information'.

Note: I have tried the same things by using the GoogleApi instead and it works, these errors only appear with oauth 2.0

@stackmagic

a method that's named getByteBodyContents uses query-string params? I don't think that's what it's intended to do? Does it break any of the gazillion other apis that scribe supports?

@benmccann

@mebibou I was able to retrieve Google Contacts using the code provided by @yincrash

@benmccann

Is there a reason this was closed and not merged yet? I'd really like to see it in Scribe.

@fernandezpablo85

I closed most tickets today, here's the reason:

https://github.com/fernandezpablo85/scribe-java/wiki/Scribe-scope-revised

Sorry :(

@literakl

Pity. At least I will learn github forking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 2, 2012
  1. @lsolova
  2. @lsolova
  3. @lsolova
  4. @lsolova

    We can set up request method via Verb, so no different body and query…

    lsolova authored
    … parameters required. I removed the body parameters and replaced with query parameters. This is necessary for Google OAuth 2 POST support.
  5. @lsolova

    New Google OAuth 2 API.

    lsolova authored
This page is out of date. Refresh to see the latest.
View
6 src/main/java/org/scribe/builder/api/DefaultApi20.java
@@ -58,6 +58,12 @@ public Verb getAccessTokenVerb()
* @return the URL where you should redirect your users
*/
public abstract String getAuthorizationUrl(OAuthConfig config);
+
+ /**
+ * Returns the grant_type parameter value for access token requests.
+ * @return the grant_type parameter placed in access token request
+ */
+ public abstract String getGrantType();
/**
* {@inheritDoc}
View
13 src/main/java/org/scribe/builder/api/FacebookApi.java
@@ -14,6 +14,19 @@ public String getAccessTokenEndpoint()
{
return "https://graph.facebook.com/oauth/access_token";
}
+
+ /* Modified from client_credentials to null, because no session ID was
+ * received in the access_token, so https://graph.facebook.com/me gives back
+ * an OAuthException: "An active access token must be used to query
+ * information about the current user."
+ *
+ * If you want to use this API for App Login, please override this method to
+ * return "client_credentials".
+ */
+ @Override
+ public String getGrantType() {
+ return null;
+ }
@Override
public String getAuthorizationUrl(OAuthConfig config)
View
7 src/main/java/org/scribe/builder/api/Foursquare2Api.java
@@ -11,8 +11,13 @@
@Override
public String getAccessTokenEndpoint()
{
- return "https://foursquare.com/oauth2/access_token?grant_type=authorization_code";
+ return "https://foursquare.com/oauth2/access_token";
}
+
+ @Override
+ public String getGrantType() {
+ return "authorization_code";
+ }
@Override
public String getAuthorizationUrl(OAuthConfig config)
View
38 src/main/java/org/scribe/builder/api/Google2Api.java
@@ -0,0 +1,38 @@
+package org.scribe.builder.api;
+
+import org.scribe.extractors.AccessTokenExtractor;
+import org.scribe.extractors.JsonTokenExtractor;
+import org.scribe.model.OAuthConfig;
+import org.scribe.model.Verb;
+import org.scribe.utils.OAuthEncoder;
+
+public class Google2Api extends DefaultApi20 {
+
+ private static final String AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/auth?scope=%s&redirect_uri=%s&response_type=code&client_id=%s";
+
+ @Override
+ public AccessTokenExtractor getAccessTokenExtractor() {
+ return new JsonTokenExtractor();
+ }
+
+ @Override
+ public Verb getAccessTokenVerb() {
+ return Verb.POST;
+ }
+
+ @Override
+ public String getAccessTokenEndpoint() {
+ return "https://accounts.google.com/o/oauth2/token";
+ }
+
+ @Override
+ public String getGrantType() {
+ return "authorization_code";
+ }
+
+ @Override
+ public String getAuthorizationUrl(OAuthConfig config) {
+ return String.format(AUTHORIZE_URL, OAuthEncoder.encode(config.getScope()),OAuthEncoder.encode(config.getCallback()),config.getApiKey());
+ }
+
+}
View
7 src/main/java/org/scribe/builder/api/LiveApi.java
@@ -13,7 +13,12 @@
@Override
public String getAccessTokenEndpoint()
{
- return "https://oauth.live.com/token?grant_type=authorization_code";
+ return "https://oauth.live.com/token";
+ }
+
+ @Override
+ public String getGrantType() {
+ return "authorization_code";
}
@Override
View
5 src/main/java/org/scribe/builder/api/VkontakteApi.java
@@ -18,6 +18,11 @@ public String getAccessTokenEndpoint()
{
return "https://api.vkontakte.ru/oauth/access_token";
}
+
+ @Override
+ public String getGrantType() {
+ return "client_credentials";
+ }
@Override
public String getAuthorizationUrl(OAuthConfig config)
View
5 src/main/java/org/scribe/extractors/JsonTokenExtractor.java
@@ -8,9 +8,8 @@
public class JsonTokenExtractor implements AccessTokenExtractor
{
- private Pattern accessTokenPattern = Pattern.compile("\"access_token\":\\s*\"(\\S*?)\"");
-
- @Override
+ private Pattern accessTokenPattern = Pattern.compile("\"access_token\"\\s*:\\s*\"(\\S*?)\"");
+
public Token extract(String response)
{
Preconditions.checkEmptyString(response, "Cannot extract a token from a null or empty String");
View
1  src/main/java/org/scribe/model/OAuthConstants.java
@@ -43,6 +43,7 @@
public static final String ACCESS_TOKEN = "access_token";
public static final String CLIENT_ID = "client_id";
public static final String CLIENT_SECRET = "client_secret";
+ public static final String GRANT_TYPE = "grant_type";
public static final String REDIRECT_URI = "redirect_uri";
public static final String CODE = "code";
View
5 src/main/java/org/scribe/model/Parameter.java
@@ -1,8 +1,5 @@
package org.scribe.model;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import org.scribe.exceptions.OAuthException;
import org.scribe.utils.OAuthEncoder;
/**
@@ -10,8 +7,6 @@
*/
public class Parameter implements Comparable<Parameter>
{
- private static final String UTF = "UTF8";
-
private final String key;
private final String value;
View
4 src/main/java/org/scribe/model/Request.java
@@ -145,6 +145,8 @@ public void addHeader(String key, String value)
*
* @param key the parameter name
* @param value the parameter value
+ *
+ * @deprecated Use addQuerystringParameter instead
*/
public void addBodyParameter(String key, String value)
{
@@ -260,7 +262,7 @@ public String getBodyContents()
byte[] getByteBodyContents()
{
if (bytePayload != null) return bytePayload;
- String body = (payload != null) ? payload : bodyParams.asFormUrlEncodedString();
+ String body = (payload != null) ? payload : querystringParams.asFormUrlEncodedString();
try
{
return body.getBytes(getCharset());
View
12 src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java
@@ -1,7 +1,12 @@
package org.scribe.oauth;
-import org.scribe.builder.api.*;
-import org.scribe.model.*;
+import org.scribe.builder.api.DefaultApi20;
+import org.scribe.model.OAuthConfig;
+import org.scribe.model.OAuthConstants;
+import org.scribe.model.OAuthRequest;
+import org.scribe.model.Response;
+import org.scribe.model.Token;
+import org.scribe.model.Verifier;
public class OAuth20ServiceImpl implements OAuthService
{
@@ -32,7 +37,8 @@ public Token getAccessToken(Token requestToken, Verifier verifier)
request.addQuerystringParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret());
request.addQuerystringParameter(OAuthConstants.CODE, verifier.getValue());
request.addQuerystringParameter(OAuthConstants.REDIRECT_URI, config.getCallback());
- if(config.hasScope()) request.addQuerystringParameter(OAuthConstants.SCOPE, config.getScope());
+ if( api.getGrantType() != null ) request.addQuerystringParameter(OAuthConstants.GRANT_TYPE, api.getGrantType());
+ if( config.hasScope() ) request.addQuerystringParameter(OAuthConstants.SCOPE, config.getScope());
Response response = request.send();
return api.getAccessTokenExtractor().extract(response.getBody());
}
View
2  src/test/java/org/scribe/examples/TwitterExample.java
@@ -46,7 +46,7 @@ public static void main(String[] args)
// Now let's go and ask for a protected resource!
System.out.println("Now we're going to access a protected resource...");
OAuthRequest request = new OAuthRequest(Verb.POST, PROTECTED_RESOURCE_URL);
- request.addBodyParameter("status", "this is sparta! *");
+ request.addQuerystringParameter("status", "this is sparta! *");
service.signRequest(accessToken, request);
Response response = request.send();
System.out.println("Got it! Lets see what we found...");
View
2  src/test/java/org/scribe/extractors/BaseStringExtractorTest.java
@@ -46,7 +46,7 @@ public void shouldThrowExceptionIfRquestHasNoOAuthParameters()
public void shouldProperlyEncodeSpaces()
{
String expected = "GET&http%3A%2F%2Fexample.com&body%3Dthis%2520param%2520has%2520whitespace%26oauth_callback%3Dhttp%253A%252F%252Fexample%252Fcallback%26oauth_consumer_key%3DAS%2523%2524%255E%252A%2540%2526%26oauth_signature%3DOAuth-Signature%26oauth_timestamp%3D123456";
- request.addBodyParameter("body", "this param has whitespace");
+ request.addQuerystringParameter("body", "this param has whitespace");
assertEquals(expected, extractor.extract(request));
}
}
View
6 src/test/java/org/scribe/model/RequestTest.java
@@ -15,8 +15,8 @@ public void setup() throws Exception
{
connection = new ConnectionStub();
postRequest = new Request(Verb.POST, "http://example.com");
- postRequest.addBodyParameter("param", "value");
- postRequest.addBodyParameter("param with spaces", "value with spaces");
+ postRequest.addQuerystringParameter("param", "value");
+ postRequest.addQuerystringParameter("param with spaces", "value with spaces");
postRequest.setConnection(connection);
getRequest = new Request(Verb.GET, "http://example.com?qsparam=value&other+param=value+with+spaces");
getRequest.setConnection(connection);
@@ -33,7 +33,7 @@ public void shouldSetRequestVerb()
public void shouldGetQueryStringParameters()
{
assertEquals(2, getRequest.getQueryStringParams().size());
- assertEquals(0, postRequest.getQueryStringParams().size());
+ assertEquals(2, postRequest.getQueryStringParams().size());
assertTrue(getRequest.getQueryStringParams().contains(new Parameter("qsparam", "value")));
}
Something went wrong with that request. Please try again.