From 32d3e069fa3a0288cbc9a4e17ca4769dd3d40d40 Mon Sep 17 00:00:00 2001 From: hide kojima Date: Wed, 25 May 2011 17:28:48 -0700 Subject: [PATCH 01/27] Added a support for OAuth 2.0 Grant Type. For instance, Grant Type is used in Facebook App Login Change-Id: Id0abb4ddedd205428e63b43c5d7a9bfe5ad87be4 --- .../org/scribe/builder/ServiceBuilder.java | 14 +++++++- .../org/scribe/builder/api/FacebookApi.java | 36 +++++++++++++++++-- .../java/org/scribe/model/OAuthConfig.java | 22 ++++++++++++ .../java/org/scribe/model/OAuthConstants.java | 1 + .../org/scribe/oauth/OAuth20ServiceImpl.java | 1 + .../scribe/builder/ServiceBuilderTest.java | 9 +++++ .../org/scribe/model/OAuthConfigTest.java | 1 + 7 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/scribe/builder/ServiceBuilder.java b/src/main/java/org/scribe/builder/ServiceBuilder.java index 10d080282..9fa884f69 100644 --- a/src/main/java/org/scribe/builder/ServiceBuilder.java +++ b/src/main/java/org/scribe/builder/ServiceBuilder.java @@ -21,6 +21,7 @@ public class ServiceBuilder private Api api; private String scope; private SignatureType signatureType; + private String grantType; /** * Default constructor @@ -137,6 +138,17 @@ public ServiceBuilder signatureType(SignatureType type) return this; } + /** + * Configures the grant type + * @return + */ + public ServiceBuilder grantType(String grantType) + { + Preconditions.checkEmptyString(grantType, "Invalid OAuth Grant Type"); + this.grantType = grantType; + return this; + } + /** * Returns the fully configured {@link OAuthService} * @@ -147,6 +159,6 @@ public OAuthService build() Preconditions.checkNotNull(api, "You must specify a valid api through the provider() method"); Preconditions.checkEmptyString(apiKey, "You must provide an api key"); Preconditions.checkEmptyString(apiSecret, "You must provide an api secret"); - return api.createService(new OAuthConfig(apiKey, apiSecret, callback, signatureType, scope)); + return api.createService(new OAuthConfig(apiKey, apiSecret, callback, signatureType, scope,grantType)); } } diff --git a/src/main/java/org/scribe/builder/api/FacebookApi.java b/src/main/java/org/scribe/builder/api/FacebookApi.java index d85ff3b65..db6a1f640 100644 --- a/src/main/java/org/scribe/builder/api/FacebookApi.java +++ b/src/main/java/org/scribe/builder/api/FacebookApi.java @@ -9,6 +9,16 @@ public class FacebookApi extends DefaultApi20 { private static final String AUTHORIZE_URL = "https://www.facebook.com/dialog/oauth?client_id=%s&redirect_uri=%s"; private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s"; + private static final String GRANT_TYPE_PARAM = "&grant_type=%s"; + private static final String CLIENT_SECRET_PARAM = "&client_secret=%s"; + + private static final String SCOPED_GRANT_TYPED_URL = SCOPED_AUTHORIZE_URL+GRANT_TYPE_PARAM; + private static final String GRANT_TYPED_URL = AUTHORIZE_URL + GRANT_TYPE_PARAM; + private static final String SCOPED_GRANT_TYPED_CLIENT_SECRET_URL = SCOPED_AUTHORIZE_URL+GRANT_TYPE_PARAM+CLIENT_SECRET_PARAM; + private static final String GRANT_TYPED_CLIENT_SECRET_URL = AUTHORIZE_URL + GRANT_TYPE_PARAM+CLIENT_SECRET_PARAM; + + + public static final String GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials"; @Override public String getAccessTokenEndpoint() @@ -22,9 +32,31 @@ public String getAuthorizationUrl(OAuthConfig config) Preconditions.checkValidUrl(config.getCallback(), "Must provide a valid url as callback. Facebook does not support OOB"); // Append scope if present - if(config.hasScope()) + if(config.hasScope() && !config.hasGrantType()) { - return String.format(SCOPED_AUTHORIZE_URL, config.getApiKey(), formURLEncode(config.getCallback()), formURLEncode(config.getScope())); + return String.format(SCOPED_AUTHORIZE_URL, config.getApiKey(), formURLEncode(config.getCallback()), formURLEncode(config.getScope())); + } + else if(config.hasScope() && config.hasGrantType()) + { + if(GRANT_TYPE_CLIENT_CREDENTIALS.equals(config.getGrantType())) + { + return String.format(SCOPED_GRANT_TYPED_CLIENT_SECRET_URL, + config.getApiKey(),formURLEncode(config.getCallback()), + formURLEncode(config.getScope()),formURLEncode(config.getGrantType()),config.getApiSecret()); + } + return String.format(SCOPED_GRANT_TYPED_URL, config.getApiKey(),formURLEncode(config.getCallback()), + formURLEncode(config.getScope()),formURLEncode(config.getGrantType())); + } + else if(!config.hasScope() && config.hasGrantType()) + { + if(GRANT_TYPE_CLIENT_CREDENTIALS.equals(config.getGrantType())) + { + return String.format(GRANT_TYPED_CLIENT_SECRET_URL, + config.getApiKey(),formURLEncode(config.getCallback()), + formURLEncode(config.getGrantType()),config.getApiSecret()); + } + return String.format(GRANT_TYPED_URL, config.getApiKey(),formURLEncode(config.getCallback()), + formURLEncode(config.getGrantType())); } else { diff --git a/src/main/java/org/scribe/model/OAuthConfig.java b/src/main/java/org/scribe/model/OAuthConfig.java index 314d2d9b4..d9decda64 100644 --- a/src/main/java/org/scribe/model/OAuthConfig.java +++ b/src/main/java/org/scribe/model/OAuthConfig.java @@ -12,6 +12,7 @@ public class OAuthConfig private final String callback; private final SignatureType signatureType; private final String scope; + private final String grantType; public OAuthConfig(String key, String secret) { @@ -25,6 +26,17 @@ public OAuthConfig(String key, String secret, String callback, SignatureType typ this.callback = callback != null ? callback : OAuthConstants.OUT_OF_BAND; this.signatureType = (type != null) ? type : SignatureType.Header; this.scope = scope; + this.grantType = null; + } + + public OAuthConfig(String key, String secret, String callback, SignatureType type, String scope, String grantType) + { + this.apiKey = key; + this.apiSecret = secret; + this.callback = callback != null ? callback : OAuthConstants.OUT_OF_BAND; + this.signatureType = (type != null) ? type : SignatureType.Header; + this.scope = scope; + this.grantType = grantType; } public String getApiKey() @@ -51,9 +63,19 @@ public String getScope() { return scope; } + + public String getGrantType() + { + return grantType; + } public boolean hasScope() { return scope != null; } + + public boolean hasGrantType() + { + return grantType != null; + } } \ No newline at end of file diff --git a/src/main/java/org/scribe/model/OAuthConstants.java b/src/main/java/org/scribe/model/OAuthConstants.java index c93c31c71..7f6abd566 100644 --- a/src/main/java/org/scribe/model/OAuthConstants.java +++ b/src/main/java/org/scribe/model/OAuthConstants.java @@ -45,5 +45,6 @@ public class OAuthConstants public static final String CLIENT_SECRET = "client_secret"; public static final String REDIRECT_URI = "redirect_uri"; public static final String CODE = "code"; + public static final String GRANT_TYPE = "grant_type"; } diff --git a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java index 6262c3700..368834052 100644 --- a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java +++ b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java @@ -33,6 +33,7 @@ public Token getAccessToken(Token requestToken, Verifier verifier) request.addQuerystringParameter(OAuthConstants.CODE, verifier.getValue()); request.addQuerystringParameter(OAuthConstants.REDIRECT_URI, config.getCallback()); if(config.hasScope()) request.addQuerystringParameter(OAuthConstants.SCOPE, config.getScope()); + if(config.hasGrantType()) request.addQuerystringParameter(OAuthConstants.GRANT_TYPE, config.getGrantType()); Response response = request.send(); return api.getAccessTokenExtractor().extract(response.getBody()); } diff --git a/src/test/java/org/scribe/builder/ServiceBuilderTest.java b/src/test/java/org/scribe/builder/ServiceBuilderTest.java index fca633be8..64b61e114 100644 --- a/src/test/java/org/scribe/builder/ServiceBuilderTest.java +++ b/src/test/java/org/scribe/builder/ServiceBuilderTest.java @@ -59,6 +59,15 @@ public void shouldAcceptAnScope() assertEquals(ApiMock.config.getApiSecret(), "secret"); assertEquals(ApiMock.config.getScope(), "rss-api"); } + @Test + public void shouldAcceptAGrantType() + { + builder.provider(ApiMock.class).apiKey("key").apiSecret("secret").grantType("client_credentials").build(); + assertEquals(ApiMock.config.getApiKey(), "key"); + assertEquals(ApiMock.config.getApiSecret(), "secret"); + assertEquals(ApiMock.config.getGrantType(), "client_credentials"); + } + public static class ApiMock implements Api { diff --git a/src/test/java/org/scribe/model/OAuthConfigTest.java b/src/test/java/org/scribe/model/OAuthConfigTest.java index 973d40112..758d5296b 100644 --- a/src/test/java/org/scribe/model/OAuthConfigTest.java +++ b/src/test/java/org/scribe/model/OAuthConfigTest.java @@ -14,6 +14,7 @@ public void shouldReturnDefaultValuesIfNotSet() assertEquals(OAuthConstants.OUT_OF_BAND, config.getCallback()); assertEquals(SignatureType.Header, config.getSignatureType()); assertFalse(config.hasScope()); + assertFalse(config.hasGrantType()); } @Test From fb0a1196fa75e5e1519b886bbde81f9dbf723c07 Mon Sep 17 00:00:00 2001 From: hide kojima Date: Wed, 25 May 2011 18:00:06 -0700 Subject: [PATCH 02/27] Added a sample for Facebook App Login and Graph API (insights) call Change-Id: Ie719bb118a98d4794d5da6c9aad2a281c7caf413 --- bundle | 0 .../examples/FacebookAppLoginExample.java | 71 +++++++++++++++++++ 2 files changed, 71 insertions(+) mode change 100755 => 100644 bundle create mode 100644 src/test/java/org/scribe/examples/FacebookAppLoginExample.java diff --git a/bundle b/bundle old mode 100755 new mode 100644 diff --git a/src/test/java/org/scribe/examples/FacebookAppLoginExample.java b/src/test/java/org/scribe/examples/FacebookAppLoginExample.java new file mode 100644 index 000000000..9a64adbea --- /dev/null +++ b/src/test/java/org/scribe/examples/FacebookAppLoginExample.java @@ -0,0 +1,71 @@ +package org.scribe.examples; + +import java.util.Scanner; + +import org.scribe.builder.ServiceBuilder; +import org.scribe.builder.api.FacebookApi; +import org.scribe.model.OAuthRequest; +import org.scribe.model.Response; +import org.scribe.model.Token; +import org.scribe.model.Verb; +import org.scribe.model.Verifier; +import org.scribe.oauth.OAuthService; + +public class FacebookAppLoginExample +{ + private static final String NETWORK_NAME = "Facebook"; + private static final Token EMPTY_TOKEN = null; + private static final String PROTECTED_RESOURCE_URL ="https://graph.facebook.com/%s/insights"; + + + public static void main(String[] args) + { + // Replace these with your own api key and secret + String apiKey = "your_app_id"; + String apiSecret = "your_api_secret"; + String callbackURL = "your_call_back"; + OAuthService service = new ServiceBuilder() + .provider(FacebookApi.class) + .apiKey(apiKey) + .apiSecret(apiSecret) + .callback(callbackURL) + .grantType(FacebookApi.GRANT_TYPE_CLIENT_CREDENTIALS) + .build(); + Scanner in = new Scanner(System.in); + + System.out.println("=== " + NETWORK_NAME + "'s OAuth Workflow ==="); + System.out.println(); + + // Obtain the Authorization URL for App Login + System.out.println("Fetching the Authorization URL..."); + String authorizationUrl = service.getAuthorizationUrl(EMPTY_TOKEN); + 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 authorization code (see url and find &code=...) here"); + System.out.print(">>"); + 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! + System.out.println("Now we're going to access a protected resource..."); + OAuthRequest request = new OAuthRequest(Verb.GET, String.format(PROTECTED_RESOURCE_URL,apiKey)); + service.signRequest(accessToken, request); + Response response = request.send(); + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + System.out.println(response.getCode()); + System.out.println(response.getBody()); + + System.out.println(); + System.out.println("Thats it man! Go and build something awesome with Scribe! :)"); + + } +} From 483a81ecaac05403b6f0d37e609d0333ed9e9d80 Mon Sep 17 00:00:00 2001 From: hide kojima Date: Thu, 26 May 2011 15:47:16 -0700 Subject: [PATCH 03/27] Refactoring. Support for OAuth 2.0 grant type based on draft-ietf-oauth-v2-16 Change-Id: I833f337776fc82fc3dd8af0d2ec68e74ec0ef489 --- .../org/scribe/builder/ServiceBuilder.java | 4 ++- .../org/scribe/builder/api/FacebookApi.java | 23 +++------------ .../java/org/scribe/model/OAuthConstants.java | 5 ++++ .../org/scribe/oauth/OAuth20ServiceImpl.java | 3 +- .../examples/FacebookAppLoginExample.java | 28 ++++--------------- 5 files changed, 20 insertions(+), 43 deletions(-) diff --git a/src/main/java/org/scribe/builder/ServiceBuilder.java b/src/main/java/org/scribe/builder/ServiceBuilder.java index 9fa884f69..4285ab5b5 100644 --- a/src/main/java/org/scribe/builder/ServiceBuilder.java +++ b/src/main/java/org/scribe/builder/ServiceBuilder.java @@ -140,7 +140,9 @@ public ServiceBuilder signatureType(SignatureType type) /** * Configures the grant type - * @return + * + * @param grant type + * @return the {@link ServiceBuilder} instance for method chaining */ public ServiceBuilder grantType(String grantType) { diff --git a/src/main/java/org/scribe/builder/api/FacebookApi.java b/src/main/java/org/scribe/builder/api/FacebookApi.java index db6a1f640..859bb4cba 100644 --- a/src/main/java/org/scribe/builder/api/FacebookApi.java +++ b/src/main/java/org/scribe/builder/api/FacebookApi.java @@ -10,20 +10,17 @@ public class FacebookApi extends DefaultApi20 private static final String AUTHORIZE_URL = "https://www.facebook.com/dialog/oauth?client_id=%s&redirect_uri=%s"; private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s"; private static final String GRANT_TYPE_PARAM = "&grant_type=%s"; - private static final String CLIENT_SECRET_PARAM = "&client_secret=%s"; private static final String SCOPED_GRANT_TYPED_URL = SCOPED_AUTHORIZE_URL+GRANT_TYPE_PARAM; private static final String GRANT_TYPED_URL = AUTHORIZE_URL + GRANT_TYPE_PARAM; - private static final String SCOPED_GRANT_TYPED_CLIENT_SECRET_URL = SCOPED_AUTHORIZE_URL+GRANT_TYPE_PARAM+CLIENT_SECRET_PARAM; - private static final String GRANT_TYPED_CLIENT_SECRET_URL = AUTHORIZE_URL + GRANT_TYPE_PARAM+CLIENT_SECRET_PARAM; + private static final String ACCESS_TOKEN_ENDPOINT = "https://graph.facebook.com/oauth/access_token"; - public static final String GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials"; @Override public String getAccessTokenEndpoint() { - return "https://graph.facebook.com/oauth/access_token"; + return ACCESS_TOKEN_ENDPOINT; } @Override @@ -31,31 +28,19 @@ public String getAuthorizationUrl(OAuthConfig config) { Preconditions.checkValidUrl(config.getCallback(), "Must provide a valid url as callback. Facebook does not support OOB"); - // Append scope if present + // Append scope and/or grant type if present if(config.hasScope() && !config.hasGrantType()) { return String.format(SCOPED_AUTHORIZE_URL, config.getApiKey(), formURLEncode(config.getCallback()), formURLEncode(config.getScope())); } else if(config.hasScope() && config.hasGrantType()) { - if(GRANT_TYPE_CLIENT_CREDENTIALS.equals(config.getGrantType())) - { - return String.format(SCOPED_GRANT_TYPED_CLIENT_SECRET_URL, - config.getApiKey(),formURLEncode(config.getCallback()), - formURLEncode(config.getScope()),formURLEncode(config.getGrantType()),config.getApiSecret()); - } return String.format(SCOPED_GRANT_TYPED_URL, config.getApiKey(),formURLEncode(config.getCallback()), formURLEncode(config.getScope()),formURLEncode(config.getGrantType())); } else if(!config.hasScope() && config.hasGrantType()) { - if(GRANT_TYPE_CLIENT_CREDENTIALS.equals(config.getGrantType())) - { - return String.format(GRANT_TYPED_CLIENT_SECRET_URL, - config.getApiKey(),formURLEncode(config.getCallback()), - formURLEncode(config.getGrantType()),config.getApiSecret()); - } - return String.format(GRANT_TYPED_URL, config.getApiKey(),formURLEncode(config.getCallback()), + return String.format(GRANT_TYPED_URL, config.getApiKey(),formURLEncode(config.getCallback()), formURLEncode(config.getGrantType())); } else diff --git a/src/main/java/org/scribe/model/OAuthConstants.java b/src/main/java/org/scribe/model/OAuthConstants.java index 7f6abd566..dbf5ae50f 100644 --- a/src/main/java/org/scribe/model/OAuthConstants.java +++ b/src/main/java/org/scribe/model/OAuthConstants.java @@ -46,5 +46,10 @@ public class OAuthConstants public static final String REDIRECT_URI = "redirect_uri"; public static final String CODE = "code"; public static final String GRANT_TYPE = "grant_type"; + // GrantType + public static final String AUTHORIZATION_CODE = "authorization_code"; + public static final String RESOURCE_OWNER_PASSWORD_CREDENTIALS = "password"; + public static final String CLIENT_CREDENTIALS = "client_credentials"; + public static final String REFRESH_TOKEN = "refresh_token"; } diff --git a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java index 368834052..5fecdfde1 100644 --- a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java +++ b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java @@ -30,7 +30,8 @@ public Token getAccessToken(Token requestToken, Verifier verifier) 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()); + // In case of Client Credentials, verfier is not required + if(verifier != null) request.addQuerystringParameter(OAuthConstants.CODE, verifier.getValue()); request.addQuerystringParameter(OAuthConstants.REDIRECT_URI, config.getCallback()); if(config.hasScope()) request.addQuerystringParameter(OAuthConstants.SCOPE, config.getScope()); if(config.hasGrantType()) request.addQuerystringParameter(OAuthConstants.GRANT_TYPE, config.getGrantType()); diff --git a/src/test/java/org/scribe/examples/FacebookAppLoginExample.java b/src/test/java/org/scribe/examples/FacebookAppLoginExample.java index 9a64adbea..c4e6df770 100644 --- a/src/test/java/org/scribe/examples/FacebookAppLoginExample.java +++ b/src/test/java/org/scribe/examples/FacebookAppLoginExample.java @@ -1,26 +1,24 @@ package org.scribe.examples; -import java.util.Scanner; - import org.scribe.builder.ServiceBuilder; import org.scribe.builder.api.FacebookApi; +import org.scribe.model.OAuthConstants; import org.scribe.model.OAuthRequest; import org.scribe.model.Response; import org.scribe.model.Token; import org.scribe.model.Verb; -import org.scribe.model.Verifier; import org.scribe.oauth.OAuthService; public class FacebookAppLoginExample { - private static final String NETWORK_NAME = "Facebook"; + private static final String NETWORK_NAME = "Facebook"; private static final Token EMPTY_TOKEN = null; private static final String PROTECTED_RESOURCE_URL ="https://graph.facebook.com/%s/insights"; - public static void main(String[] args) { // Replace these with your own api key and secret + String apiKey = "your_app_id"; String apiSecret = "your_api_secret"; String callbackURL = "your_call_back"; @@ -29,27 +27,13 @@ public static void main(String[] args) .apiKey(apiKey) .apiSecret(apiSecret) .callback(callbackURL) - .grantType(FacebookApi.GRANT_TYPE_CLIENT_CREDENTIALS) + .grantType(OAuthConstants.CLIENT_CREDENTIALS) .build(); - Scanner in = new Scanner(System.in); System.out.println("=== " + NETWORK_NAME + "'s OAuth Workflow ==="); System.out.println(); - - // Obtain the Authorization URL for App Login - System.out.println("Fetching the Authorization URL..."); - String authorizationUrl = service.getAuthorizationUrl(EMPTY_TOKEN); - 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 authorization code (see url and find &code=...) here"); - System.out.print(">>"); - 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("Getting an access Token with Client Credentials (a.k.a App Login as Facebook defines)"); + Token accessToken = service.getAccessToken(EMPTY_TOKEN, null); System.out.println("Got the Access Token!"); System.out.println("(if your curious it looks like this: " + accessToken + " )"); System.out.println(); From 3e4527f91c5d39bafb6d8d3a377d05759e86b401 Mon Sep 17 00:00:00 2001 From: tbruyelle Date: Sun, 29 May 2011 16:09:35 +0200 Subject: [PATCH 04/27] support Google OAuth 2.0 --- .gitignore | 3 +- pom.xml | 6 +-- .../org/scribe/builder/api/GoogleApi20.java | 38 +++++++++++++++++++ src/main/java/org/scribe/model/Request.java | 10 ++++- .../org/scribe/oauth/OAuth20ServiceImpl.java | 2 +- 5 files changed, 53 insertions(+), 6 deletions(-) create mode 100755 src/main/java/org/scribe/builder/api/GoogleApi20.java diff --git a/.gitignore b/.gitignore index f2730f1fa..457254fdf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,8 @@ .classpath .project .settings +*.iml # Binaries -target \ No newline at end of file +target diff --git a/pom.xml b/pom.xml index e6bb9e818..85a6aed1f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.scribe scribe jar - 1.2.0 + 1.2.0-patched Scribe OAuth Library The best OAuth library out there http://github.com/fernandezpablo85/scribe-java @@ -67,7 +67,7 @@ 1.6 - + diff --git a/src/main/java/org/scribe/builder/api/GoogleApi20.java b/src/main/java/org/scribe/builder/api/GoogleApi20.java new file mode 100755 index 000000000..959f27725 --- /dev/null +++ b/src/main/java/org/scribe/builder/api/GoogleApi20.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.URLUtils; + +public class GoogleApi20 + extends DefaultApi20 +{ + private static final String AUTHORIZATION_URL = + "https://accounts.google.com/o/oauth2/auth?client_id=%s&redirect_uri=%s&scope=%s&response_type=code"; + + @Override + public String getAuthorizationUrl( OAuthConfig config ) + { + return String.format( AUTHORIZATION_URL, config.getApiKey(), config.getCallback(), config.getScope() ); + } + + @Override + public String getAccessTokenEndpoint() + { + return "https://accounts.google.com/o/oauth2/token"; + } + + @Override + public Verb getAccessTokenVerb() + { + return Verb.POST; + } + + @Override + public AccessTokenExtractor getAccessTokenExtractor() + { + return new JsonTokenExtractor(); + } +} diff --git a/src/main/java/org/scribe/model/Request.java b/src/main/java/org/scribe/model/Request.java index d49aa3e0a..cdedaaa60 100644 --- a/src/main/java/org/scribe/model/Request.java +++ b/src/main/java/org/scribe/model/Request.java @@ -63,7 +63,9 @@ public Response send() private void createConnection() throws IOException { - String effectiveUrl = URLUtils.appendParametersToQueryString(url, querystringParams); + + String effectiveUrl = getVerb().equals( Verb.GET ) ? + URLUtils.appendParametersToQueryString(url, querystringParams) : url; if (connection == null) { System.setProperty("http.keepAlive", "false"); @@ -95,6 +97,12 @@ void addBody(HttpURLConnection conn, String content) throws IOException conn.setRequestProperty(CONTENT_LENGTH, String.valueOf(content.getBytes(charset).length)); conn.setDoOutput(true); conn.getOutputStream().write(content.getBytes(charset)); + if ( getVerb().equals( Verb.POST ) ) + { + OutputStreamWriter wr = new OutputStreamWriter( conn.getOutputStream() ); + wr.write( URLUtils.formURLEncodeMap( querystringParams ) ); + wr.flush(); + } } /** diff --git a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java index 5fecdfde1..295d82b76 100644 --- a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java +++ b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java @@ -60,7 +60,7 @@ public String getVersion() */ public void signRequest(Token accessToken, OAuthRequest request) { - request.addQuerystringParameter(OAuthConstants.ACCESS_TOKEN, accessToken.getToken()); + request.addQuerystringParameter(OAuthConstants.TOKEN, accessToken.getToken()); } /** From a691e094ab2b9ffa57dc46265a658d0423c6216e Mon Sep 17 00:00:00 2001 From: "U-EUR\\TBRUYELLE" Date: Wed, 1 Jun 2011 12:45:21 +0200 Subject: [PATCH 05/27] ensure backward compatiblity : google OAuth2.0 needs "oauth_token" parameter name instead of "access_token" to sign a request. --- .../org/scribe/builder/api/DefaultApi20.java | 99 +++++++------ .../org/scribe/builder/api/GoogleApi20.java | 8 ++ .../org/scribe/oauth/OAuth20ServiceImpl.java | 134 ++++++++++-------- 3 files changed, 133 insertions(+), 108 deletions(-) diff --git a/src/main/java/org/scribe/builder/api/DefaultApi20.java b/src/main/java/org/scribe/builder/api/DefaultApi20.java index ffb620702..4335df678 100644 --- a/src/main/java/org/scribe/builder/api/DefaultApi20.java +++ b/src/main/java/org/scribe/builder/api/DefaultApi20.java @@ -6,65 +6,70 @@ /** * Default implementation of the OAuth protocol, version 2.0 (draft 11) - * + *

* This class is meant to be extended by concrete implementations of the API, * providing the endpoints and endpoint-http-verbs. - * + *

* If your Api adheres to the 2.0 (draft 11) protocol correctly, you just need to extend * this class and define the getters for your endpoints. - * + *

* If your Api does something a bit different, you can override the different * extractors or services, in order to fine-tune the process. Please read the * javadocs of the interfaces to get an idea of what to do. * * @author Diego Silveira - * */ -public abstract class DefaultApi20 implements Api +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. - * - * @param config OAuth 2.0 configuration param object - * @return the URL where you should redirect your users - */ - public abstract String getAuthorizationUrl(OAuthConfig config); + public String getTokenParameterName() + { + return OAuthConstants.ACCESS_TOKEN; + } + + /** + * 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. + * + * @param config OAuth 2.0 configuration param object + * @return the URL where you should redirect your users + */ + public abstract String getAuthorizationUrl( OAuthConfig config ); - /** - * {@inheritDoc} - */ - public OAuthService createService(OAuthConfig config) - { - return new OAuth20ServiceImpl(this, config); - } + /** + * {@inheritDoc} + */ + public OAuthService createService( OAuthConfig config ) + { + return new OAuth20ServiceImpl( this, config ); + } } diff --git a/src/main/java/org/scribe/builder/api/GoogleApi20.java b/src/main/java/org/scribe/builder/api/GoogleApi20.java index 959f27725..487302b7d 100755 --- a/src/main/java/org/scribe/builder/api/GoogleApi20.java +++ b/src/main/java/org/scribe/builder/api/GoogleApi20.java @@ -3,6 +3,7 @@ import org.scribe.extractors.AccessTokenExtractor; import org.scribe.extractors.JsonTokenExtractor; import org.scribe.model.OAuthConfig; +import org.scribe.model.OAuthConstants; import org.scribe.model.Verb; import org.scribe.utils.URLUtils; @@ -35,4 +36,11 @@ public AccessTokenExtractor getAccessTokenExtractor() { return new JsonTokenExtractor(); } + + @Override + public String getTokenParameterName() + { + /* google OAuth2.0 specs needs oauth_token parameter name instead of access_token */ + return OAuthConstants.TOKEN; + } } diff --git a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java index 295d82b76..d54e3ba5b 100644 --- a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java +++ b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java @@ -3,72 +3,84 @@ import org.scribe.builder.api.*; import org.scribe.model.*; -public class OAuth20ServiceImpl implements OAuthService +public class OAuth20ServiceImpl + implements OAuthService { - private static final String VERSION = "2.0"; - - private final DefaultApi20 api; - private final OAuthConfig config; - - /** - * Default constructor - * - * @param api OAuth2.0 api information - * @param config OAuth 2.0 configuration param object - */ - public OAuth20ServiceImpl(DefaultApi20 api, OAuthConfig config) - { - this.api = api; - this.config = config; - } + private static final String VERSION = "2.0"; - /** - * {@inheritDoc} - */ - public Token getAccessToken(Token requestToken, Verifier verifier) - { - OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); - request.addQuerystringParameter(OAuthConstants.CLIENT_ID, config.getApiKey()); - request.addQuerystringParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret()); - // In case of Client Credentials, verfier is not required - if(verifier != null) request.addQuerystringParameter(OAuthConstants.CODE, verifier.getValue()); - request.addQuerystringParameter(OAuthConstants.REDIRECT_URI, config.getCallback()); - if(config.hasScope()) request.addQuerystringParameter(OAuthConstants.SCOPE, config.getScope()); - if(config.hasGrantType()) request.addQuerystringParameter(OAuthConstants.GRANT_TYPE, config.getGrantType()); - Response response = request.send(); - return api.getAccessTokenExtractor().extract(response.getBody()); - } + private final DefaultApi20 api; - /** - * {@inheritDoc} - */ - public Token getRequestToken() - { - throw new UnsupportedOperationException("Unsupported operation, please use 'getAuthorizationUrl' and redirect your users there"); - } + private final OAuthConfig config; - /** - * {@inheritDoc} - */ - public String getVersion() - { - return VERSION; - } + /** + * Default constructor + * + * @param api OAuth2.0 api information + * @param config OAuth 2.0 configuration param object + */ + public OAuth20ServiceImpl( DefaultApi20 api, OAuthConfig config ) + { + this.api = api; + this.config = config; + } - /** - * {@inheritDoc} - */ - public void signRequest(Token accessToken, OAuthRequest request) - { - request.addQuerystringParameter(OAuthConstants.TOKEN, accessToken.getToken()); - } + /** + * {@inheritDoc} + */ + public Token getAccessToken( Token requestToken, Verifier verifier ) + { + OAuthRequest request = new OAuthRequest( api.getAccessTokenVerb(), api.getAccessTokenEndpoint() ); + request.addQuerystringParameter( OAuthConstants.CLIENT_ID, config.getApiKey() ); + request.addQuerystringParameter( OAuthConstants.CLIENT_SECRET, config.getApiSecret() ); + // In case of Client Credentials, verfier is not required + if ( verifier != null ) + { + request.addQuerystringParameter( OAuthConstants.CODE, verifier.getValue() ); + } + request.addQuerystringParameter( OAuthConstants.REDIRECT_URI, config.getCallback() ); + if ( config.hasScope() ) + { + request.addQuerystringParameter( OAuthConstants.SCOPE, config.getScope() ); + } + if ( config.hasGrantType() ) + { + request.addQuerystringParameter( OAuthConstants.GRANT_TYPE, config.getGrantType() ); + } + Response response = request.send(); + return api.getAccessTokenExtractor().extract( response.getBody() ); + } - /** - * {@inheritDoc} - */ - public String getAuthorizationUrl(Token requestToken) - { - return api.getAuthorizationUrl(config); - } + /** + * {@inheritDoc} + */ + public Token getRequestToken() + { + throw new UnsupportedOperationException( + "Unsupported operation, please use 'getAuthorizationUrl' and redirect your users there" ); + } + + /** + * {@inheritDoc} + */ + public String getVersion() + { + return VERSION; + } + + /** + * {@inheritDoc} + */ + public void signRequest( Token accessToken, OAuthRequest request ) + { + request.addQuerystringParameter( api.getTokenParameterName(), accessToken.getToken() ); + } + + /** + * {@inheritDoc} + */ + public String getAuthorizationUrl( Token requestToken ) + { + return api.getAuthorizationUrl( config ); + } } From 1d6d70666abe94da1cf34b3c1638f2124f74691c Mon Sep 17 00:00:00 2001 From: tbruyelle Date: Wed, 1 Jun 2011 14:41:41 +0200 Subject: [PATCH 06/27] add example for Google OAuth 2.0 --- .../org/scribe/examples/GoogleExample20.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100755 src/test/java/org/scribe/examples/GoogleExample20.java diff --git a/src/test/java/org/scribe/examples/GoogleExample20.java b/src/test/java/org/scribe/examples/GoogleExample20.java new file mode 100755 index 000000000..ac1bfdbb7 --- /dev/null +++ b/src/test/java/org/scribe/examples/GoogleExample20.java @@ -0,0 +1,66 @@ +package org.scribe.examples; + +import org.scribe.builder.ServiceBuilder; +import org.scribe.builder.api.GoogleApi20; +import org.scribe.model.OAuthConstants; +import org.scribe.model.OAuthRequest; +import org.scribe.model.Response; +import org.scribe.model.Token; +import org.scribe.model.Verb; +import org.scribe.model.Verifier; +import org.scribe.oauth.OAuthService; + +import java.util.Scanner; + +public class GoogleExample20 +{ + private static final String NETWORK_NAME = "Google 2.0"; + + private static final String PROTECTED_RESOURCE_URL = "https://docs.google.com/feeds/default/private/full/"; + + private static final String SCOPE = "https://docs.google.com/feeds/"; + + public static void main( String[] args ) + { + // Replace these with your own api key, secret and callback + String apiKey = "apiKey"; + String apiSecret = "apiSecret"; + String callback = "http://www.example.com/google/back"; + + OAuthService service = + new ServiceBuilder().provider( GoogleApi20.class ).apiKey( apiKey ).apiSecret( apiSecret ).callback( + callback ).scope( SCOPE ).grantType( OAuthConstants.AUTHORIZATION_CODE ).build(); + + System.out.println( "=== " + NETWORK_NAME + "'s OAuth Workflow ===" ); + System.out.println(); + + // Obtain the authorization url + String url = service.getAuthorizationUrl( null ); + + System.out.println( "go there : " + url ); + System.out.println( "paste the authorization code >>" ); + Scanner in = new Scanner( System.in ); + Verifier verifier = new Verifier( in.nextLine() ); + + Token accessToken = service.getAccessToken( null, 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! + System.out.println( "Now we're going to access a protected resource..." ); + OAuthRequest request = new OAuthRequest( Verb.GET, PROTECTED_RESOURCE_URL ); + service.signRequest( accessToken, request ); + request.addHeader( "GData-Version", "3.0" ); + Response response = request.send(); + System.out.println( "Got it! Lets see what we found..." ); + System.out.println(); + System.out.println( response.getCode() ); + System.out.println( response.getBody() ); + + System.out.println(); + System.out.println( "Thats it man! Go and build something awesome with Scribe! :)" ); + + } +} \ No newline at end of file From 6c42c96d55b3a727d62068cb51019a14292ad705 Mon Sep 17 00:00:00 2001 From: tbruyelle Date: Wed, 1 Jun 2011 16:24:13 +0200 Subject: [PATCH 07/27] add test on GoogleApi20 --- src/test/java/org/scribe/api/ApisTest.java | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100755 src/test/java/org/scribe/api/ApisTest.java diff --git a/src/test/java/org/scribe/api/ApisTest.java b/src/test/java/org/scribe/api/ApisTest.java new file mode 100755 index 000000000..28aa42435 --- /dev/null +++ b/src/test/java/org/scribe/api/ApisTest.java @@ -0,0 +1,29 @@ +package org.scribe.api; + + +import junit.framework.TestCase; +import org.junit.Test; +import org.scribe.builder.api.GoogleApi20; +import org.scribe.extractors.JsonTokenExtractor; +import org.scribe.model.OAuthConfig; +import org.scribe.model.OAuthConstants; +import org.scribe.model.Verb; + +public class ApisTest + extends TestCase +{ + @Test + public void testGoogleApi20() + { + GoogleApi20 api = new GoogleApi20(); + assertEquals( OAuthConstants.TOKEN, api.getTokenParameterName() ); + assertEquals( "https://accounts.google.com/o/oauth2/token", api.getAccessTokenEndpoint() ); + assertEquals( Verb.POST, api.getAccessTokenVerb() ); + assertEquals( JsonTokenExtractor.class, api.getAccessTokenExtractor().getClass() ); + + OAuthConfig config = new OAuthConfig( "key", null, "callback", null, "scope", null ); + assertEquals( + "https://accounts.google.com/o/oauth2/auth?client_id=keyq&redirect_uri=callback&scope=scope&response_type=code", + api.getAuthorizationUrl( config ) ); + } +} From ae216cdae5e22536ba8717f94d7d3c5316111b9b Mon Sep 17 00:00:00 2001 From: tbruyelle Date: Tue, 13 Sep 2011 10:01:23 +0200 Subject: [PATCH 08/27] add constructor to enable mock up --- src/main/java/org/scribe/model/Response.java | 5 ++++ src/test/java/org/scribe/api/ApisTest.java | 29 -------------------- 2 files changed, 5 insertions(+), 29 deletions(-) delete mode 100755 src/test/java/org/scribe/api/ApisTest.java diff --git a/src/main/java/org/scribe/model/Response.java b/src/main/java/org/scribe/model/Response.java index 6164e1495..bc27e313d 100644 --- a/src/main/java/org/scribe/model/Response.java +++ b/src/main/java/org/scribe/model/Response.java @@ -35,6 +35,11 @@ public class Response } } + public Response() + { + + } + private String parseBodyContents() { body = StreamUtils.getStreamContents(getStream()); diff --git a/src/test/java/org/scribe/api/ApisTest.java b/src/test/java/org/scribe/api/ApisTest.java deleted file mode 100755 index 28aa42435..000000000 --- a/src/test/java/org/scribe/api/ApisTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.scribe.api; - - -import junit.framework.TestCase; -import org.junit.Test; -import org.scribe.builder.api.GoogleApi20; -import org.scribe.extractors.JsonTokenExtractor; -import org.scribe.model.OAuthConfig; -import org.scribe.model.OAuthConstants; -import org.scribe.model.Verb; - -public class ApisTest - extends TestCase -{ - @Test - public void testGoogleApi20() - { - GoogleApi20 api = new GoogleApi20(); - assertEquals( OAuthConstants.TOKEN, api.getTokenParameterName() ); - assertEquals( "https://accounts.google.com/o/oauth2/token", api.getAccessTokenEndpoint() ); - assertEquals( Verb.POST, api.getAccessTokenVerb() ); - assertEquals( JsonTokenExtractor.class, api.getAccessTokenExtractor().getClass() ); - - OAuthConfig config = new OAuthConfig( "key", null, "callback", null, "scope", null ); - assertEquals( - "https://accounts.google.com/o/oauth2/auth?client_id=keyq&redirect_uri=callback&scope=scope&response_type=code", - api.getAuthorizationUrl( config ) ); - } -} From 50d0a4a8c0fa35578d3a6b477e0b6c462520ce70 Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Tue, 20 Sep 2011 18:46:54 +0200 Subject: [PATCH 09/27] Revert 97f45d75e76e45bd24ec810a90dffee793020a6a^..HEAD --- README.textile | 2 +- changelog.txt | 3 +++ pom.xml | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.textile b/README.textile index eec145178..8d8e1655a 100644 --- a/README.textile +++ b/README.textile @@ -61,7 +61,7 @@ You can pull scribe from a maven repository, just add this to your __pom.xml__ f @@ @org.scribe@ @scribe@ - @1.2.0@ + @1.1.3@ @@ h1. Getting started in less than 2 minutes diff --git a/changelog.txt b/changelog.txt index faed428df..892b43598 100644 --- a/changelog.txt +++ b/changelog.txt @@ -48,9 +48,12 @@ [1.2] * REFACTOR: Moved scope inside OAuthConfig (breaks backwards compatibility) * FEATURE: Added scopes to Facebook 2.0 Api +<<<<<<< HEAD * FEATURE: Added Plaintext signature for Yammer * FEATURE: Added Twitter SSL endpoints (use TwitterApi.SSL.class) [1.2.1] * FEATURE: Added custom charset support to Request (thanks Eric Genet) * FEATURE: Added support for Vkontakte (thanks dotbg) +======= +>>>>>>> parent of 97f45d7... bounced version numbers for release diff --git a/pom.xml b/pom.xml index 85a6aed1f..2dbf277d1 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,11 @@ org.scribe scribe jar +<<<<<<< HEAD 1.2.0-patched +======= + 1.1.3 +>>>>>>> parent of 97f45d7... bounced version numbers for release Scribe OAuth Library The best OAuth library out there http://github.com/fernandezpablo85/scribe-java From 794663b6c972bccd3ad0f428752d83aa21870bf9 Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Mon, 26 Sep 2011 18:13:46 +0200 Subject: [PATCH 10/27] fix merge --- pom.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pom.xml b/pom.xml index 2dbf277d1..85a6aed1f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,11 +4,7 @@ org.scribe scribe jar -<<<<<<< HEAD 1.2.0-patched -======= - 1.1.3 ->>>>>>> parent of 97f45d7... bounced version numbers for release Scribe OAuth Library The best OAuth library out there http://github.com/fernandezpablo85/scribe-java From b65b5f6f7138e7a60ba6dead1d2d2b66bb2ce57c Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Mon, 26 Sep 2011 18:42:49 +0200 Subject: [PATCH 11/27] update version and comment gpg plugin --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 02a30c768..1e6a43877 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.scribe scribe jar - 1.2.3 + 1.2.3-patched Scribe OAuth Library The best OAuth library out there http://github.com/fernandezpablo85/scribe-java @@ -67,7 +67,7 @@ 1.5 - + From 667c192a2e37179dca4364a059bcbd983dff28af Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Wed, 26 Oct 2011 18:46:07 +0200 Subject: [PATCH 12/27] fix jsonextractor pattern --- pom.xml | 2 +- src/main/java/org/scribe/extractors/JsonTokenExtractor.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1e6a43877..ac75a1dd9 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.scribe scribe jar - 1.2.3-patched + 1.2.4-patched Scribe OAuth Library The best OAuth library out there http://github.com/fernandezpablo85/scribe-java diff --git a/src/main/java/org/scribe/extractors/JsonTokenExtractor.java b/src/main/java/org/scribe/extractors/JsonTokenExtractor.java index 7c1ee04e1..55d344769 100644 --- a/src/main/java/org/scribe/extractors/JsonTokenExtractor.java +++ b/src/main/java/org/scribe/extractors/JsonTokenExtractor.java @@ -8,7 +8,7 @@ public class JsonTokenExtractor implements AccessTokenExtractor { - private Pattern accessTokenPattern = Pattern.compile("\"access_token\":\\s*\"(\\S*?)\""); + private Pattern accessTokenPattern = Pattern.compile("\"access_token\"\\s*:\\s*\"(\\S*?)\""); @Override public Token extract(String response) From d0a7b7cee5d345673043400f6959337c3b79a7be Mon Sep 17 00:00:00 2001 From: Emmanuel Venisse Date: Mon, 20 Feb 2012 17:55:51 +0100 Subject: [PATCH 13/27] Add support for Bistri --- pom.xml | 2 +- .../org/scribe/builder/api/BistriApi.java | 40 +++++++++++ .../org/scribe/examples/BistriExample.java | 66 +++++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/scribe/builder/api/BistriApi.java create mode 100644 src/test/java/org/scribe/examples/BistriExample.java diff --git a/pom.xml b/pom.xml index ac75a1dd9..9ca29ab60 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.scribe scribe jar - 1.2.4-patched + 1.2.5-patched Scribe OAuth Library The best OAuth library out there http://github.com/fernandezpablo85/scribe-java diff --git a/src/main/java/org/scribe/builder/api/BistriApi.java b/src/main/java/org/scribe/builder/api/BistriApi.java new file mode 100644 index 000000000..d5b9615a0 --- /dev/null +++ b/src/main/java/org/scribe/builder/api/BistriApi.java @@ -0,0 +1,40 @@ +package org.scribe.builder.api; + +import org.scribe.model.Token; +import org.scribe.model.Verb; + +public class BistriApi + extends DefaultApi10a +{ + private static final String AUTHORIZATION_URL = "http://localhost:8080/oauth/authorize?oauth_token=%s"; + + @Override + public String getRequestTokenEndpoint() + { + return "http://localhost:8080/oauth/request_token"; + } + + @Override + public String getAccessTokenEndpoint() + { + return "http://localhost:8080/oauth/access_token"; + } + + @Override + public Verb getAccessTokenVerb() + { + return Verb.GET; + } + + @Override + public Verb getRequestTokenVerb() + { + return Verb.GET; + } + + @Override + public String getAuthorizationUrl( Token requestToken ) + { + return String.format( AUTHORIZATION_URL, requestToken.getToken() ); + } +} diff --git a/src/test/java/org/scribe/examples/BistriExample.java b/src/test/java/org/scribe/examples/BistriExample.java new file mode 100644 index 000000000..e13510c3b --- /dev/null +++ b/src/test/java/org/scribe/examples/BistriExample.java @@ -0,0 +1,66 @@ +package org.scribe.examples; + +import org.scribe.builder.ServiceBuilder; +import org.scribe.builder.api.BistriApi; +import org.scribe.model.OAuthRequest; +import org.scribe.model.Response; +import org.scribe.model.Token; +import org.scribe.model.Verb; +import org.scribe.model.Verifier; +import org.scribe.oauth.OAuthService; + +import java.util.Scanner; + +public class BistriExample +{ + private static final String NETWORK_NAME = "Bistri"; + + private static final String AUTHORIZE_URL = "http://localhost:8080/oauth/authorize?oauth_token="; + + private static final String PROTECTED_RESOURCE_URL = "http://localhost:8080/static/supportedlanguages"; + + public static void main( String[] args ) + { + OAuthService service = + new ServiceBuilder().provider( BistriApi.class ).apiKey( "testUser" ).apiSecret( "testSecret" ).build(); + Scanner in = new Scanner( System.in ); + + System.out.println( "=== " + NETWORK_NAME + "'s OAuth Workflow ===" ); + System.out.println(); + + // Obtain the Request Token + System.out.println( "Fetching the Request Token..." ); + Token requestToken = service.getRequestToken(); + System.out.println( "Got the Request Token!" ); + System.out.println( "(if your curious it looks like this: " + requestToken + " )" ); + System.out.println(); + + System.out.println( "Now go and authorize Scribe here:" ); + System.out.println( AUTHORIZE_URL + requestToken.getToken() ); + System.out.println( "And paste the verifier here" ); + System.out.print( ">>" ); + 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( requestToken, 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! + System.out.println( "Now we're going to access a protected resource..." ); + OAuthRequest request = new OAuthRequest( Verb.GET, PROTECTED_RESOURCE_URL ); + service.signRequest( accessToken, request ); + Response response = request.send(); + System.out.println( "Got it! Lets see what we found..." ); + System.out.println(); + System.out.println( response.getCode() ); + System.out.println( response.getBody() ); + + System.out.println(); + System.out.println( "Thats it man! Go and build something awesome with Scribe! :)" ); + + } +} From 14c78657ebdbf99ab138325591863f5467f3830c Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Tue, 24 Apr 2012 15:32:13 +0200 Subject: [PATCH 14/27] fix bad merge --- .../org/scribe/builder/api/FacebookApi.java | 57 +++++++------------ 1 file changed, 20 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/scribe/builder/api/FacebookApi.java b/src/main/java/org/scribe/builder/api/FacebookApi.java index 3f762edfa..f4d438d5e 100644 --- a/src/main/java/org/scribe/builder/api/FacebookApi.java +++ b/src/main/java/org/scribe/builder/api/FacebookApi.java @@ -6,45 +6,28 @@ public class FacebookApi extends DefaultApi20 { - private static final String AUTHORIZE_URL = "https://www.facebook.com/dialog/oauth?client_id=%s&redirect_uri=%s"; - private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s"; - private static final String GRANT_TYPE_PARAM = "&grant_type=%s"; + private static final String AUTHORIZE_URL = "https://www.facebook.com/dialog/oauth?client_id=%s&redirect_uri=%s"; + private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s"; - private static final String SCOPED_GRANT_TYPED_URL = SCOPED_AUTHORIZE_URL+GRANT_TYPE_PARAM; - private static final String GRANT_TYPED_URL = AUTHORIZE_URL + GRANT_TYPE_PARAM; - - private static final String ACCESS_TOKEN_ENDPOINT = "https://graph.facebook.com/oauth/access_token"; - - - @Override - public String getAccessTokenEndpoint() - { - return ACCESS_TOKEN_ENDPOINT; - } - - @Override - public String getAuthorizationUrl(OAuthConfig config) - { - Preconditions.checkValidUrl(config.getCallback(), "Must provide a valid url as callback. Facebook does not support OOB"); - - // Append scope and/or grant type if present - if(config.hasScope() && !config.hasGrantType()) + @Override + public String getAccessTokenEndpoint() { - return String.format(SCOPED_AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback()), OAuthEncoder.encode(config.getScope())); + return "https://graph.facebook.com/oauth/access_token"; } - else if(config.hasScope() && config.hasGrantType()) - { - return String.format(SCOPED_GRANT_TYPED_URL, config.getApiKey(),OAuthEncoder.encode(config.getCallback()), - OAuthEncoder.encode(config.getScope()),OAuthEncoder.encode(config.getGrantType())); - } - else if(!config.hasScope() && config.hasGrantType()) - { - return String.format(GRANT_TYPED_URL, config.getApiKey(),OAuthEncoder.encode(config.getCallback()), - OAuthEncoder.encode(config.getGrantType())); - } - else + + @Override + public String getAuthorizationUrl(OAuthConfig config) { - return String.format(AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback())); + Preconditions.checkValidUrl(config.getCallback(), "Must provide a valid url as callback. Facebook does not support OOB"); + + // Append scope if present + if(config.hasScope()) + { + return String.format(SCOPED_AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback()), OAuthEncoder.encode(config.getScope())); + } + else + { + return String.format(AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback())); + } } - } -} +} \ No newline at end of file From 32351feb2339af80170446ddc6aff6c733461235 Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Tue, 24 Apr 2012 16:21:49 +0200 Subject: [PATCH 15/27] fix format --- .../org/scribe/oauth/OAuth20ServiceImpl.java | 130 ++++++++---------- 1 file changed, 58 insertions(+), 72 deletions(-) diff --git a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java index d54e3ba5b..2048103ac 100644 --- a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java +++ b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java @@ -1,86 +1,72 @@ package org.scribe.oauth; -import org.scribe.builder.api.*; +import org.scribe.builder.api.DefaultApi20; import org.scribe.model.*; -public class OAuth20ServiceImpl - implements OAuthService +public class OAuth20ServiceImpl implements OAuthService { - private static final String VERSION = "2.0"; + private static final String VERSION = "2.0"; - private final DefaultApi20 api; + private final DefaultApi20 api; + private final OAuthConfig config; - private final OAuthConfig config; + /** + * Default constructor + * + * @param api OAuth2.0 api information + * @param config OAuth 2.0 configuration param object + */ + public OAuth20ServiceImpl(DefaultApi20 api, OAuthConfig config) + { + this.api = api; + this.config = config; + } - /** - * Default constructor - * - * @param api OAuth2.0 api information - * @param config OAuth 2.0 configuration param object - */ - public OAuth20ServiceImpl( DefaultApi20 api, OAuthConfig config ) - { - this.api = api; - this.config = config; - } + /** + * {@inheritDoc} + */ + public Token getAccessToken(Token requestToken, Verifier verifier) + { + 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()); + if (config.hasScope()) request.addQuerystringParameter(OAuthConstants.SCOPE, config.getScope()); + Response response = request.send(); + return api.getAccessTokenExtractor().extract(response.getBody()); + } - /** - * {@inheritDoc} - */ - public Token getAccessToken( Token requestToken, Verifier verifier ) - { - OAuthRequest request = new OAuthRequest( api.getAccessTokenVerb(), api.getAccessTokenEndpoint() ); - request.addQuerystringParameter( OAuthConstants.CLIENT_ID, config.getApiKey() ); - request.addQuerystringParameter( OAuthConstants.CLIENT_SECRET, config.getApiSecret() ); - // In case of Client Credentials, verfier is not required - if ( verifier != null ) - { - request.addQuerystringParameter( OAuthConstants.CODE, verifier.getValue() ); - } - request.addQuerystringParameter( OAuthConstants.REDIRECT_URI, config.getCallback() ); - if ( config.hasScope() ) - { - request.addQuerystringParameter( OAuthConstants.SCOPE, config.getScope() ); - } - if ( config.hasGrantType() ) - { - request.addQuerystringParameter( OAuthConstants.GRANT_TYPE, config.getGrantType() ); - } - Response response = request.send(); - return api.getAccessTokenExtractor().extract( response.getBody() ); - } + /** + * {@inheritDoc} + */ + public Token getRequestToken() + { + throw new UnsupportedOperationException("Unsupported operation, please use 'getAuthorizationUrl' and redirect your users there"); + } - /** - * {@inheritDoc} - */ - public Token getRequestToken() - { - throw new UnsupportedOperationException( - "Unsupported operation, please use 'getAuthorizationUrl' and redirect your users there" ); - } + /** + * {@inheritDoc} + */ + public String getVersion() + { + return VERSION; + } - /** - * {@inheritDoc} - */ - public String getVersion() - { - return VERSION; - } + /** + * {@inheritDoc} + */ + public void signRequest(Token accessToken, OAuthRequest request) + { + request.addQuerystringParameter(OAuthConstants.ACCESS_TOKEN, accessToken.getToken()); + } - /** - * {@inheritDoc} - */ - public void signRequest( Token accessToken, OAuthRequest request ) - { - request.addQuerystringParameter( api.getTokenParameterName(), accessToken.getToken() ); - } - - /** - * {@inheritDoc} - */ - public String getAuthorizationUrl( Token requestToken ) - { - return api.getAuthorizationUrl( config ); - } + /** + * {@inheritDoc} + */ + public String getAuthorizationUrl(Token requestToken) + { + return api.getAuthorizationUrl(config); + } } From 6f44f7dbe9ead16bd81e6590b8d22ab223abc31a Mon Sep 17 00:00:00 2001 From: Thomas Bruyelle Date: Tue, 24 Apr 2012 18:11:29 +0200 Subject: [PATCH 16/27] Implement OAuth 2.0 refresh access token Some OAuth providers ask to extend the access token expiration date, in order to continue using it. Microsoft Live do that, and it will be also soon mandatory for Facebook because they will deprecate the 'offline_access' permission. To refresh an access token, you need to access the accessTokenEndPoint with other parameters like a specific 'grant_type' parameter. --- .../org/scribe/builder/api/DefaultApi20.java | 7 ++++++ .../org/scribe/builder/api/FacebookApi.java | 6 +++++ .../java/org/scribe/builder/api/LiveApi.java | 6 +++++ .../java/org/scribe/model/OAuthConstants.java | 1 - .../org/scribe/oauth/OAuth10aServiceImpl.java | 8 +++++++ .../org/scribe/oauth/OAuth20ServiceImpl.java | 22 +++++++++++++++++++ .../java/org/scribe/oauth/OAuthService.java | 13 +++++++++++ 7 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/scribe/builder/api/DefaultApi20.java b/src/main/java/org/scribe/builder/api/DefaultApi20.java index 4335df678..46fface96 100644 --- a/src/main/java/org/scribe/builder/api/DefaultApi20.java +++ b/src/main/java/org/scribe/builder/api/DefaultApi20.java @@ -72,4 +72,11 @@ public OAuthService createService( OAuthConfig config ) return new OAuth20ServiceImpl( this, config ); } + /** + * @return the parameter needed to refresh a access token. + */ + public String getRefreshTokenParameterName() + { + throw new UnsupportedOperationException("Refresh token is not implemented for "+getClass().getSimpleName()); + } } diff --git a/src/main/java/org/scribe/builder/api/FacebookApi.java b/src/main/java/org/scribe/builder/api/FacebookApi.java index f4d438d5e..e582a2e5e 100644 --- a/src/main/java/org/scribe/builder/api/FacebookApi.java +++ b/src/main/java/org/scribe/builder/api/FacebookApi.java @@ -30,4 +30,10 @@ public String getAuthorizationUrl(OAuthConfig config) return String.format(AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback())); } } + + @Override + public String getRefreshTokenParameterName() + { + return "fb_exchange_token"; + } } \ No newline at end of file diff --git a/src/main/java/org/scribe/builder/api/LiveApi.java b/src/main/java/org/scribe/builder/api/LiveApi.java index 18140f603..f3db6acc2 100644 --- a/src/main/java/org/scribe/builder/api/LiveApi.java +++ b/src/main/java/org/scribe/builder/api/LiveApi.java @@ -37,4 +37,10 @@ public AccessTokenExtractor getAccessTokenExtractor() { return new JsonTokenExtractor(); } + + @Override + public String getRefreshTokenParameterName() + { + return "refresh_token"; + } } \ No newline at end of file diff --git a/src/main/java/org/scribe/model/OAuthConstants.java b/src/main/java/org/scribe/model/OAuthConstants.java index dbf5ae50f..aea819545 100644 --- a/src/main/java/org/scribe/model/OAuthConstants.java +++ b/src/main/java/org/scribe/model/OAuthConstants.java @@ -50,6 +50,5 @@ public class OAuthConstants public static final String AUTHORIZATION_CODE = "authorization_code"; public static final String RESOURCE_OWNER_PASSWORD_CREDENTIALS = "password"; public static final String CLIENT_CREDENTIALS = "client_credentials"; - public static final String REFRESH_TOKEN = "refresh_token"; } diff --git a/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java index b847b2d77..00a7f320e 100644 --- a/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java +++ b/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java @@ -82,6 +82,14 @@ public Token getAccessToken(Token requestToken, Verifier verifier) return api.getAccessTokenExtractor().extract(response.getBody()); } + /** + * {@inheritDoc} + */ + public Token refreshAccessToken(Token accessToken) + { + throw new UnsupportedOperationException("Refresh token is not supported in Scribe OAuth 1.0"); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java index 2048103ac..dd22ed905 100644 --- a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java +++ b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java @@ -37,6 +37,28 @@ public Token getAccessToken(Token requestToken, Verifier verifier) return api.getAccessTokenExtractor().extract(response.getBody()); } + /** + * {@inheritDoc} + */ + public Token refreshAccessToken(Token accessToken) + { + + String accessTokenEndpoint = api.getAccessTokenEndpoint(); + if (accessTokenEndpoint.contains("?grant_type=")) + { + // handle the ugly case where the grant_type parameter is already hardcoded in the constant url + accessTokenEndpoint = accessTokenEndpoint.substring(0, accessTokenEndpoint.indexOf("?")); + } + OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), accessTokenEndpoint); + request.addQuerystringParameter(OAuthConstants.CLIENT_ID, config.getApiKey()); + request.addQuerystringParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret()); + request.addQuerystringParameter(OAuthConstants.REDIRECT_URI, config.getCallback()); + request.addQuerystringParameter(OAuthConstants.GRANT_TYPE, api.getRefreshTokenParameterName()); + request.addQuerystringParameter(api.getRefreshTokenParameterName(), accessToken.getToken()); + Response response = request.send(); + return api.getAccessTokenExtractor().extract(response.getBody()); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/org/scribe/oauth/OAuthService.java b/src/main/java/org/scribe/oauth/OAuthService.java index 0c9c57e9b..f7467f95d 100644 --- a/src/main/java/org/scribe/oauth/OAuthService.java +++ b/src/main/java/org/scribe/oauth/OAuthService.java @@ -27,6 +27,19 @@ public interface OAuthService */ public Token getAccessToken(Token requestToken, Verifier verifier); + /** + * Refresh the access token to extend its expiration date. + *

+ * For the token in parameter, Facebook needs the access_token, while Live + * needs the refresh_token (which can be found only in the + * {@link org.scribe.model.Token#getRawResponse()} returned by + * {@link #getAccessToken(org.scribe.model.Token, org.scribe.model.Verifier)}) + * + * @param accessToken access or refresh token, depending on the OAuth provider + * @return fresh access token + */ + public Token refreshAccessToken(Token accessToken); + /** * Signs am OAuth request * From 3b6a9a007611ccf8e26750e092498b250ea069ed Mon Sep 17 00:00:00 2001 From: Christian Huff Date: Thu, 31 May 2012 10:58:09 +0200 Subject: [PATCH 17/27] Added Google2Api from https://gist.github.com/2465453 --- .../org/scribe/builder/api/Google2Api.java | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 src/main/java/org/scribe/builder/api/Google2Api.java diff --git a/src/main/java/org/scribe/builder/api/Google2Api.java b/src/main/java/org/scribe/builder/api/Google2Api.java new file mode 100644 index 000000000..9d4eb3149 --- /dev/null +++ b/src/main/java/org/scribe/builder/api/Google2Api.java @@ -0,0 +1,118 @@ +package org.scribe.builder.api; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.scribe.exceptions.OAuthException; +import org.scribe.extractors.AccessTokenExtractor; +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.Verb; +import org.scribe.model.Verifier; +import org.scribe.oauth.OAuth20ServiceImpl; +import org.scribe.oauth.OAuthService; +import org.scribe.utils.OAuthEncoder; +import org.scribe.utils.Preconditions; + +/** + * Google OAuth2.0 + * Released under the same license as scribe (MIT License) + * @author yincrash + * + */ +public class Google2Api extends DefaultApi20 { + + private static final String AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=%s&redirect_uri=%s"; + private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s"; + + @Override + public String getAccessTokenEndpoint() { + return "https://accounts.google.com/o/oauth2/token"; + } + + @Override + public AccessTokenExtractor getAccessTokenExtractor() { + return new AccessTokenExtractor() { + + @Override + 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("\"access_token\" : \"([^&\"]+)\"").matcher(response); + if (matcher.find()) + { + String token = OAuthEncoder.decode(matcher.group(1)); + return new Token(token, "", response); + } + else + { + throw new OAuthException("Response body is incorrect. Can't extract a token from this: '" + response + "'", null); + } + } + }; + } + + @Override + public String getAuthorizationUrl(OAuthConfig config) { + // Append scope if present + if (config.hasScope()) { + return String.format(SCOPED_AUTHORIZE_URL, config.getApiKey(), + OAuthEncoder.encode(config.getCallback()), + OAuthEncoder.encode(config.getScope())); + } else { + return String.format(AUTHORIZE_URL, config.getApiKey(), + OAuthEncoder.encode(config.getCallback())); + } + } + + @Override + public Verb getAccessTokenVerb() { + return Verb.POST; + } + + @Override + public OAuthService createService(OAuthConfig config) { + return new GoogleOAuth2Service(this, config); + } + + private class GoogleOAuth2Service extends OAuth20ServiceImpl { + + private static final String GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code"; + private static final String GRANT_TYPE = "grant_type"; + private DefaultApi20 api; + private OAuthConfig config; + + public GoogleOAuth2Service(DefaultApi20 api, OAuthConfig config) { + super(api, config); + this.api = api; + this.config = config; + } + + @Override + public Token getAccessToken(Token requestToken, Verifier verifier) { + OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); + switch (api.getAccessTokenVerb()) { + case POST: + request.addBodyParameter(OAuthConstants.CLIENT_ID, config.getApiKey()); + request.addBodyParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret()); + request.addBodyParameter(OAuthConstants.CODE, verifier.getValue()); + request.addBodyParameter(OAuthConstants.REDIRECT_URI, config.getCallback()); + request.addBodyParameter(GRANT_TYPE, GRANT_TYPE_AUTHORIZATION_CODE); + break; + case GET: + default: + 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()); + if(config.hasScope()) request.addQuerystringParameter(OAuthConstants.SCOPE, config.getScope()); + } + Response response = request.send(); + return api.getAccessTokenExtractor().extract(response.getBody()); + } + } + +} From ef4e656e32cdafc61064978bc61dcafae2d61525 Mon Sep 17 00:00:00 2001 From: Christian Huff Date: Thu, 31 May 2012 11:32:06 +0200 Subject: [PATCH 18/27] Removed redundant Google2Api.java, removed @override annotations from JsonTokenExtractor --- .../org/scribe/builder/api/Google2Api.java | 118 ------------------ .../scribe/extractors/JsonTokenExtractor.java | 1 - 2 files changed, 119 deletions(-) delete mode 100644 src/main/java/org/scribe/builder/api/Google2Api.java diff --git a/src/main/java/org/scribe/builder/api/Google2Api.java b/src/main/java/org/scribe/builder/api/Google2Api.java deleted file mode 100644 index 9d4eb3149..000000000 --- a/src/main/java/org/scribe/builder/api/Google2Api.java +++ /dev/null @@ -1,118 +0,0 @@ -package org.scribe.builder.api; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.scribe.exceptions.OAuthException; -import org.scribe.extractors.AccessTokenExtractor; -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.Verb; -import org.scribe.model.Verifier; -import org.scribe.oauth.OAuth20ServiceImpl; -import org.scribe.oauth.OAuthService; -import org.scribe.utils.OAuthEncoder; -import org.scribe.utils.Preconditions; - -/** - * Google OAuth2.0 - * Released under the same license as scribe (MIT License) - * @author yincrash - * - */ -public class Google2Api extends DefaultApi20 { - - private static final String AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=%s&redirect_uri=%s"; - private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s"; - - @Override - public String getAccessTokenEndpoint() { - return "https://accounts.google.com/o/oauth2/token"; - } - - @Override - public AccessTokenExtractor getAccessTokenExtractor() { - return new AccessTokenExtractor() { - - @Override - 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("\"access_token\" : \"([^&\"]+)\"").matcher(response); - if (matcher.find()) - { - String token = OAuthEncoder.decode(matcher.group(1)); - return new Token(token, "", response); - } - else - { - throw new OAuthException("Response body is incorrect. Can't extract a token from this: '" + response + "'", null); - } - } - }; - } - - @Override - public String getAuthorizationUrl(OAuthConfig config) { - // Append scope if present - if (config.hasScope()) { - return String.format(SCOPED_AUTHORIZE_URL, config.getApiKey(), - OAuthEncoder.encode(config.getCallback()), - OAuthEncoder.encode(config.getScope())); - } else { - return String.format(AUTHORIZE_URL, config.getApiKey(), - OAuthEncoder.encode(config.getCallback())); - } - } - - @Override - public Verb getAccessTokenVerb() { - return Verb.POST; - } - - @Override - public OAuthService createService(OAuthConfig config) { - return new GoogleOAuth2Service(this, config); - } - - private class GoogleOAuth2Service extends OAuth20ServiceImpl { - - private static final String GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code"; - private static final String GRANT_TYPE = "grant_type"; - private DefaultApi20 api; - private OAuthConfig config; - - public GoogleOAuth2Service(DefaultApi20 api, OAuthConfig config) { - super(api, config); - this.api = api; - this.config = config; - } - - @Override - public Token getAccessToken(Token requestToken, Verifier verifier) { - OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); - switch (api.getAccessTokenVerb()) { - case POST: - request.addBodyParameter(OAuthConstants.CLIENT_ID, config.getApiKey()); - request.addBodyParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret()); - request.addBodyParameter(OAuthConstants.CODE, verifier.getValue()); - request.addBodyParameter(OAuthConstants.REDIRECT_URI, config.getCallback()); - request.addBodyParameter(GRANT_TYPE, GRANT_TYPE_AUTHORIZATION_CODE); - break; - case GET: - default: - 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()); - if(config.hasScope()) request.addQuerystringParameter(OAuthConstants.SCOPE, config.getScope()); - } - Response response = request.send(); - return api.getAccessTokenExtractor().extract(response.getBody()); - } - } - -} diff --git a/src/main/java/org/scribe/extractors/JsonTokenExtractor.java b/src/main/java/org/scribe/extractors/JsonTokenExtractor.java index 55d344769..98d713a0d 100644 --- a/src/main/java/org/scribe/extractors/JsonTokenExtractor.java +++ b/src/main/java/org/scribe/extractors/JsonTokenExtractor.java @@ -10,7 +10,6 @@ public class JsonTokenExtractor implements AccessTokenExtractor { private Pattern accessTokenPattern = Pattern.compile("\"access_token\"\\s*:\\s*\"(\\S*?)\""); - @Override public Token extract(String response) { Preconditions.checkEmptyString(response, "Cannot extract a token from a null or empty String"); From d7759598ab13f1325d8928683e7ebdb28162e55f Mon Sep 17 00:00:00 2001 From: Christian Huff Date: Mon, 4 Jun 2012 09:23:58 +0200 Subject: [PATCH 19/27] *ENHANCE: Added accessType and expiresIn to Token and added refresh token functionality for Google --- .../org/scribe/builder/ServiceBuilder.java | 15 +- .../org/scribe/builder/api/FacebookApi.java | 73 ++++++--- .../org/scribe/builder/api/GoogleApi20.java | 152 +++++++++++++++--- .../java/org/scribe/model/OAuthConfig.java | 16 +- src/main/java/org/scribe/model/Token.java | 58 ++++--- 5 files changed, 240 insertions(+), 74 deletions(-) diff --git a/src/main/java/org/scribe/builder/ServiceBuilder.java b/src/main/java/org/scribe/builder/ServiceBuilder.java index b7b0a2e4b..a25e6ca59 100644 --- a/src/main/java/org/scribe/builder/ServiceBuilder.java +++ b/src/main/java/org/scribe/builder/ServiceBuilder.java @@ -23,6 +23,7 @@ public class ServiceBuilder private String scope; private SignatureType signatureType; private String grantType; + private String accessType; private OutputStream debugStream; /** @@ -128,6 +129,18 @@ public ServiceBuilder scope(String scope) this.scope = scope; return this; } + + /** + * Configures the OAuth access type. This is only necessary in some APIs (like Google's). + * + * @param accessType the OAuth access type, e.g. online or offline + * @return the {@link ServiceBuilder} instance for method chaining + */ + public ServiceBuilder accessType(String accessType) { + Preconditions.checkEmptyString(accessType, "Invalid OAuth access type"); + this.accessType = accessType; + return this; + } /** * Configures the signature type, choose between header, querystring, etc. Defaults to Header @@ -178,6 +191,6 @@ public OAuthService build() Preconditions.checkNotNull(api, "You must specify a valid api through the provider() method"); Preconditions.checkEmptyString(apiKey, "You must provide an api key"); Preconditions.checkEmptyString(apiSecret, "You must provide an api secret"); - return api.createService(new OAuthConfig(apiKey, apiSecret, callback, signatureType, scope, debugStream, grantType)); + return api.createService(new OAuthConfig(apiKey, apiSecret, callback, signatureType, scope, debugStream, grantType, accessType)); } } diff --git a/src/main/java/org/scribe/builder/api/FacebookApi.java b/src/main/java/org/scribe/builder/api/FacebookApi.java index e582a2e5e..fb4e71376 100644 --- a/src/main/java/org/scribe/builder/api/FacebookApi.java +++ b/src/main/java/org/scribe/builder/api/FacebookApi.java @@ -1,39 +1,66 @@ package org.scribe.builder.api; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.scribe.exceptions.OAuthException; +import org.scribe.extractors.AccessTokenExtractor; +import org.scribe.extractors.TokenExtractor20Impl; import org.scribe.model.*; import org.scribe.utils.*; -public class FacebookApi extends DefaultApi20 -{ - private static final String AUTHORIZE_URL = "https://www.facebook.com/dialog/oauth?client_id=%s&redirect_uri=%s"; - private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s"; +public class FacebookApi extends DefaultApi20 { + private static final String AUTHORIZE_URL = "https://www.facebook.com/dialog/oauth?client_id=%s&redirect_uri=%s"; + private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s"; + + private class FacebookTokenExtractor extends TokenExtractor20Impl { + + private static final String TOKEN_REGEX = "expires=([0-9]+)"; @Override - public String getAccessTokenEndpoint() - { - return "https://graph.facebook.com/oauth/access_token"; + public Token extract(String response) { + Token token = super.extract(response); + + Matcher matcher = Pattern.compile(TOKEN_REGEX).matcher(response); + if (matcher.find()) { + int expires = Integer.valueOf(OAuthEncoder.decode(matcher.group(1))); + token.setExpiresIn(expires); + } else { + throw new OAuthException("Response body is incorrect. Can't extract a token from this: '" + response + "'", + null); + } + + return token; } + } - @Override - public String getAuthorizationUrl(OAuthConfig config) - { - Preconditions.checkValidUrl(config.getCallback(), "Must provide a valid url as callback. Facebook does not support OOB"); - - // Append scope if present - if(config.hasScope()) - { - return String.format(SCOPED_AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback()), OAuthEncoder.encode(config.getScope())); - } - else - { - return String.format(AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback())); - } + @Override + public String getAccessTokenEndpoint() { + return "https://graph.facebook.com/oauth/access_token"; + } + + @Override + public String getAuthorizationUrl(OAuthConfig config) { + Preconditions.checkValidUrl(config.getCallback(), + "Must provide a valid url as callback. Facebook does not support OOB"); + + // Append scope if present + if (config.hasScope()) { + return String.format(SCOPED_AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback()), + OAuthEncoder.encode(config.getScope())); + } else { + return String.format(AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback())); } + } + + @Override + public AccessTokenExtractor getAccessTokenExtractor() { + return new FacebookTokenExtractor(); + } @Override - public String getRefreshTokenParameterName() - { + public String getRefreshTokenParameterName() { return "fb_exchange_token"; } } \ No newline at end of file diff --git a/src/main/java/org/scribe/builder/api/GoogleApi20.java b/src/main/java/org/scribe/builder/api/GoogleApi20.java index 3a415cab9..907e61bcd 100755 --- a/src/main/java/org/scribe/builder/api/GoogleApi20.java +++ b/src/main/java/org/scribe/builder/api/GoogleApi20.java @@ -1,45 +1,145 @@ package org.scribe.builder.api; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.scribe.exceptions.OAuthException; import org.scribe.extractors.AccessTokenExtractor; -import org.scribe.extractors.JsonTokenExtractor; 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.Verb; +import org.scribe.model.Verifier; +import org.scribe.oauth.OAuth20ServiceImpl; +import org.scribe.oauth.OAuthService; +import org.scribe.utils.OAuthEncoder; +import org.scribe.utils.Preconditions; -public class GoogleApi20 - extends DefaultApi20 -{ - private static final String AUTHORIZATION_URL = - "https://accounts.google.com/o/oauth2/auth?client_id=%s&redirect_uri=%s&scope=%s&response_type=code"; +public class GoogleApi20 extends DefaultApi20 { + private static final String AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=%s&redirect_uri=%s"; + private static final String SCOPE = "&scope=%s"; + private static final String ACCESS_TYPE = "&access_type=%s"; - @Override - public String getAuthorizationUrl( OAuthConfig config ) - { - return String.format( AUTHORIZATION_URL, config.getApiKey(), config.getCallback(), config.getScope() ); - } + @Override + public String getAccessTokenEndpoint() { + return "https://accounts.google.com/o/oauth2/token"; + } - @Override - public String getAccessTokenEndpoint() - { - return "https://accounts.google.com/o/oauth2/token"; + @Override + public AccessTokenExtractor getAccessTokenExtractor() { + return new AccessTokenExtractor() { + + 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("\"access_token\" : \"([^&\"]+)\"").matcher(response); + Matcher refreshMatcher = Pattern.compile("\"refresh_token\" : \"([^&\"]+)\"").matcher(response); + Matcher expiryMatcher = Pattern.compile("\"expires_in\" : ([0-9]+)").matcher(response); + if (matcher.find()) { + String token = OAuthEncoder.decode(matcher.group(1)); + Token refreshToken = null; + int expiresIn = -1; + + if (refreshMatcher.find()) { + String refreshTokenString = OAuthEncoder.decode(refreshMatcher.group(1)); + refreshToken = new Token(refreshTokenString, "", response); + } + + if (expiryMatcher.find()) { + String expiryString = OAuthEncoder.decode(expiryMatcher.group(1)); + expiresIn = Integer.parseInt(expiryString); + } + + return new Token(token, "", response, refreshToken, expiresIn); + } else { + throw new OAuthException("Response body is incorrect. Can't extract a token from this: '" + response + "'", + null); + } + } + }; + } + + @Override + public String getAuthorizationUrl(OAuthConfig config) { + String url = String.format(AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback())); + if (config.hasScope()) { + // Append scope if present + url = url.concat(String.format(SCOPE, OAuthEncoder.encode(config.getScope()))); } + if (config.hasAccessType()) { + // Append access type if present + url = url.concat(String.format(ACCESS_TYPE, OAuthEncoder.encode(config.getAccessType()))); + } + return url; - @Override - public Verb getAccessTokenVerb() - { - return Verb.POST; + } + + @Override + public Verb getAccessTokenVerb() { + return Verb.POST; + } + + @Override + public OAuthService createService(OAuthConfig config) { + return new GoogleOAuth2Service(this, config); + } + + private class GoogleOAuth2Service extends OAuth20ServiceImpl { + + private static final String GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code"; + private static final String GRANT_TYPE = "grant_type"; + private DefaultApi20 api; + private OAuthConfig config; + + public GoogleOAuth2Service(DefaultApi20 api, OAuthConfig config) { + super(api, config); + this.api = api; + this.config = config; } @Override - public AccessTokenExtractor getAccessTokenExtractor() - { - return new JsonTokenExtractor(); + public Token getAccessToken(Token requestToken, Verifier verifier) { + OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); + switch (api.getAccessTokenVerb()) { + case POST: + request.addBodyParameter(OAuthConstants.CLIENT_ID, config.getApiKey()); + request.addBodyParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret()); + request.addBodyParameter(OAuthConstants.CODE, verifier.getValue()); + request.addBodyParameter(OAuthConstants.REDIRECT_URI, config.getCallback()); + request.addBodyParameter(GRANT_TYPE, GRANT_TYPE_AUTHORIZATION_CODE); + break; + case GET: + default: + 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()); + if (config.hasScope()) + request.addQuerystringParameter(OAuthConstants.SCOPE, config.getScope()); + } + Response response = request.send(); + return api.getAccessTokenExtractor().extract(response.getBody()); } @Override - public String getTokenParameterName() - { - /* google OAuth2.0 specs needs oauth_token parameter name instead of access_token */ - return OAuthConstants.TOKEN; + public Token refreshAccessToken(Token accessToken) { + String accessTokenEndpoint = api.getAccessTokenEndpoint(); + OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), accessTokenEndpoint); + request.addBodyParameter(OAuthConstants.CLIENT_ID, config.getApiKey()); + request.addBodyParameter(OAuthConstants.CLIENT_SECRET, config.getApiSecret()); + request.addBodyParameter(OAuthConstants.GRANT_TYPE, api.getRefreshTokenParameterName()); + request.addBodyParameter(api.getRefreshTokenParameterName(), accessToken.getToken()); + Response response = request.send(); + return api.getAccessTokenExtractor().extract(response.getBody()); } + } + + @Override + public String getRefreshTokenParameterName() { + return "refresh_token"; + } + } diff --git a/src/main/java/org/scribe/model/OAuthConfig.java b/src/main/java/org/scribe/model/OAuthConfig.java index 4c12edfe9..51cde94dc 100644 --- a/src/main/java/org/scribe/model/OAuthConfig.java +++ b/src/main/java/org/scribe/model/OAuthConfig.java @@ -16,6 +16,7 @@ public class OAuthConfig private final String scope; private final String grantType; private final OutputStream debugStream; + private final String accessType; public OAuthConfig(String key, String secret ) { @@ -24,11 +25,11 @@ public OAuthConfig(String key, String secret ) public OAuthConfig(String key, String secret, String callback, SignatureType type, String scope, OutputStream stream) { - this(key, secret, callback, type, scope, stream, null); + this(key, secret, callback, type, scope, stream, null, null); } public OAuthConfig(String key, String secret, String callback, SignatureType type, String scope, OutputStream stream, - String grantType) + String grantType, String accessType) { this.apiKey = key; this.apiSecret = secret; @@ -37,6 +38,7 @@ public OAuthConfig(String key, String secret, String callback, SignatureType typ this.scope = scope; this.grantType = grantType; this.debugStream = stream; + this.accessType = accessType; } public String getApiKey() @@ -63,6 +65,11 @@ public String getScope() { return scope; } + + public String getAccessType() + { + return accessType; + } public String getGrantType() { @@ -78,6 +85,11 @@ public boolean hasGrantType() { return grantType != null; } + + public boolean hasAccessType() + { + return accessType != null; + } public void log(String message) { diff --git a/src/main/java/org/scribe/model/Token.java b/src/main/java/org/scribe/model/Token.java index 286fec69d..572affc55 100644 --- a/src/main/java/org/scribe/model/Token.java +++ b/src/main/java/org/scribe/model/Token.java @@ -7,54 +7,68 @@ * * @author Pablo Fernandez */ -public class Token implements Serializable -{ +public class Token implements Serializable { private static final long serialVersionUID = 715000866082812683L; - - private final String token; - private final String secret; - private final String rawResponse; + + private final String token; + private final String secret; + private final String rawResponse; + private Token refreshToken = null; + private int expiresIn = 0; /** * Default constructor * - * @param token token value - * @param secret token secret + * @param token + * token value + * @param secret + * token secret */ - public Token(String token, String secret) - { + public Token(String token, String secret) { this(token, secret, null); } - public Token(String token, String secret, String rawResponse) - { + public Token(String token, String secret, String rawResponse) { this.token = token; this.secret = secret; this.rawResponse = rawResponse; } - public String getToken() - { + public Token(String token, String secret, String rawResponse, Token refreshToken, int expiresIn) { + this(token, secret, rawResponse); + this.refreshToken = refreshToken; + this.expiresIn = expiresIn; + } + + public String getToken() { return token; } - public String getSecret() - { + public String getSecret() { return secret; } - public String getRawResponse() - { - if (rawResponse == null) - { + public String getRawResponse() { + if (rawResponse == null) { throw new IllegalStateException("This token object was not constructed by scribe and does not have a rawResponse"); } return rawResponse; } @Override - public String toString() - { + public String toString() { return String.format("Token[%s , %s]", token, secret); } + + public Token getRefreshToken() { + return refreshToken; + } + + public int getExpiresIn() { + return expiresIn; + } + + public void setExpiresIn(int expiresIn) { + this.expiresIn = expiresIn; + } } From 4e9631eeeae20f0131e60452937644cf8c88c82e Mon Sep 17 00:00:00 2001 From: Christian Huff Date: Fri, 13 Jul 2012 11:57:53 +0200 Subject: [PATCH 20/27] Pull from github --- .../java/org/scribe/builder/api/YammerApi.java | 2 +- src/main/java/org/scribe/model/Token.java | 18 ++++++++++++++++++ .../org/scribe/oauth/OAuth10aServiceImpl.java | 5 +++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/scribe/builder/api/YammerApi.java b/src/main/java/org/scribe/builder/api/YammerApi.java index 68bcda511..e06543d97 100644 --- a/src/main/java/org/scribe/builder/api/YammerApi.java +++ b/src/main/java/org/scribe/builder/api/YammerApi.java @@ -5,7 +5,7 @@ public class YammerApi extends DefaultApi10a { - private static final String AUTHORIZATION_URL = "'https://www.yammer.com/oauth/authorize?oauth_token=%s'"; + private static final String AUTHORIZATION_URL = "https://www.yammer.com/oauth/authorize?oauth_token=%s"; @Override public String getRequestTokenEndpoint() diff --git a/src/main/java/org/scribe/model/Token.java b/src/main/java/org/scribe/model/Token.java index 572affc55..7e9b0fa6e 100644 --- a/src/main/java/org/scribe/model/Token.java +++ b/src/main/java/org/scribe/model/Token.java @@ -71,4 +71,22 @@ public int getExpiresIn() { public void setExpiresIn(int expiresIn) { this.expiresIn = expiresIn; } + + /** + * Returns true if the token is empty (token = "", secret = "") + */ + public boolean isEmpty() + { + return "".equals(this.token) && "".equals(this.secret); + } + + /** + * Factory method that returns an empty token (token = "", secret = ""). + * + * Useful for two legged OAuth. + */ + public static Token empty() + { + return new Token("",""); + } } diff --git a/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java index 00a7f320e..57fa74075 100644 --- a/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java +++ b/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java @@ -98,6 +98,11 @@ public void signRequest(Token token, OAuthRequest request) config.log("signing request: " + request.getCompleteUrl()); request.addOAuthParameter(OAuthConstants.TOKEN, token.getToken()); + // Do not append the token if empty. This is for two legged OAuth calls. + if (!token.isEmpty()) + { + request.addOAuthParameter(OAuthConstants.TOKEN, token.getToken()); + } config.log("setting token to: " + token); addOAuthParams(request, token); appendSignature(request); From 8c021ae8cd093c328c966d86e8b6b473ca0499cb Mon Sep 17 00:00:00 2001 From: Christian Huff Date: Fri, 13 Jul 2012 12:01:45 +0200 Subject: [PATCH 21/27] Pull from github --- src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java index 57fa74075..137de6ed0 100644 --- a/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java +++ b/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java @@ -8,7 +8,7 @@ /** * OAuth 1.0a implementation of {@link OAuthService} - * + * * @author Pablo Fernandez */ public class OAuth10aServiceImpl implements OAuthService @@ -20,7 +20,7 @@ public class OAuth10aServiceImpl implements OAuthService /** * Default constructor - * + * * @param api OAuth1.0a api information * @param config OAuth 1.0a configuration param object */ @@ -96,7 +96,6 @@ public Token refreshAccessToken(Token accessToken) public void signRequest(Token token, OAuthRequest request) { config.log("signing request: " + request.getCompleteUrl()); - request.addOAuthParameter(OAuthConstants.TOKEN, token.getToken()); // Do not append the token if empty. This is for two legged OAuth calls. if (!token.isEmpty()) @@ -123,7 +122,7 @@ public String getAuthorizationUrl(Token requestToken) { return api.getAuthorizationUrl(requestToken); } - + private String getSignature(OAuthRequest request, Token token) { config.log("generating signature..."); From 5e7281cbf403ee95e8dc22290c63e21f426ba9df Mon Sep 17 00:00:00 2001 From: Christian Huff Date: Mon, 3 Dec 2012 17:13:31 +0100 Subject: [PATCH 22/27] Merge from 1.3.2 upstream --- README.textile | 2 +- changelog.txt | 11 +- pom.xml | 6 +- .../builder/api/ConstantContactApi2.java | 55 +++ .../org/scribe/builder/api/LinkedInApi.java | 2 +- .../org/scribe/builder/api/MendeleyApi.java | 43 +++ .../org/scribe/builder/api/TrelloApi.java | 27 ++ .../org/scribe/builder/api/UbuntuOneApi.java | 40 +++ .../org/scribe/builder/api/VkontakteApi.java | 2 +- .../scribe/extractors/JsonTokenExtractor.java | 4 +- src/main/java/org/scribe/model/Request.java | 77 ++--- .../java/org/scribe/model/RequestTuner.java | 6 + src/main/java/org/scribe/model/Token.java | 184 +++++----- src/main/java/org/scribe/model/Verb.java | 2 +- .../org/scribe/oauth/OAuth10aServiceImpl.java | 327 ++++++++++-------- .../services/PlaintextSignatureService.java | 2 +- .../services/RSASha1SignatureService.java | 47 +++ .../org/scribe/examples/TrelloExample.java | 60 ++++ src/test/java/org/scribe/model/TokenTest.java | 44 +++ .../services/RSASha1SignatureServiceTest.java | 66 ++++ 20 files changed, 729 insertions(+), 278 deletions(-) create mode 100644 src/main/java/org/scribe/builder/api/ConstantContactApi2.java create mode 100644 src/main/java/org/scribe/builder/api/MendeleyApi.java create mode 100644 src/main/java/org/scribe/builder/api/TrelloApi.java create mode 100644 src/main/java/org/scribe/builder/api/UbuntuOneApi.java create mode 100644 src/main/java/org/scribe/model/RequestTuner.java create mode 100644 src/main/java/org/scribe/services/RSASha1SignatureService.java create mode 100644 src/test/java/org/scribe/examples/TrelloExample.java create mode 100644 src/test/java/org/scribe/model/TokenTest.java create mode 100644 src/test/java/org/scribe/services/RSASha1SignatureServiceTest.java diff --git a/README.textile b/README.textile index 9cecfb451..a20bccaa7 100644 --- a/README.textile +++ b/README.textile @@ -65,7 +65,7 @@ You can pull scribe from a maven repository, just add this to your __pom.xml__ f @@ @org.scribe@ @scribe@ - @1.3.0@ + @1.3.2@ @@ h1. Getting started in less than 2 minutes diff --git a/changelog.txt b/changelog.txt index dedcba636..617567805 100644 --- a/changelog.txt +++ b/changelog.txt @@ -85,4 +85,13 @@ * FIX: Allow digits in url schemes * FEATURE: Specific exception for connection problems (OAuthConnectionException) * FIX: Dropbox Api and Evernote Api updated to their latests versions - * FIX: Digg and Skyrock Apis + * FEATURE: Digg and Skyrock Apis + +[1.3.2] + * FIX: Don't include oauth_token in 2legged calls. + * FEATURE: RSA-SHA1 Signatures. + * FEATURE: equals & hashcode on Token object. + * FEATURE: ConstantContact Api + +[1.3.3] + * FEATURE: accessToken and requestToken timeouts default to 2 seconds and can be specified. diff --git a/pom.xml b/pom.xml index baee260d4..dbee18cf6 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.scribe scribe jar - 1.3.1-patched + 1.3.2 Scribe OAuth Library The best OAuth library out there http://github.com/fernandezpablo85/scribe-java @@ -67,7 +67,7 @@ 1.5 - + diff --git a/src/main/java/org/scribe/builder/api/ConstantContactApi2.java b/src/main/java/org/scribe/builder/api/ConstantContactApi2.java new file mode 100644 index 000000000..b5e4cac27 --- /dev/null +++ b/src/main/java/org/scribe/builder/api/ConstantContactApi2.java @@ -0,0 +1,55 @@ +package org.scribe.builder.api; + +import java.util.regex.*; +import org.scribe.exceptions.*; +import org.scribe.extractors.*; +import org.scribe.model.*; +import org.scribe.utils.*; + +public class ConstantContactApi2 extends DefaultApi20 +{ + private static final String AUTHORIZE_URL = "https://oauth2.constantcontact.com/oauth2/oauth/siteowner/authorize?client_id=%s&response_type=code&redirect_uri=%s"; + + @Override + public String getAccessTokenEndpoint() + { + return "https://oauth2.constantcontact.com/oauth2/oauth/token?grant_type=authorization_code"; + } + + @Override + public String getAuthorizationUrl(OAuthConfig config) + { + return String.format(AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback())); + } + + @Override + public Verb getAccessTokenVerb() + { + return Verb.POST; + } + + @Override + public AccessTokenExtractor getAccessTokenExtractor() + { + return new AccessTokenExtractor() + { + + public Token extract(String response) + { + Preconditions.checkEmptyString(response, "Response body is incorrect. Can't extract a token from an empty string"); + + String regex = "\"access_token\"\\s*:\\s*\"([^&\"]+)\""; + Matcher matcher = Pattern.compile(regex).matcher(response); + if (matcher.find()) + { + String token = OAuthEncoder.decode(matcher.group(1)); + return new Token(token, "", response); + } + else + { + throw new OAuthException("Response body is incorrect. Can't extract a token from this: '" + response + "'", null); + } + } + }; + } +} \ No newline at end of file diff --git a/src/main/java/org/scribe/builder/api/LinkedInApi.java b/src/main/java/org/scribe/builder/api/LinkedInApi.java index f459404d3..dc3e58327 100644 --- a/src/main/java/org/scribe/builder/api/LinkedInApi.java +++ b/src/main/java/org/scribe/builder/api/LinkedInApi.java @@ -4,7 +4,7 @@ public class LinkedInApi extends DefaultApi10a { - private static final String AUTHORIZE_URL = "https://api.linkedin.com/uas/oauth/authorize?oauth_token=%s"; + private static final String AUTHORIZE_URL = "https://api.linkedin.com/uas/oauth/authenticate?oauth_token=%s"; @Override public String getAccessTokenEndpoint() diff --git a/src/main/java/org/scribe/builder/api/MendeleyApi.java b/src/main/java/org/scribe/builder/api/MendeleyApi.java new file mode 100644 index 000000000..c6e0eced7 --- /dev/null +++ b/src/main/java/org/scribe/builder/api/MendeleyApi.java @@ -0,0 +1,43 @@ +package org.scribe.builder.api; + +import org.scribe.model.*; + +/** + * @author Arieh "Vainolo" Bibliowicz + * @see http://apidocs.mendeley.com/home/authentication + */ +public class MendeleyApi extends DefaultApi10a +{ + + private static final String AUTHORIZATION_URL = "http://api.mendeley.com/oauth/authorize?oauth_token=%s"; + + @Override + public String getRequestTokenEndpoint() + { + return "http://api.mendeley.com/oauth/request_token/"; + } + + @Override + public String getAccessTokenEndpoint() + { + return "http://api.mendeley.com/oauth/access_token/"; + } + + @Override + public String getAuthorizationUrl(Token requestToken) + { + return String.format(AUTHORIZATION_URL, requestToken.getToken()); + } + + @Override + public Verb getAccessTokenVerb() + { + return Verb.GET; + } + + @Override + public Verb getRequestTokenVerb() + { + return Verb.GET; + } +} diff --git a/src/main/java/org/scribe/builder/api/TrelloApi.java b/src/main/java/org/scribe/builder/api/TrelloApi.java new file mode 100644 index 000000000..9e46730d4 --- /dev/null +++ b/src/main/java/org/scribe/builder/api/TrelloApi.java @@ -0,0 +1,27 @@ +package org.scribe.builder.api; + +import org.scribe.model.*; + +public class TrelloApi extends DefaultApi10a +{ + private static final String AUTHORIZE_URL = "https://trello.com/1/OAuthAuthorizeToken?oauth_token=%s"; + + @Override + public String getAccessTokenEndpoint() + { + return "https://trello.com/1/OAuthGetAccessToken"; + } + + @Override + public String getRequestTokenEndpoint() + { + return "https://trello.com/1/OAuthGetRequestToken"; + } + + @Override + public String getAuthorizationUrl(Token requestToken) + { + return String.format(AUTHORIZE_URL, requestToken.getToken()); + } + +} diff --git a/src/main/java/org/scribe/builder/api/UbuntuOneApi.java b/src/main/java/org/scribe/builder/api/UbuntuOneApi.java new file mode 100644 index 000000000..2a72c3afe --- /dev/null +++ b/src/main/java/org/scribe/builder/api/UbuntuOneApi.java @@ -0,0 +1,40 @@ +package org.scribe.builder.api; + +import org.scribe.model.Token; +import org.scribe.services.*; + +/** + * @author Julio Gutierrez + * + * Sep 6, 2012 + */ +public class UbuntuOneApi extends DefaultApi10a +{ + + private static final String AUTHORIZATION_URL = "https://one.ubuntu.com/oauth/authorize/?oauth_token=%s"; + + @Override + public String getAccessTokenEndpoint() + { + return "https://one.ubuntu.com/oauth/access/"; + } + + @Override + public String getAuthorizationUrl(Token requestToken) + { + return String.format(AUTHORIZATION_URL, requestToken.getToken()); + } + + @Override + public String getRequestTokenEndpoint() + { + return "https://one.ubuntu.com/oauth/request/"; + } + + @Override + public SignatureService getSignatureService() + { + return new PlaintextSignatureService(); + } + +} diff --git a/src/main/java/org/scribe/builder/api/VkontakteApi.java b/src/main/java/org/scribe/builder/api/VkontakteApi.java index 83050e3bd..e12f39559 100644 --- a/src/main/java/org/scribe/builder/api/VkontakteApi.java +++ b/src/main/java/org/scribe/builder/api/VkontakteApi.java @@ -10,7 +10,7 @@ */ public class VkontakteApi extends DefaultApi20 { - private static final String AUTHORIZE_URL = "https://api.vkontakte.ru/oauth/authorize?client_id=%s&redirect_uri=%s&response_type=code"; + private static final String AUTHORIZE_URL = "https://oauth.vk.com/authorize?client_id=%s&redirect_uri=%s&response_type=code"; private static final String SCOPED_AUTHORIZE_URL = String.format("%s&scope=%%s", AUTHORIZE_URL); @Override diff --git a/src/main/java/org/scribe/extractors/JsonTokenExtractor.java b/src/main/java/org/scribe/extractors/JsonTokenExtractor.java index 98d713a0d..801cd6296 100644 --- a/src/main/java/org/scribe/extractors/JsonTokenExtractor.java +++ b/src/main/java/org/scribe/extractors/JsonTokenExtractor.java @@ -8,7 +8,7 @@ public class JsonTokenExtractor implements AccessTokenExtractor { - private Pattern accessTokenPattern = Pattern.compile("\"access_token\"\\s*:\\s*\"(\\S*?)\""); + private Pattern accessTokenPattern = Pattern.compile("\"access_token\":\\s*\"(\\S*?)\""); public Token extract(String response) { @@ -24,4 +24,4 @@ public Token extract(String response) } } -} +} \ No newline at end of file diff --git a/src/main/java/org/scribe/model/Request.java b/src/main/java/org/scribe/model/Request.java index 81f0e73b5..b19e4a80d 100644 --- a/src/main/java/org/scribe/model/Request.java +++ b/src/main/java/org/scribe/model/Request.java @@ -10,13 +10,16 @@ /** * Represents an HTTP Request object - * + * * @author Pablo Fernandez */ -class Request +public class Request { private static final String CONTENT_LENGTH = "Content-Length"; private static final String CONTENT_TYPE = "Content-Type"; + private static RequestTuner NOOP = new RequestTuner() { + @Override public void tune(Request _){} + }; public static final String DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded"; private String url; @@ -34,7 +37,7 @@ class Request /** * Creates a new Http Request - * + * * @param verb Http Verb (GET, POST, etc) * @param url url with optional querystring parameters. */ @@ -49,17 +52,17 @@ public Request(Verb verb, String url) /** * Execute the request and return a {@link Response} - * + * * @return Http Response * @throws RuntimeException * if the connection cannot be created. */ - public Response send() + public Response send(RequestTuner tuner) { try { createConnection(); - return doSend(); + return doSend(tuner); } catch (Exception e) { @@ -67,6 +70,11 @@ public Response send() } } + public Response send() + { + return send(NOOP); + } + private void createConnection() throws IOException { String completeUrl = getCompleteUrl(); @@ -84,21 +92,13 @@ private void createConnection() throws IOException */ public String getCompleteUrl() { - switch ( getVerb() ) - { - case GET: - return querystringParams.appendTo(url); - - case POST: - default: - return url; - } + return querystringParams.appendTo(url); } - Response doSend() throws IOException + Response doSend(RequestTuner tuner) throws IOException { connection.setRequestMethod(this.verb.name()); - if (connectTimeout != null) + if (connectTimeout != null) { connection.setConnectTimeout(connectTimeout.intValue()); } @@ -111,6 +111,7 @@ Response doSend() throws IOException { addBody(connection, getByteBodyContents()); } + tuner.tune(this); return new Response(connection); } @@ -130,18 +131,12 @@ void addBody(HttpURLConnection conn, byte[] content) throws IOException conn.setRequestProperty(CONTENT_TYPE, DEFAULT_CONTENT_TYPE); } conn.setDoOutput(true); - conn.getOutputStream().write( content ); - if ( getVerb().equals( Verb.POST ) ) - { - OutputStreamWriter wr = new OutputStreamWriter( conn.getOutputStream() ); - wr.write( querystringParams.asFormUrlEncodedString() ); - wr.flush(); - } + conn.getOutputStream().write(content); } /** * Add an HTTP Header to the Request - * + * * @param key the header name * @param value the header value */ @@ -152,7 +147,7 @@ public void addHeader(String key, String value) /** * Add a body Parameter (for POST/ PUT Requests) - * + * * @param key the parameter name * @param value the parameter value */ @@ -174,12 +169,12 @@ public void addQuerystringParameter(String key, String value) /** * Add body payload. - * + * * This method is used when the HTTP body is not a form-url-encoded string, * but another thing. Like for example XML. - * + * * Note: The contents are not part of the OAuth signature - * + * * @param payload the body of the request */ public void addPayload(String payload) @@ -199,7 +194,7 @@ public void addPayload(byte[] payload) /** * Get a {@link ParameterList} with the query string parameters. - * + * * @return a {@link ParameterList} containing the query string parameters. * @throws OAuthException if the request URL is not valid. */ @@ -221,7 +216,7 @@ public ParameterList getQueryStringParams() /** * Obtains a {@link ParameterList} of the body parameters. - * + * * @return a {@link ParameterList}containing the body parameters. */ public ParameterList getBodyParams() @@ -231,7 +226,7 @@ public ParameterList getBodyParams() /** * Obtains the URL of the HTTP Request. - * + * * @return the original URL of the HTTP Request */ public String getUrl() @@ -241,7 +236,7 @@ public String getUrl() /** * Returns the URL without the port and the query string part. - * + * * @return the OAuth-sanitized URL */ public String getSanitizedUrl() @@ -251,7 +246,7 @@ public String getSanitizedUrl() /** * Returns the body of the request - * + * * @return form encoded string * @throws OAuthException if the charset chosen is not supported */ @@ -283,17 +278,17 @@ byte[] getByteBodyContents() /** * Returns the HTTP Verb - * + * * @return the verb */ public Verb getVerb() { return verb; } - + /** * Returns the connection headers as a {@link Map} - * + * * @return map of headers */ public Map getHeaders() @@ -313,9 +308,9 @@ public String getCharset() /** * Sets the connect timeout for the underlying {@link HttpURLConnection} - * + * * @param duration duration of the timeout - * + * * @param unit unit of time (milliseconds, seconds, etc) */ public void setConnectTimeout(int duration, TimeUnit unit) @@ -325,9 +320,9 @@ public void setConnectTimeout(int duration, TimeUnit unit) /** * Sets the read timeout for the underlying {@link HttpURLConnection} - * + * * @param duration duration of the timeout - * + * * @param unit unit of time (milliseconds, seconds, etc) */ public void setReadTimeout(int duration, TimeUnit unit) diff --git a/src/main/java/org/scribe/model/RequestTuner.java b/src/main/java/org/scribe/model/RequestTuner.java new file mode 100644 index 000000000..34ea1eb45 --- /dev/null +++ b/src/main/java/org/scribe/model/RequestTuner.java @@ -0,0 +1,6 @@ +package org.scribe.model; + +public abstract class RequestTuner +{ + public abstract void tune(Request request); +} \ No newline at end of file diff --git a/src/main/java/org/scribe/model/Token.java b/src/main/java/org/scribe/model/Token.java index 7e9b0fa6e..abc0dce6f 100644 --- a/src/main/java/org/scribe/model/Token.java +++ b/src/main/java/org/scribe/model/Token.java @@ -1,6 +1,7 @@ package org.scribe.model; -import java.io.Serializable; +import java.io.*; +import org.scribe.utils.*; /** * Represents an OAuth token (either request or access token) and its secret @@ -8,85 +9,104 @@ * @author Pablo Fernandez */ public class Token implements Serializable { - private static final long serialVersionUID = 715000866082812683L; - - private final String token; - private final String secret; - private final String rawResponse; - private Token refreshToken = null; - private int expiresIn = 0; - - /** - * Default constructor - * - * @param token - * token value - * @param secret - * token secret - */ - public Token(String token, String secret) { - this(token, secret, null); - } - - public Token(String token, String secret, String rawResponse) { - this.token = token; - this.secret = secret; - this.rawResponse = rawResponse; - } - - public Token(String token, String secret, String rawResponse, Token refreshToken, int expiresIn) { - this(token, secret, rawResponse); - this.refreshToken = refreshToken; - this.expiresIn = expiresIn; - } - - public String getToken() { - return token; - } - - public String getSecret() { - return secret; - } - - public String getRawResponse() { - if (rawResponse == null) { - throw new IllegalStateException("This token object was not constructed by scribe and does not have a rawResponse"); - } - return rawResponse; - } - - @Override - public String toString() { - return String.format("Token[%s , %s]", token, secret); - } - - public Token getRefreshToken() { - return refreshToken; - } - - public int getExpiresIn() { - return expiresIn; - } - - public void setExpiresIn(int expiresIn) { - this.expiresIn = expiresIn; - } - - /** - * Returns true if the token is empty (token = "", secret = "") - */ - public boolean isEmpty() - { - return "".equals(this.token) && "".equals(this.secret); - } - - /** - * Factory method that returns an empty token (token = "", secret = ""). - * - * Useful for two legged OAuth. - */ - public static Token empty() - { - return new Token("",""); - } + private static final long serialVersionUID = 715000866082812683L; + + private final String token; + private final String secret; + private final String rawResponse; + private Token refreshToken = null; + private int expiresIn = 0; + + /** + * Default constructor + * + * @param token + * token value. Can't be null. + * @param secret + * token secret. Can't be null. + */ + public Token(String token, String secret) { + this(token, secret, null); + } + + public Token(String token, String secret, String rawResponse) { + Preconditions.checkNotNull(token, "Token can't be null"); + Preconditions.checkNotNull(secret, "Secret can't be null"); + + this.token = token; + this.secret = secret; + this.rawResponse = rawResponse; + } + + public Token(String token, String secret, String rawResponse, + Token refreshToken, int expiresIn) { + this(token, secret, rawResponse); + this.refreshToken = refreshToken; + this.expiresIn = expiresIn; + } + + public String getToken() { + return token; + } + + public String getSecret() { + return secret; + } + + public String getRawResponse() { + if (rawResponse == null) { + throw new IllegalStateException( + "This token object was not constructed by scribe and does not have a rawResponse"); + } + return rawResponse; + } + + @Override + public String toString() { + return String.format("Token[%s , %s]", token, secret); + } + + public Token getRefreshToken() { + return refreshToken; + } + + public int getExpiresIn() { + return expiresIn; + } + + public void setExpiresIn(int expiresIn) { + this.expiresIn = expiresIn; + } + + /** + * Returns true if the token is empty (token = "", secret = "") + */ + public boolean isEmpty() { + return "".equals(this.token) && "".equals(this.secret); + } + + /** + * Factory method that returns an empty token (token = "", secret = ""). + * + * Useful for two legged OAuth. + */ + public static Token empty() { + return new Token("", ""); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + Token that = (Token) o; + return token.equals(that.token) && secret.equals(that.secret); + } + + @Override + public int hashCode() { + return 31 * token.hashCode() + secret.hashCode(); + } } diff --git a/src/main/java/org/scribe/model/Verb.java b/src/main/java/org/scribe/model/Verb.java index b370791c6..0c22f6690 100644 --- a/src/main/java/org/scribe/model/Verb.java +++ b/src/main/java/org/scribe/model/Verb.java @@ -7,5 +7,5 @@ */ public enum Verb { - GET, POST, PUT, DELETE + GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, PATCH } diff --git a/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java index 137de6ed0..981997bc7 100644 --- a/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java +++ b/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java @@ -5,153 +5,192 @@ import org.scribe.builder.api.*; import org.scribe.model.*; import org.scribe.utils.*; +import java.util.concurrent.TimeUnit; /** * OAuth 1.0a implementation of {@link OAuthService} - * + * * @author Pablo Fernandez */ -public class OAuth10aServiceImpl implements OAuthService -{ - private static final String VERSION = "1.0"; - - private OAuthConfig config; - private DefaultApi10a api; - - /** - * Default constructor - * - * @param api OAuth1.0a api information - * @param config OAuth 1.0a configuration param object - */ - public OAuth10aServiceImpl(DefaultApi10a api, OAuthConfig config) - { - this.api = api; - this.config = config; - } - - /** - * {@inheritDoc} - */ - public Token getRequestToken() - { - config.log("obtaining request token from " + api.getRequestTokenEndpoint()); - OAuthRequest request = new OAuthRequest(api.getRequestTokenVerb(), api.getRequestTokenEndpoint()); - - config.log("setting oauth_callback to " + config.getCallback()); - request.addOAuthParameter(OAuthConstants.CALLBACK, config.getCallback()); - addOAuthParams(request, OAuthConstants.EMPTY_TOKEN); - appendSignature(request); - - config.log("sending request..."); - Response response = request.send(); - String body = response.getBody(); - - config.log("response status code: " + response.getCode()); - config.log("response body: " + body); - return api.getRequestTokenExtractor().extract(body); - } - - private void addOAuthParams(OAuthRequest request, Token token) - { - request.addOAuthParameter(OAuthConstants.TIMESTAMP, api.getTimestampService().getTimestampInSeconds()); - request.addOAuthParameter(OAuthConstants.NONCE, api.getTimestampService().getNonce()); - request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, config.getApiKey()); - request.addOAuthParameter(OAuthConstants.SIGN_METHOD, api.getSignatureService().getSignatureMethod()); - request.addOAuthParameter(OAuthConstants.VERSION, getVersion()); - if(config.hasScope()) request.addOAuthParameter(OAuthConstants.SCOPE, config.getScope()); - request.addOAuthParameter(OAuthConstants.SIGNATURE, getSignature(request, token)); - - config.log("appended additional OAuth parameters: " + MapUtils.toString(request.getOauthParameters())); - } - - /** - * {@inheritDoc} - */ - public Token getAccessToken(Token requestToken, Verifier verifier) - { - config.log("obtaining access token from " + api.getAccessTokenEndpoint()); - OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint()); - request.addOAuthParameter(OAuthConstants.TOKEN, requestToken.getToken()); - request.addOAuthParameter(OAuthConstants.VERIFIER, verifier.getValue()); - - config.log("setting token to: " + requestToken + " and verifier to: " + verifier); - addOAuthParams(request, requestToken); - appendSignature(request); - Response response = request.send(); - return api.getAccessTokenExtractor().extract(response.getBody()); - } - - /** - * {@inheritDoc} - */ - public Token refreshAccessToken(Token accessToken) - { - throw new UnsupportedOperationException("Refresh token is not supported in Scribe OAuth 1.0"); - } - - /** - * {@inheritDoc} - */ - public void signRequest(Token token, OAuthRequest request) - { - config.log("signing request: " + request.getCompleteUrl()); - - // Do not append the token if empty. This is for two legged OAuth calls. - if (!token.isEmpty()) - { - request.addOAuthParameter(OAuthConstants.TOKEN, token.getToken()); - } - config.log("setting token to: " + token); - addOAuthParams(request, token); - appendSignature(request); - } - - /** - * {@inheritDoc} - */ - public String getVersion() - { - return VERSION; - } - - /** - * {@inheritDoc} - */ - public String getAuthorizationUrl(Token requestToken) - { - return api.getAuthorizationUrl(requestToken); - } - - private String getSignature(OAuthRequest request, Token token) - { - config.log("generating signature..."); - String baseString = api.getBaseStringExtractor().extract(request); - String signature = api.getSignatureService().getSignature(baseString, config.getApiSecret(), token.getSecret()); - - config.log("base string is: " + baseString); - config.log("signature is: " + signature); - return signature; - } - - private void appendSignature(OAuthRequest request) - { - switch (config.getSignatureType()) - { - case Header: - config.log("using Http Header signature"); - - String oauthHeader = api.getHeaderExtractor().extract(request); - request.addHeader(OAuthConstants.HEADER, oauthHeader); - break; - case QueryString: - config.log("using Querystring signature"); - - for (Map.Entry entry : request.getOauthParameters().entrySet()) - { - request.addQuerystringParameter(entry.getKey(), entry.getValue()); - } - break; - } - } +public class OAuth10aServiceImpl implements OAuthService { + private static final String VERSION = "1.0"; + + private OAuthConfig config; + private DefaultApi10a api; + + /** + * Default constructor + * + * @param api + * OAuth1.0a api information + * @param config + * OAuth 1.0a configuration param object + */ + public OAuth10aServiceImpl(DefaultApi10a api, OAuthConfig config) { + this.api = api; + this.config = config; + } + + /** + * {@inheritDoc} + */ + public Token getRequestToken(int timeout, TimeUnit unit) { + return getRequestToken(new TimeoutTuner(timeout, unit)); + } + + public Token getRequestToken() { + return getRequestToken(2, TimeUnit.SECONDS); + } + + public Token getRequestToken(RequestTuner tuner) { + config.log("obtaining request token from " + + api.getRequestTokenEndpoint()); + OAuthRequest request = new OAuthRequest(api.getRequestTokenVerb(), + api.getRequestTokenEndpoint()); + + config.log("setting oauth_callback to " + config.getCallback()); + request.addOAuthParameter(OAuthConstants.CALLBACK, config.getCallback()); + addOAuthParams(request, OAuthConstants.EMPTY_TOKEN); + appendSignature(request); + + config.log("sending request..."); + Response response = request.send(tuner); + String body = response.getBody(); + + config.log("response status code: " + response.getCode()); + config.log("response body: " + body); + return api.getRequestTokenExtractor().extract(body); + } + + private void addOAuthParams(OAuthRequest request, Token token) { + request.addOAuthParameter(OAuthConstants.TIMESTAMP, api + .getTimestampService().getTimestampInSeconds()); + request.addOAuthParameter(OAuthConstants.NONCE, api + .getTimestampService().getNonce()); + request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, + config.getApiKey()); + request.addOAuthParameter(OAuthConstants.SIGN_METHOD, api + .getSignatureService().getSignatureMethod()); + request.addOAuthParameter(OAuthConstants.VERSION, getVersion()); + if (config.hasScope()) + request.addOAuthParameter(OAuthConstants.SCOPE, config.getScope()); + request.addOAuthParameter(OAuthConstants.SIGNATURE, + getSignature(request, token)); + + config.log("appended additional OAuth parameters: " + + MapUtils.toString(request.getOauthParameters())); + } + + /** + * {@inheritDoc} + */ + public Token getAccessToken(Token requestToken, Verifier verifier, + int timeout, TimeUnit unit) { + return getAccessToken(requestToken, verifier, new TimeoutTuner(timeout, + unit)); + } + + public Token getAccessToken(Token requestToken, Verifier verifier) { + return getAccessToken(requestToken, verifier, 2, TimeUnit.SECONDS); + } + + public Token getAccessToken(Token requestToken, Verifier verifier, + RequestTuner tuner) { + config.log("obtaining access token from " + + api.getAccessTokenEndpoint()); + OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), + api.getAccessTokenEndpoint()); + request.addOAuthParameter(OAuthConstants.TOKEN, requestToken.getToken()); + request.addOAuthParameter(OAuthConstants.VERIFIER, verifier.getValue()); + + config.log("setting token to: " + requestToken + " and verifier to: " + + verifier); + addOAuthParams(request, requestToken); + appendSignature(request); + Response response = request.send(tuner); + return api.getAccessTokenExtractor().extract(response.getBody()); + } + + /** + * {@inheritDoc} + */ + public void signRequest(Token token, OAuthRequest request) { + config.log("signing request: " + request.getCompleteUrl()); + + // Do not append the token if empty. This is for two legged OAuth calls. + if (!token.isEmpty()) { + request.addOAuthParameter(OAuthConstants.TOKEN, token.getToken()); + } + config.log("setting token to: " + token); + addOAuthParams(request, token); + appendSignature(request); + } + + /** + * {@inheritDoc} + */ + public String getVersion() { + return VERSION; + } + + /** + * {@inheritDoc} + */ + public String getAuthorizationUrl(Token requestToken) { + return api.getAuthorizationUrl(requestToken); + } + + private String getSignature(OAuthRequest request, Token token) { + config.log("generating signature..."); + String baseString = api.getBaseStringExtractor().extract(request); + String signature = api.getSignatureService().getSignature(baseString, + config.getApiSecret(), token.getSecret()); + + config.log("base string is: " + baseString); + config.log("signature is: " + signature); + return signature; + } + + private void appendSignature(OAuthRequest request) { + switch (config.getSignatureType()) { + case Header: + config.log("using Http Header signature"); + + String oauthHeader = api.getHeaderExtractor().extract(request); + request.addHeader(OAuthConstants.HEADER, oauthHeader); + break; + case QueryString: + config.log("using Querystring signature"); + + for (Map.Entry entry : request.getOauthParameters() + .entrySet()) { + request.addQuerystringParameter(entry.getKey(), + entry.getValue()); + } + break; + } + } + + private static class TimeoutTuner extends RequestTuner { + private final int duration; + private final TimeUnit unit; + + public TimeoutTuner(int duration, TimeUnit unit) { + this.duration = duration; + this.unit = unit; + } + + @Override + public void tune(Request request) { + request.setReadTimeout(duration, unit); + } + } + + /** + * {@inheritDoc} + */ + public Token refreshAccessToken(Token accessToken) { + throw new UnsupportedOperationException( + "Refresh token is not supported in Scribe OAuth 1.0"); + } } diff --git a/src/main/java/org/scribe/services/PlaintextSignatureService.java b/src/main/java/org/scribe/services/PlaintextSignatureService.java index b71ca973d..03306e8e3 100644 --- a/src/main/java/org/scribe/services/PlaintextSignatureService.java +++ b/src/main/java/org/scribe/services/PlaintextSignatureService.java @@ -11,7 +11,7 @@ */ public class PlaintextSignatureService implements SignatureService { - private static final String METHOD = "plaintext"; + private static final String METHOD = "PLAINTEXT"; /** * {@inheritDoc} diff --git a/src/main/java/org/scribe/services/RSASha1SignatureService.java b/src/main/java/org/scribe/services/RSASha1SignatureService.java new file mode 100644 index 000000000..3b996f5c6 --- /dev/null +++ b/src/main/java/org/scribe/services/RSASha1SignatureService.java @@ -0,0 +1,47 @@ +package org.scribe.services; + +import org.apache.commons.codec.binary.*; +import org.scribe.exceptions.*; +import java.security.*; + +/** + * A signature service that uses the RSA-SHA1 algorithm. + */ +public class RSASha1SignatureService implements SignatureService +{ + private static final String METHOD = "RSA-SHA1"; + private static final String RSA_SHA1 = "SHA1withRSA"; + + private PrivateKey privateKey; + + public RSASha1SignatureService(PrivateKey privateKey) + { + this.privateKey = privateKey; + } + + /** + * {@inheritDoc} + */ + public String getSignature(String baseString, String apiSecret, String tokenSecret) + { + try + { + Signature signature = Signature.getInstance(RSA_SHA1); + signature.initSign(privateKey); + signature.update(baseString.getBytes()); + return new String(Base64.encodeBase64(signature.sign(), false)); + } + catch (Exception e) + { + throw new OAuthSignatureException(baseString, e); + } + } + + /** + * {@inheritDoc} + */ + public String getSignatureMethod() + { + return METHOD; + } +} \ No newline at end of file diff --git a/src/test/java/org/scribe/examples/TrelloExample.java b/src/test/java/org/scribe/examples/TrelloExample.java new file mode 100644 index 000000000..9888f5e90 --- /dev/null +++ b/src/test/java/org/scribe/examples/TrelloExample.java @@ -0,0 +1,60 @@ +package org.scribe.examples; + +import java.util.Scanner; + +import org.scribe.builder.*; +import org.scribe.builder.api.*; +import org.scribe.model.*; +import org.scribe.oauth.*; + +public class TrelloExample +{ + private static final String API_KEY = "your_api_key"; + private static final String API_SECRET = "your_api_secret"; + private static final String PROTECTED_RESOURCE_URL = "https://trello.com/1/members/me"; + public static void main(String[] args) + { + OAuthService service = new ServiceBuilder() + .provider(TrelloApi.class) + .apiKey(API_KEY) + .apiSecret(API_SECRET) + .build(); + Scanner in = new Scanner(System.in); + + System.out.println("=== Trello's OAuth Workflow ==="); + System.out.println(); + + // Obtain the Request Token + System.out.println("Fetching the Request Token..."); + Token requestToken = service.getRequestToken(); + System.out.println("Got the Request Token!"); + System.out.println(); + + System.out.println("Now go and authorize Scribe here:"); + System.out.println(service.getAuthorizationUrl(requestToken)); + System.out.println("And paste the verifier here"); + System.out.print(">>"); + 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(requestToken, 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! + System.out.println("Now we're going to access a protected resource..."); + OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL); + service.signRequest(accessToken, request); + Response response = request.send(); + System.out.println("Got it! Lets see what we found..."); + System.out.println(); + System.out.println(response.getBody()); + + System.out.println(); + System.out.println("Thats it man! Go and build something awesome with Scribe! :)"); + } + +} diff --git a/src/test/java/org/scribe/model/TokenTest.java b/src/test/java/org/scribe/model/TokenTest.java new file mode 100644 index 000000000..dff59ccf7 --- /dev/null +++ b/src/test/java/org/scribe/model/TokenTest.java @@ -0,0 +1,44 @@ +package org.scribe.model; + +import static junit.framework.Assert.*; +import org.junit.*; + +public class TokenTest +{ + @Test + public void shouldTestEqualityBasedOnTokenAndSecret() throws Exception + { + Token expected = new Token("access","secret"); + Token actual = new Token("access","secret"); + + assertEquals(expected, actual); + assertEquals(actual, actual); + } + + @Test + public void shouldNotDependOnRawString() throws Exception + { + Token expected = new Token("access","secret", "raw_string"); + Token actual = new Token("access","secret", "different_raw_string"); + + assertEquals(expected, actual); + } + + @Test + public void shouldReturnSameHashCodeForEqualObjects() throws Exception + { + Token expected = new Token("access","secret"); + Token actual = new Token("access","secret"); + + assertEquals(expected.hashCode(), actual.hashCode()); + } + + @Test + public void shouldNotBeEqualToNullOrOtherObjects() throws Exception + { + Token expected = new Token("access","secret","response"); + + assertNotSame(expected, null); + assertNotSame(expected, new Object()); + } +} diff --git a/src/test/java/org/scribe/services/RSASha1SignatureServiceTest.java b/src/test/java/org/scribe/services/RSASha1SignatureServiceTest.java new file mode 100644 index 000000000..9e375c5be --- /dev/null +++ b/src/test/java/org/scribe/services/RSASha1SignatureServiceTest.java @@ -0,0 +1,66 @@ +package org.scribe.services; + +import static org.junit.Assert.*; + +import org.junit.*; +import org.apache.commons.codec.binary.*; +import java.security.*; +import java.security.spec.*; + +public class RSASha1SignatureServiceTest +{ + + RSASha1SignatureService service = new RSASha1SignatureService(getPrivateKey()); + + @Test + public void shouldReturnSignatureMethodString() + { + String expected = "RSA-SHA1"; + assertEquals(expected, service.getSignatureMethod()); + } + + @Test + public void shouldReturnSignature() + { + String apiSecret = "api secret"; + String tokenSecret = "token secret"; + String baseString = "base string"; + String signature = "LUNRzQAlpdNyM9mLXm96Va6g/qVNnEAb7p7K1KM0g8IopOFQJPoOO7cvppgt7w3QyhijWJnCmvqXaaIAGrqvdyr3fIzBULh8D/iZQUNLMi08GCOA34P81XBvsc7A5uJjPDsGhJg2MzoVJ8nWJhU/lMMk4c92S1WGskeoDofRwpo="; + assertEquals(signature, service.getSignature(baseString, apiSecret, tokenSecret)); + } + + /** + *Created primary key using openssl. + * + * openssl req -x509 -nodes -days 365 -newkey rsa:1024 -sha1 -subj '/C=GB/ST=/L=Manchester/CN=www.example.com' -keyout myrsakey.pem -out /tmp/myrsacert.pem + * openssl pkcs8 -in myrsakey.pem -topk8 -nocrypt -out myrsakey.pk8 + */ + private static PrivateKey getPrivateKey() + { + String str = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMPQ5BCMxlUq2TYy\n"+ + "iRIoEUsz6HGTJhHuasS2nx1Se4Co3lxwxyubVdFj8AuhHNJSmJvjlpbTsGOjLZpr\n"+ + "HyDEDdJmf1Fensh1MhUnBZ4a7uLrZrKzFHHJdamX9pxapB89vLeHlCot9hVXdrZH\n"+ + "nNtg6FdmRKH/8gbs8iDyIayFvzYDAgMBAAECgYA+c9MpTBy9cQsR9BAvkEPjvkx2\n"+ + "XL4ZnfbDgpNA4Nuu7yzsQrPjPomiXMNkkiAFHH67yVxwAlgRjyuuQlgNNTpKvyQt\n"+ + "XcHxffnU0820VmE23M+L7jg2TlB3+rUnEDmDvCoyjlwGDR6lNb7t7Fgg2iR+iaov\n"+ + "0iVzz+l9w0slRlyGsQJBAPWXW2m3NmFgqfDxtw8fsKC2y8o17/cnPjozRGtWb8LQ\n"+ + "g3VCb8kbOFHOYNGazq3M7+wD1qILF2h/HecgK9eQrZ0CQQDMHXoJMfKKbrFrTKgE\n"+ + "zyggO1gtuT5OXYeFewMEb5AbDI2FfSc2YP7SHij8iQ2HdukBrbTmi6qxh3HmIR58\n"+ + "I/AfAkEA0Y9vr0tombsUB8cZv0v5OYoBZvCTbMANtzfb4AOHpiKqqbohDOevLQ7/\n"+ + "SpvgVCmVaDz2PptcRAyEBZ5MCssneQJAB2pmvaDH7Ambfod5bztLfOhLCtY5EkXJ\n"+ + "n6rZcDbRaHorRhdG7m3VtDKOUKZ2DF7glkQGV33phKukErVPUzlHBwJAScD9TqaG\n"+ + "wJ3juUsVtujV23SnH43iMggXT7m82STpPGam1hPfmqu2Z0niePFo927ogQ7H1EMJ\n"+ + "UHgqXmuvk2X/Ww=="; + + try + { + KeyFactory fac = KeyFactory.getInstance("RSA"); + PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(str.getBytes())); + return fac.generatePrivate(privKeySpec); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } +} From 2c30da316f541ff6add80f8f350a88905e28ac63 Mon Sep 17 00:00:00 2001 From: Christian Huff Date: Mon, 3 Dec 2012 17:23:15 +0100 Subject: [PATCH 23/27] Merge from SVN: Don't throw an exception if token does not provide expiration time --- src/main/java/org/scribe/builder/api/FacebookApi.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/scribe/builder/api/FacebookApi.java b/src/main/java/org/scribe/builder/api/FacebookApi.java index fb4e71376..dbb5ebf8c 100644 --- a/src/main/java/org/scribe/builder/api/FacebookApi.java +++ b/src/main/java/org/scribe/builder/api/FacebookApi.java @@ -3,12 +3,12 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.scribe.exceptions.OAuthException; import org.scribe.extractors.AccessTokenExtractor; import org.scribe.extractors.TokenExtractor20Impl; -import org.scribe.model.*; - -import org.scribe.utils.*; +import org.scribe.model.OAuthConfig; +import org.scribe.model.Token; +import org.scribe.utils.OAuthEncoder; +import org.scribe.utils.Preconditions; public class FacebookApi extends DefaultApi20 { private static final String AUTHORIZE_URL = "https://www.facebook.com/dialog/oauth?client_id=%s&redirect_uri=%s"; @@ -26,9 +26,6 @@ public Token extract(String response) { if (matcher.find()) { int expires = Integer.valueOf(OAuthEncoder.decode(matcher.group(1))); token.setExpiresIn(expires); - } else { - throw new OAuthException("Response body is incorrect. Can't extract a token from this: '" + response + "'", - null); } return token; From 3f59f2b4613ca6769eae1511c418c96c9a83ec7f Mon Sep 17 00:00:00 2001 From: Christian Huff Date: Mon, 3 Dec 2012 17:24:07 +0100 Subject: [PATCH 24/27] Changed artifact Id --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dbee18cf6..8a1cb9af4 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.scribe scribe jar - 1.3.2 + 1.3.2-patched Scribe OAuth Library The best OAuth library out there http://github.com/fernandezpablo85/scribe-java From 424bd39212b5cfdbc9bd96ce7536a7da2f4cd871 Mon Sep 17 00:00:00 2001 From: Christian Huff Date: Tue, 4 Dec 2012 10:36:53 +0100 Subject: [PATCH 25/27] Reintegrate changes --- src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java | 3 --- src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java index d469008a0..670bc7ced 100644 --- a/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java +++ b/src/main/java/org/scribe/oauth/OAuth10aServiceImpl.java @@ -186,9 +186,6 @@ public void tune(Request request) { } } - /** - * {@inheritDoc} - */ public Token refreshAccessToken(Token accessToken) { throw new UnsupportedOperationException( "Refresh token is not supported in Scribe OAuth 1.0"); diff --git a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java index dd22ed905..e238d789b 100644 --- a/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java +++ b/src/main/java/org/scribe/oauth/OAuth20ServiceImpl.java @@ -33,6 +33,7 @@ public Token getAccessToken(Token requestToken, Verifier verifier) request.addQuerystringParameter(OAuthConstants.CODE, verifier.getValue()); request.addQuerystringParameter(OAuthConstants.REDIRECT_URI, config.getCallback()); if (config.hasScope()) request.addQuerystringParameter(OAuthConstants.SCOPE, config.getScope()); + if (config.hasGrantType()) request.addQuerystringParameter(OAuthConstants.GRANT_TYPE, config.getGrantType()); Response response = request.send(); return api.getAccessTokenExtractor().extract(response.getBody()); } From 1c99fc00db5e9e26ad98d5b460edb1273241331e Mon Sep 17 00:00:00 2001 From: Christian Huff Date: Tue, 4 Dec 2012 11:10:38 +0100 Subject: [PATCH 26/27] Bugfix: Disable gpg plugin --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8a1cb9af4..de7b0ba18 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ 1.5 - + From 09b1a984a315cb0ae8be5da12be3fd3e4056e483 Mon Sep 17 00:00:00 2001 From: Christian Huff Date: Tue, 12 Mar 2013 09:38:07 +0100 Subject: [PATCH 27/27] Removed artifact signing and made inner classes static, as suggested by findbugs --- pom.xml | 4 ++-- src/main/java/org/scribe/builder/api/FacebookApi.java | 2 +- src/main/java/org/scribe/builder/api/GoogleApi20.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index b4fe7110b..2c26155b7 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,7 @@ org.apache.maven.plugins maven-gpg-plugin 1.4 - + org.codehaus.mojo diff --git a/src/main/java/org/scribe/builder/api/FacebookApi.java b/src/main/java/org/scribe/builder/api/FacebookApi.java index dbb5ebf8c..f19310cbf 100644 --- a/src/main/java/org/scribe/builder/api/FacebookApi.java +++ b/src/main/java/org/scribe/builder/api/FacebookApi.java @@ -14,7 +14,7 @@ public class FacebookApi extends DefaultApi20 { private static final String AUTHORIZE_URL = "https://www.facebook.com/dialog/oauth?client_id=%s&redirect_uri=%s"; private static final String SCOPED_AUTHORIZE_URL = AUTHORIZE_URL + "&scope=%s"; - private class FacebookTokenExtractor extends TokenExtractor20Impl { + private static class FacebookTokenExtractor extends TokenExtractor20Impl { private static final String TOKEN_REGEX = "expires=([0-9]+)"; diff --git a/src/main/java/org/scribe/builder/api/GoogleApi20.java b/src/main/java/org/scribe/builder/api/GoogleApi20.java index 907e61bcd..3fcdb3eb0 100755 --- a/src/main/java/org/scribe/builder/api/GoogleApi20.java +++ b/src/main/java/org/scribe/builder/api/GoogleApi20.java @@ -87,7 +87,7 @@ public OAuthService createService(OAuthConfig config) { return new GoogleOAuth2Service(this, config); } - private class GoogleOAuth2Service extends OAuth20ServiceImpl { + private static class GoogleOAuth2Service extends OAuth20ServiceImpl { private static final String GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code"; private static final String GRANT_TYPE = "grant_type";