From c7176c97e297ec9a419918c7202c8996b6714d6d Mon Sep 17 00:00:00 2001 From: diegossilveira Date: Thu, 10 Feb 2011 16:54:28 -0200 Subject: [PATCH 1/7] Added access_token related methods to OAuth 2.0 APIs --- .../org/scribe/builder/api/DefaultApi20.java | 35 +++++++++++++++++-- .../org/scribe/builder/api/FacebookApi.java | 8 ++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/scribe/builder/api/DefaultApi20.java b/src/main/java/org/scribe/builder/api/DefaultApi20.java index 6e03567..cc09db3 100644 --- a/src/main/java/org/scribe/builder/api/DefaultApi20.java +++ b/src/main/java/org/scribe/builder/api/DefaultApi20.java @@ -1,7 +1,11 @@ package org.scribe.builder.api; -import org.scribe.model.*; -import org.scribe.oauth.*; +import org.scribe.extractors.AccessTokenExtractor; +import org.scribe.extractors.TokenExtractor20Impl; +import org.scribe.model.OAuthConfig; +import org.scribe.model.Verb; +import org.scribe.oauth.OAuth20ServiceImpl; +import org.scribe.oauth.OAuthService; /** * Default implementation of the OAuth protocol, version 2.0 (draft 11) @@ -22,6 +26,33 @@ import org.scribe.oauth.*; public abstract class DefaultApi20 implements Api { + /** + * Returns the access token extractor. + * + * @return access token extractor + */ + public AccessTokenExtractor getAccessTokenExtractor() + { + return new TokenExtractor20Impl(); + } + + /** + * Returns the verb for the access token endpoint (defaults to GET) + * + * @return access token endpoint verb + */ + public Verb getAccessTokenVerb() + { + return Verb.GET; + } + + /** + * Returns the URL that receives the access token requests. + * + * @return access token URL + */ + public abstract String getAccessTokenEndpoint(); + /** * Returns the URL where you should redirect your users to authenticate * your application. diff --git a/src/main/java/org/scribe/builder/api/FacebookApi.java b/src/main/java/org/scribe/builder/api/FacebookApi.java index c945520..70784aa 100644 --- a/src/main/java/org/scribe/builder/api/FacebookApi.java +++ b/src/main/java/org/scribe/builder/api/FacebookApi.java @@ -5,8 +5,14 @@ import org.scribe.utils.*; public class FacebookApi extends DefaultApi20 { - private static final String AUTHORIZE_URL = "https://graph.facebook.com/oauth/authorize?response_type=token&client_id=%s&redirect_uri=%s"; + private static final String AUTHORIZE_URL = "https://www.facebook.com/dialog/oauth?client_id=%s&redirect_uri=%s"; + @Override + public String getAccessTokenEndpoint() + { + return "https://graph.facebook.com/oauth/access_token"; + } + @Override public String getAuthorizationUrl(OAuthConfig config) { From 08aebb5fb17778205ee69e5903c90c61d01da65f Mon Sep 17 00:00:00 2001 From: diegossilveira Date: Thu, 10 Feb 2011 16:56:10 -0200 Subject: [PATCH 2/7] Added specific constants for OAuth 2.0 --- src/main/java/org/scribe/model/OAuthConstants.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/scribe/model/OAuthConstants.java b/src/main/java/org/scribe/model/OAuthConstants.java index 01b98d3..c93c31c 100644 --- a/src/main/java/org/scribe/model/OAuthConstants.java +++ b/src/main/java/org/scribe/model/OAuthConstants.java @@ -41,4 +41,9 @@ public class OAuthConstants //OAuth 2.0 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 REDIRECT_URI = "redirect_uri"; + public static final String CODE = "code"; + } From 581e2b6d84a2bd8235b604211d87074fad4e9a91 Mon Sep 17 00:00:00 2001 From: diegossilveira Date: Thu, 10 Feb 2011 17:03:28 -0200 Subject: [PATCH 3/7] Changed the getAccessToken() method implementation to conform to the OAuth 2.0 server-side flow --- .../org/scribe/oauth/OAuth20ServiceImpl.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java index a81dc88..86407de 100644 --- a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java +++ b/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 { @@ -35,7 +40,13 @@ public class OAuth20ServiceImpl implements OAuthService */ public Token getAccessToken(Token requestToken, Verifier verifier) { - throw new UnsupportedOperationException("Unsupported operation, please use 'getAuthorizationUrl' and redirect your users there"); + OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); + request.addQuerystringParameter(OAuthConstants.CLIENT_ID, config.getApiKey()); + request.addQuerystringParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret()); + request.addQuerystringParameter(OAuthConstants.CODE, verifier.getValue()); + request.addQuerystringParameter(OAuthConstants.REDIRECT_URI, config.getCallback()); + Response response = request.send(); + return api.getAccessTokenExtractor().extract(response.getBody()); } /** From 7385d3702ede1629446b1ee9e62f4c2a7b42c32b Mon Sep 17 00:00:00 2001 From: diegossilveira Date: Thu, 10 Feb 2011 17:09:11 -0200 Subject: [PATCH 4/7] Added specific token extractor for OAuth 2.0 --- .../extractors/TokenExtractor20Impl.java | 35 +++++++++++ .../extractors/TokenExtractor20Test.java | 59 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 src/main/java/org/scribe/extractors/TokenExtractor20Impl.java create mode 100644 src/test/java/org/scribe/extractors/TokenExtractor20Test.java diff --git a/src/main/java/org/scribe/extractors/TokenExtractor20Impl.java b/src/main/java/org/scribe/extractors/TokenExtractor20Impl.java new file mode 100644 index 0000000..8c956c5 --- /dev/null +++ b/src/main/java/org/scribe/extractors/TokenExtractor20Impl.java @@ -0,0 +1,35 @@ +package org.scribe.extractors; + +import java.util.regex.*; + +import org.scribe.exceptions.*; +import org.scribe.model.*; +import org.scribe.utils.*; + +/** + * Default implementation of {@AccessTokenExtractor}. Conforms to OAuth 2.0 + * + */ +public class TokenExtractor20Impl implements AccessTokenExtractor +{ + private static final String TOKEN_REGEX = "access_token=(\\S*?)(&(\\S*))?"; + private static final String EMPTY_SECRET = ""; + + /** + * {@inheritDoc} + */ + public Token extract(String response) + { + Preconditions.checkEmptyString(response, "Response body is incorrect. Can't extract a token from an empty string"); + + Matcher matcher = Pattern.compile(TOKEN_REGEX).matcher(response); + if (matcher.matches()) + { + String token = URLUtils.percentDecode(matcher.group(1)); + return new Token(token, EMPTY_SECRET); + } else + { + throw new OAuthException("Response body is incorrect. Can't extract a token from this: '" + response + "'", null); + } + } +} diff --git a/src/test/java/org/scribe/extractors/TokenExtractor20Test.java b/src/test/java/org/scribe/extractors/TokenExtractor20Test.java new file mode 100644 index 0000000..167b2d7 --- /dev/null +++ b/src/test/java/org/scribe/extractors/TokenExtractor20Test.java @@ -0,0 +1,59 @@ +package org.scribe.extractors; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; +import org.scribe.exceptions.OAuthException; +import org.scribe.model.Token; + +public class TokenExtractor20Test +{ + + private TokenExtractor20Impl extractor; + + @Before + public void setup() + { + extractor = new TokenExtractor20Impl(); + } + + @Test + public void shouldExtractTokenFromOAuthStandardResponse() + { + String response = "access_token=166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159|RsXNdKrpxg8L6QNLWcs2TVTmcaE"; + Token extracted = extractor.extract(response); + assertEquals("166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159|RsXNdKrpxg8L6QNLWcs2TVTmcaE", extracted.getToken()); + assertEquals("", extracted.getSecret()); + } + + @Test + public void shouldExtractTokenFromResponseWithExpiresParam() + { + String response = "access_token=166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159|RsXNdKrpxg8L6QNLWcs2TVTmcaE&expires=5108"; + Token extracted = extractor.extract(response); + assertEquals("166942940015970|2.2ltzWXYNDjCtg5ZDVVJJeg__.3600.1295816400-548517159|RsXNdKrpxg8L6QNLWcs2TVTmcaE", extracted.getToken()); + assertEquals("", extracted.getSecret()); + } + + @Test(expected = OAuthException.class) + public void shouldThrowExceptionIfTokenIsAbsent() + { + String response = "&expires=5108"; + extractor.extract(response); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowExceptionIfResponseIsNull() + { + String response = null; + extractor.extract(response); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowExceptionIfResponseIsEmptyString() + { + String response = ""; + extractor.extract(response); + } +} From 02f3d4dac242042dea1afff227bea1452e36247b Mon Sep 17 00:00:00 2001 From: diegossilveira Date: Thu, 10 Feb 2011 17:13:03 -0200 Subject: [PATCH 5/7] Changed the Facebook example to conform to the OAuth 2.0 server-side flow --- .../org/scribe/examples/FacebookExample.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/scribe/examples/FacebookExample.java b/src/test/java/org/scribe/examples/FacebookExample.java index 74e8ed0..02f3396 100644 --- a/src/test/java/org/scribe/examples/FacebookExample.java +++ b/src/test/java/org/scribe/examples/FacebookExample.java @@ -11,19 +11,19 @@ public class FacebookExample { private static final String NETWORK_NAME = "Facebook"; private static final String PROTECTED_RESOURCE_URL = "https://graph.facebook.com/me"; - private static final String NO_SECRET_NEEDED = ""; private static final Token EMPTY_TOKEN = null; public static void main(String[] args) { // Replace these with your own api key and secret - String apiKey = "your_api_key"; - String apiSecret = "your_api_secret"; + String apiKey = "151212568269315";//"your_api_key"; + String apiSecret = "783080a95ff0e1e7266c8022b2e95fa2";//"your_api_secret"; OAuthService service = new ServiceBuilder() .provider(FacebookApi.class) .apiKey(apiKey) .apiSecret(apiSecret) - .callback("http://www.example.com/oauth_callback/") + //.callback("http://www.example.com/oauth_callback/") + .callback("https://github.com/diegossilveira/") .build(); Scanner in = new Scanner(System.in); @@ -36,9 +36,16 @@ public class FacebookExample System.out.println("Got the Authorization URL!"); System.out.println("Now go and authorize Scribe here:"); System.out.println(authorizationUrl); - System.out.println("And paste the access token here"); + System.out.println("And paste the authorization code here"); System.out.print(">>"); - Token accessToken = new Token(in.nextLine(), NO_SECRET_NEEDED); + Verifier verifier = new Verifier(in.nextLine()); + System.out.println(); + + // Trade the Request Token and Verfier for the Access Token + System.out.println("Trading the Request Token for an Access Token..."); + Token accessToken = service.getAccessToken(EMPTY_TOKEN, verifier); + System.out.println("Got the Access Token!"); + System.out.println("(if your curious it looks like this: " + accessToken + " )"); System.out.println(); // Now let's go and ask for a protected resource! From db37744fa39e982903ef6ab32d1553a6d695a543 Mon Sep 17 00:00:00 2001 From: diegossilveira Date: Thu, 10 Feb 2011 21:54:46 -0200 Subject: [PATCH 6/7] Fixed package imports --- src/main/java/org/scribe/builder/api/DefaultApi20.java | 9 +++------ src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java | 9 ++------- .../java/org/scribe/extractors/TokenExtractor20Test.java | 9 ++++----- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/scribe/builder/api/DefaultApi20.java b/src/main/java/org/scribe/builder/api/DefaultApi20.java index cc09db3..7e3eb9b 100644 --- a/src/main/java/org/scribe/builder/api/DefaultApi20.java +++ b/src/main/java/org/scribe/builder/api/DefaultApi20.java @@ -1,11 +1,8 @@ package org.scribe.builder.api; -import org.scribe.extractors.AccessTokenExtractor; -import org.scribe.extractors.TokenExtractor20Impl; -import org.scribe.model.OAuthConfig; -import org.scribe.model.Verb; -import org.scribe.oauth.OAuth20ServiceImpl; -import org.scribe.oauth.OAuthService; +import org.scribe.extractors.*; +import org.scribe.model.*; +import org.scribe.oauth.*; /** * Default implementation of the OAuth protocol, version 2.0 (draft 11) diff --git a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java index 86407de..94b0cbe 100644 --- a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java +++ b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java @@ -1,12 +1,7 @@ package org.scribe.oauth; -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; +import org.scribe.builder.api.*; +import org.scribe.model.*; public class OAuth20ServiceImpl implements OAuthService { diff --git a/src/test/java/org/scribe/extractors/TokenExtractor20Test.java b/src/test/java/org/scribe/extractors/TokenExtractor20Test.java index 167b2d7..17f1a01 100644 --- a/src/test/java/org/scribe/extractors/TokenExtractor20Test.java +++ b/src/test/java/org/scribe/extractors/TokenExtractor20Test.java @@ -1,11 +1,10 @@ package org.scribe.extractors; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; -import org.junit.Before; -import org.junit.Test; -import org.scribe.exceptions.OAuthException; -import org.scribe.model.Token; +import org.junit.*; +import org.scribe.exceptions.*; +import org.scribe.model.*; public class TokenExtractor20Test { From de8f0d621d7427b9079af0f6b791c8e000cf90a3 Mon Sep 17 00:00:00 2001 From: diegossilveira Date: Thu, 10 Feb 2011 17:13:03 -0200 Subject: [PATCH 7/7] Changed the Facebook example to conform to the OAuth 2.0 server-side flow --- src/test/java/org/scribe/examples/FacebookExample.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/scribe/examples/FacebookExample.java b/src/test/java/org/scribe/examples/FacebookExample.java index 02f3396..d5dc3cb 100644 --- a/src/test/java/org/scribe/examples/FacebookExample.java +++ b/src/test/java/org/scribe/examples/FacebookExample.java @@ -16,14 +16,13 @@ public class FacebookExample public static void main(String[] args) { // Replace these with your own api key and secret - String apiKey = "151212568269315";//"your_api_key"; - String apiSecret = "783080a95ff0e1e7266c8022b2e95fa2";//"your_api_secret"; + String apiKey = "your_app_id"; + String apiSecret = "your_api_secret"; OAuthService service = new ServiceBuilder() .provider(FacebookApi.class) .apiKey(apiKey) .apiSecret(apiSecret) - //.callback("http://www.example.com/oauth_callback/") - .callback("https://github.com/diegossilveira/") + .callback("http://www.example.com/oauth_callback/") .build(); Scanner in = new Scanner(System.in);