diff --git a/changelog.txt b/changelog.txt index 47ed18c..1ae4834 100644 --- a/changelog.txt +++ b/changelog.txt @@ -41,3 +41,4 @@ * FEATURE: Added rawResponse to Token, for extracting additional parameters (thanks Dirk McCormick) * FEATURE: Added Dropbox Api * FIX: Token responses now work despite of the token/secret order + * FEATURE: Added Foursquare OAuth 2.0 Api (thanks Tom Tasche) diff --git a/src/main/java/org/scribe/builder/api/Foursquare2Api.java b/src/main/java/org/scribe/builder/api/Foursquare2Api.java new file mode 100644 index 0000000..5bb0fdc --- /dev/null +++ b/src/main/java/org/scribe/builder/api/Foursquare2Api.java @@ -0,0 +1,29 @@ +package org.scribe.builder.api; + +import org.scribe.extractors.*; +import org.scribe.model.*; +import org.scribe.utils.*; + +public class Foursquare2Api extends DefaultApi20 +{ + private static final String AUTHORIZATION_URL = "https://foursquare.com/oauth2/authenticate?client_id=%s&response_type=code&redirect_uri=%s"; + + @Override + public String getAccessTokenEndpoint() + { + return "https://foursquare.com/oauth2/access_token?grant_type=authorization_code"; + } + + @Override + public String getAuthorizationUrl(OAuthConfig config) + { + Preconditions.checkValidUrl(config.getCallback(), "Must provide a valid url as callback. Foursquare2 does not support OOB"); + return String.format(AUTHORIZATION_URL, config.getApiKey(), URLUtils.formURLEncode(config.getCallback())); + } + + @Override + public AccessTokenExtractor getAccessTokenExtractor() + { + return new JsonTokenExtractor(); + } +} diff --git a/src/main/java/org/scribe/extractors/FoursquareAccessTokenExtractor.java b/src/main/java/org/scribe/extractors/FoursquareAccessTokenExtractor.java new file mode 100644 index 0000000..4ef5433 --- /dev/null +++ b/src/main/java/org/scribe/extractors/FoursquareAccessTokenExtractor.java @@ -0,0 +1,28 @@ +package org.scribe.extractors; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.scribe.exceptions.OAuthException; +import org.scribe.extractors.TokenExtractor20Impl; +import org.scribe.model.Token; +import org.scribe.utils.Preconditions; +import org.scribe.utils.URLUtils; + +public class FoursquareAccessTokenExtractor extends TokenExtractor20Impl { + + private static final String TOKEN_REGEX = ".[\\s]+\"access_token\":\"(\\S*?)(&(\\S*))?\"[\\s]+."; + private static final String EMPTY_SECRET = ""; + + public Token extract(String response) { + Preconditions.checkEmptyString(response, "Response body is incorrect. Can't extract a token from an empty string"); + + Matcher matcher = Pattern.compile(TOKEN_REGEX).matcher(response); + if (matcher.matches()) { + String token = URLUtils.formURLDecode(matcher.group(1)); + return new Token(token, EMPTY_SECRET); + } else { + throw new OAuthException("Response body is incorrect. Can't extract a token from this: '" + response + "'", null); + } + } +} diff --git a/src/main/java/org/scribe/extractors/JsonTokenExtractor.java b/src/main/java/org/scribe/extractors/JsonTokenExtractor.java new file mode 100644 index 0000000..fbbe3d1 --- /dev/null +++ b/src/main/java/org/scribe/extractors/JsonTokenExtractor.java @@ -0,0 +1,28 @@ +package org.scribe.extractors; + +import java.util.regex.*; + +import org.scribe.exceptions.*; +import org.scribe.model.*; +import org.scribe.utils.*; + +public class JsonTokenExtractor implements AccessTokenExtractor +{ + private Pattern accessTokenPattern = Pattern.compile("\"access_token\":\"(\\S*)\""); + + @Override + public Token extract(String response) + { + Preconditions.checkEmptyString(response, "Cannot extract a token from a null or empty String"); + Matcher matcher = accessTokenPattern.matcher(response); + if(matcher.find()) + { + return new Token(matcher.group(1), ""); + } + else + { + throw new OAuthException("Cannot extract an acces token. Response was: "+response); + } + } + +} diff --git a/src/test/java/org/scribe/examples/Foursquare2Example.java b/src/test/java/org/scribe/examples/Foursquare2Example.java new file mode 100644 index 0000000..35495a8 --- /dev/null +++ b/src/test/java/org/scribe/examples/Foursquare2Example.java @@ -0,0 +1,63 @@ +package org.scribe.examples; + +import java.util.*; + +import org.scribe.builder.*; +import org.scribe.builder.api.*; +import org.scribe.model.*; +import org.scribe.oauth.*; + +public class Foursquare2Example +{ + private static final String PROTECTED_RESOURCE_URL = "https://api.foursquare.com/v2/users/self/friends?oauth_token="; + private static final Token EMPTY_TOKEN = null; + + public static void main(String[] args) + { + // Replace these with your own api key and secret + String apiKey = "FEGFXJUFANVVDHVSNUAMUKTTXCP1AJQD53E33XKJ44YP1S4I"; + String apiSecret = "AYWKUL5SWPNC0CTQ202QXRUG2NLZYXMRA34ZSDW4AUYBG2RC"; + OAuthService service = new ServiceBuilder() + .provider(Foursquare2Api.class) + .apiKey(apiKey) + .apiSecret(apiSecret) + .callback("http://localhost:9000/") + .build(); + Scanner in = new Scanner(System.in); + + System.out.println("=== Foursquare2's OAuth Workflow ==="); + System.out.println(); + + // Obtain the Authorization URL + 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 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, PROTECTED_RESOURCE_URL + accessToken.getToken()); + 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! :)"); + + } +} diff --git a/src/test/java/org/scribe/extractors/JsonTokenExtractorTest.java b/src/test/java/org/scribe/extractors/JsonTokenExtractorTest.java new file mode 100644 index 0000000..412e131 --- /dev/null +++ b/src/test/java/org/scribe/extractors/JsonTokenExtractorTest.java @@ -0,0 +1,31 @@ +package org.scribe.extractors; + +import static org.junit.Assert.*; + +import org.junit.*; +import org.scribe.model.*; + +public class JsonTokenExtractorTest +{ + private String response = "'{ \"access_token\":\"I0122HHJKLEM21F3WLPYHDKGKZULAUO4SGMV3ABKFTDT3T3X\"}'"; + private JsonTokenExtractor extractor = new JsonTokenExtractor(); + + @Test + public void shouldParseResponse() + { + Token token = extractor.extract(response); + assertEquals(token.getToken(), "I0122HHJKLEM21F3WLPYHDKGKZULAUO4SGMV3ABKFTDT3T3X"); + } + + @Test(expected=IllegalArgumentException.class) + public void shouldThrowExceptionIfForNullParameters() + { + extractor.extract(null); + } + + @Test(expected=IllegalArgumentException.class) + public void shouldThrowExceptionIfForEmptyStrings() + { + extractor.extract(""); + } +}