diff --git a/app/src/main/java/com/codepath/oauth/OAuthAsyncHttpClient.java b/app/src/main/java/com/codepath/oauth/OAuthAsyncHttpClient.java deleted file mode 100755 index 8b4a2b5..0000000 --- a/app/src/main/java/com/codepath/oauth/OAuthAsyncHttpClient.java +++ /dev/null @@ -1,176 +0,0 @@ -package com.codepath.oauth; - -import android.content.Context; -import android.net.Uri; - -import com.codepath.utils.AsyncSimpleTask; -import com.github.scribejava.core.builder.ServiceBuilder; -import com.github.scribejava.core.builder.api.BaseApi; -import com.github.scribejava.core.exceptions.OAuthException; -import com.github.scribejava.core.model.OAuth1RequestToken; -import com.github.scribejava.core.model.OAuthConstants; -import com.github.scribejava.core.model.Token; -import com.github.scribejava.core.oauth.OAuth10aService; -import com.github.scribejava.core.oauth.OAuth20Service; -import com.github.scribejava.core.oauth.OAuthService; -import com.loopj.android.http.AsyncHttpClient; -import com.loopj.android.http.RequestHandle; -import com.loopj.android.http.ResponseHandlerInterface; - -/* - * OAuthAsyncHttpClient is responsible for managing the request and access token exchanges and then - * signing all requests with the OAuth signature after access token has been retrieved and stored. - * The client is based on AsyncHttpClient for async http requests and uses Scribe to manage the OAuth authentication. - */ -public class OAuthAsyncHttpClient extends AsyncHttpClient { - - private BaseApi apiInstance; - private OAuthTokenHandler handler; - private Token accessToken; - private OAuthService service; - - // Requires the apiClass, consumerKey, consumerSecret and callbackUrl along with the TokenHandler - public OAuthAsyncHttpClient(BaseApi apiInstance, String consumerKey, String consumerSecret, String callbackUrl, - OAuthTokenHandler handler) { - this.apiInstance = apiInstance; - this.handler = handler; - if (callbackUrl == null) { callbackUrl = OAuthConstants.OUT_OF_BAND; }; - this.service = new ServiceBuilder() - .apiKey(consumerKey) - .apiSecret(consumerSecret).callback(callbackUrl) - .build(apiInstance); - } - - // Get a request token and the authorization url - // Once fetched, fire the onReceivedRequestToken for the request token handler - // Works for both OAuth1.0a and OAuth2 - public void fetchRequestToken() { - new AsyncSimpleTask(new AsyncSimpleTask.AsyncSimpleTaskHandler() { - String authorizeUrl = null; - Exception e = null; - Token requestToken; - - public void doInBackground() { - try { - if (service.getVersion() == "1.0") { - OAuth10aService oAuth10aService = (OAuth10aService) service; - requestToken = oAuth10aService.getRequestToken(); - authorizeUrl = oAuth10aService.getAuthorizationUrl((OAuth1RequestToken) requestToken); - } else if (service.getVersion() == "2.0") { - OAuth20Service oAuth20Service = (OAuth20Service) service; - authorizeUrl = oAuth20Service.getAuthorizationUrl(null); - } - } catch (Exception e) { - this.e = e; - } - } - - public void onPostExecute() { - if (e != null) { - handler.onFailure(e); - } else { - handler.onReceivedRequestToken(requestToken, authorizeUrl, service.getVersion()); - } - } - }); - } - - // Get the access token by exchanging the requestToken to the defined URL - // Once receiving the access token, fires the onReceivedAccessToken method on the handler - public void fetchAccessToken(final Token requestToken, final Uri uri) { - - new AsyncSimpleTask(new AsyncSimpleTask.AsyncSimpleTaskHandler() { - Exception e = null; - - public void doInBackground() { - // Fetch the verifier code from redirect url parameters - Uri authorizedUri = uri; - - try { - if (service.getVersion() == "1.0") { - // Use verifier token to fetch access token - - if (authorizedUri.getQuery().contains(OAuthConstants.VERIFIER)) { - String oauth_verifier = authorizedUri.getQueryParameter(OAuthConstants.VERIFIER); - OAuth1RequestToken oAuth1RequestToken = (OAuth1RequestToken) requestToken; - OAuth10aService oAuth10aService = (OAuth10aService) service; - accessToken = oAuth10aService.getAccessToken(oAuth1RequestToken, oauth_verifier); - } - else { // verifier was null - throw new OAuthException("No verifier code was returned with uri '" + uri + "' " + - "and access token cannot be retrieved"); - } - } else if (service.getVersion() == "2.0") { - if (authorizedUri.getQuery().contains(OAuthConstants.CODE)) { - String code = authorizedUri.getQueryParameter(OAuthConstants.CODE); - OAuth20Service oAuth20Service = (OAuth20Service) service; - accessToken = oAuth20Service.getAccessToken(code); - } - else { // verifier was null - throw new OAuthException("No code was returned with uri '" + uri + "' " + - "and access token cannot be retrieved"); - } - } - } catch (Exception e) { - this.e = e; - } - } - - public void onPostExecute() { - if (e != null) { - handler.onFailure(e); - } else { - setAccessToken(accessToken); - handler.onReceivedAccessToken(accessToken, service.getVersion()); - } - } - }); - } - - // Set the access token used for signing requests - public void setAccessToken(Token accessToken) { - if (accessToken == null) { - this.accessToken = null; - } else { - this.accessToken = accessToken; - } - } - - public Token getAccessToken() { - return this.accessToken; - } - - // Send scribe signed request based on the async http client to construct a signed request - // Accepts an HttpEntity which has the underlying entity for the request params - - @Override - protected RequestHandle sendRequest( - cz.msebera.android.httpclient.impl.client.DefaultHttpClient client, - cz.msebera.android.httpclient.protocol.HttpContext httpContext, - cz.msebera.android.httpclient.client.methods.HttpUriRequest uriRequest, - String contentType, ResponseHandlerInterface responseHandler, - Context context) { - - if (this.service != null && accessToken != null) { - try { - ScribeRequestAdapter adapter = new ScribeRequestAdapter(uriRequest); - this.service.signRequest(accessToken, adapter); - return super.sendRequest(client, httpContext, uriRequest, contentType, responseHandler, context); - } catch (Exception e) { - e.printStackTrace(); - } - } else if (accessToken == null) { - throw new OAuthException("Cannot send unauthenticated requests for " + apiInstance.getClass().getSimpleName() + " client. Please attach an access token!"); - } else { // service is null - throw new OAuthException("Cannot send unauthenticated requests for undefined service. Please specify a valid api service!"); - } - return null; // Hopefully never reaches here - } - - // Defines the interface handler for different token handlers - public interface OAuthTokenHandler { - public void onReceivedRequestToken(Token requestToken, String authorizeUrl, String oAuthVersion); - public void onReceivedAccessToken(Token accessToken, String oAuthVersion); - public void onFailure(Exception e); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/codepath/oauth/ScribeRequestAdapter.java b/app/src/main/java/com/codepath/oauth/ScribeRequestAdapter.java deleted file mode 100755 index 12f9aa8..0000000 --- a/app/src/main/java/com/codepath/oauth/ScribeRequestAdapter.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.codepath.oauth; - -import android.net.Uri; - -import com.github.scribejava.core.model.OAuthConstants; -import com.github.scribejava.core.model.OAuthRequest; -import com.github.scribejava.core.model.ParameterList; -import com.github.scribejava.core.model.Verb; - -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import cz.msebera.android.httpclient.NameValuePair; -import cz.msebera.android.httpclient.client.methods.HttpEntityEnclosingRequestBase; -import cz.msebera.android.httpclient.client.methods.HttpRequestBase; -import cz.msebera.android.httpclient.client.methods.HttpUriRequest; -import cz.msebera.android.httpclient.client.utils.URLEncodedUtils; - -/* - * Implements the scribe-java Request interface allowing - * AsyncHttpClient requests to be signed with Scribe. - */ -public class ScribeRequestAdapter extends OAuthRequest { - private cz.msebera.android.httpclient.client.methods.HttpUriRequest httpUriRequest; - private HashMap oauthParameters; - - public ScribeRequestAdapter( - cz.msebera.android.httpclient.client.methods.HttpUriRequest httpUriRequest) { - - super(getMethod(httpUriRequest.getMethod()), httpUriRequest.getURI().toString()); - - this.httpUriRequest = httpUriRequest; - this.oauthParameters = new HashMap(); - } - - public static Verb getMethod(String method) { - switch (method) { - case "GET": - return Verb.GET; - case "POST": - return Verb.POST; - case "DELETE": - return Verb.DELETE; - case "PUT": - return Verb.PUT; - case "PATCH": - return Verb.PATCH; - case "OPTIONS": - return Verb.OPTIONS; - } - throw new IllegalStateException(method); - } - // Adds OAuth parameter with associated value - @Override - public void addOAuthParameter(String key, String value) { - this.oauthParameters.put(key, value); - } - - // Returns OAuth parameters - @Override - public Map getOauthParameters() { - return this.oauthParameters; - } - - // Adds header entry with associated value - @Override - public void addHeader(String key, String value) { - this.httpUriRequest.addHeader(key, value); - } - - // Add query string parameters to the HTTP Request. - @Override - public void addQuerystringParameter(String key, String value) { - // Workaround since some OAuth2 require "access_token" and others "oauth_token" - if (key.equals(OAuthConstants.ACCESS_TOKEN)) { addQuerystringParameter(OAuthConstants.TOKEN, value); } - // Workaround, convert URI to Uri, build on the URL to add the new query parameter and then update the HTTP Request - Uri updatedUri = Uri.parse(httpUriRequest.getURI().toString()).buildUpon().appendQueryParameter(key, value).build(); - ((HttpRequestBase) httpUriRequest).setURI(URI.create(updatedUri.toString())); - } - - // Returns query strings embedded in the URL - @Override - public ParameterList getQueryStringParams() { - try { - return parseQueryParams(); - } catch (UnsupportedEncodingException e) { - return new ParameterList(); - } - } - - // Returns params parsed from the entity body - @Override - public ParameterList getBodyParams() { - if (getVerb() == Verb.GET || getVerb() == Verb.DELETE) { return new ParameterList(); } - else { return parseEntityParams(); } - } - - // Returns the full URL with query strings - @Override - public String getCompleteUrl() { - return getHttpRequest().getURI().toString(); - } - - // Returns the base URL without query strings or host - @Override - public String getSanitizedUrl() { - return getCompleteUrl().replaceAll("\\?.*", "").replace("\\:\\d{4}", ""); - } - - // Returns Verb enum for the request method (i.e Verb.GET) - @Override - public Verb getVerb() { - return Verb.valueOf(getHttpRequest().getMethod()); - } - - // Returns simple string representation of the request - // i.e @Request(GET http://foo.com/bar) - @Override - public String toString() - { - return String.format("@Request(%s %s)", getVerb(), getCompleteUrl()); - } - - // Returns the underlying HTTP request - protected HttpUriRequest getHttpRequest() { - return this.httpUriRequest; - } - - // Parses and returns the entity provided as a ParameterList - private ParameterList parseEntityParams() { - cz.msebera.android.httpclient.HttpEntity entity = null; - List parameters = null; - try{ - entity = ((HttpEntityEnclosingRequestBase) httpUriRequest).getEntity(); - parameters = new ArrayList( URLEncodedUtils.parse(entity)); - } catch (Exception e) { - return new ParameterList(); - } - - ParameterList list = new ParameterList(); - for (NameValuePair pair : parameters) { - list.add(pair.getName(), pair.getValue()); - } - return list; - } - - // Returns the ParameterList of query parameters parsed from the URL string - private ParameterList parseQueryParams() throws UnsupportedEncodingException { - ParameterList params = new ParameterList(); - String queryString = URI.create(getCompleteUrl()).getQuery(); - if (queryString == null) { return params; } - for (String param : queryString.split("&")) { - String pair[] = param.split("="); - String key = URLDecoder.decode(pair[0], "UTF-8"); - String value = ""; - if (pair.length > 1) { - value = URLDecoder.decode(pair[1], "UTF-8"); - } - params.add(new String(key), new String(value)); - } - return params; - } - - @Override - public String getRealm() { - return null; - } - -} diff --git a/build.gradle b/build.gradle index 9d73a49..edb3243 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:3.4.2' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index dc63add..f4ac9b0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jan 28 10:39:02 PST 2019 +#Sun Aug 11 22:16:42 PDT 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip diff --git a/app/build.gradle b/library/build.gradle similarity index 87% rename from app/build.gradle rename to library/build.gradle index 9744b4b..471e149 100644 --- a/app/build.gradle +++ b/library/build.gradle @@ -7,8 +7,8 @@ apply plugin: 'com.jfrog.bintray' ext { GROUP = 'com.codepath.libraries' - BASE_VERSION = "1.4" - VERSION_NAME = "1.4.0" + BASE_VERSION = "2.0" + VERSION_NAME = "2.0.0" POM_PACKAGING = "aar" POM_DESCRIPTION = "CodePath OAuth Handler" @@ -37,7 +37,7 @@ android { defaultConfig { versionCode 1 versionName VERSION_NAME - minSdkVersion 14 + minSdkVersion 21 targetSdkVersion 28 } @@ -114,11 +114,16 @@ ext { } dependencies { - api "com.android.support:appcompat-v7:${supportVersion}" - api "com.android.support:support-v4:${supportVersion}" - api 'com.loopj.android:android-async-http:1.4.9' + api "androidx.appcompat:appcompat:1.0.2" + api 'com.codepath.libraries:asynchttpclient:0.0.5' implementation files('libs/codepath-utils.jar') api 'com.github.scribejava:scribejava-apis:4.1.1' + api 'com.github.scribejava:scribejava-httpclient-okhttp:4.1.1' + implementation 'se.akerfeldt:okhttp-signpost:1.1.0' + implementation 'oauth.signpost:signpost-core:1.2.1.2' + implementation 'com.facebook.stetho:stetho:1.5.1' + implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1' + } /*task jar(type: Jar) { diff --git a/app/libs/codepath-utils.jar b/library/libs/codepath-utils.jar similarity index 100% rename from app/libs/codepath-utils.jar rename to library/libs/codepath-utils.jar diff --git a/app/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml similarity index 100% rename from app/src/main/AndroidManifest.xml rename to library/src/main/AndroidManifest.xml diff --git a/library/src/main/java/com/codepath/oauth/OAuthAsyncHttpClient.java b/library/src/main/java/com/codepath/oauth/OAuthAsyncHttpClient.java new file mode 100644 index 0000000..8b345e7 --- /dev/null +++ b/library/src/main/java/com/codepath/oauth/OAuthAsyncHttpClient.java @@ -0,0 +1,26 @@ +package com.codepath.oauth; + +import com.codepath.asynchttpclient.AsyncHttpClient; +import com.facebook.stetho.okhttp3.StethoInterceptor; +import com.github.scribejava.core.model.OAuth1AccessToken; + +import okhttp3.OkHttpClient; +import se.akerfeldt.okhttp.signpost.OkHttpOAuthConsumer; +import se.akerfeldt.okhttp.signpost.SigningInterceptor; + +public class OAuthAsyncHttpClient extends AsyncHttpClient { + + protected OAuthAsyncHttpClient(OkHttpClient httpClient) { + super(httpClient); + } + + public static OAuthAsyncHttpClient create(String consumerKey, String consumerSecret, OAuth1AccessToken token) { + OkHttpOAuthConsumer consumer = new OkHttpOAuthConsumer(consumerKey, consumerSecret); + consumer.setTokenWithSecret(token.getToken(), token.getTokenSecret()); + OkHttpClient httpClient = new OkHttpClient.Builder().addNetworkInterceptor(new StethoInterceptor()).addInterceptor(new SigningInterceptor(consumer)).build(); + + OAuthAsyncHttpClient asyncHttpClient = new OAuthAsyncHttpClient(httpClient); + return asyncHttpClient; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/codepath/oauth/OAuthBaseClient.java b/library/src/main/java/com/codepath/oauth/OAuthBaseClient.java similarity index 81% rename from app/src/main/java/com/codepath/oauth/OAuthBaseClient.java rename to library/src/main/java/com/codepath/oauth/OAuthBaseClient.java index 1db4680..04ef5fc 100755 --- a/app/src/main/java/com/codepath/oauth/OAuthBaseClient.java +++ b/library/src/main/java/com/codepath/oauth/OAuthBaseClient.java @@ -17,6 +17,7 @@ public abstract class OAuthBaseClient { protected String baseUrl; protected Context context; + protected OAuthTokenClient tokenClient; protected OAuthAsyncHttpClient client; protected SharedPreferences prefs; protected SharedPreferences.Editor editor; @@ -45,11 +46,11 @@ public static OAuthBaseClient getInstance(Class klass return instance; } - public OAuthBaseClient(Context c, BaseApi apiInstance, String consumerUrl, String consumerKey, String consumerSecret, String callbackUrl) { + public OAuthBaseClient(Context c, final BaseApi apiInstance, String consumerUrl, final String consumerKey, final String consumerSecret, String callbackUrl) { this.baseUrl = consumerUrl; this.callbackUrl = callbackUrl; - client = new OAuthAsyncHttpClient(apiInstance, consumerKey, - consumerSecret, callbackUrl, new OAuthAsyncHttpClient.OAuthTokenHandler() { + tokenClient = new OAuthTokenClient(apiInstance, consumerKey, + consumerSecret, callbackUrl, new OAuthTokenClient.OAuthTokenHandler() { // Store request token and launch the authorization URL in the browser @Override @@ -70,21 +71,24 @@ public void onReceivedRequestToken(Token requestToken, String authorizeUrl, Stri OAuthBaseClient.this.context.startActivity(intent); } - // Store the access token in preferences, set the token in the client and fire the success callback + // Store the access token in preferences, set the token in the tokenClient and fire the success callback @Override public void onReceivedAccessToken(Token accessToken, String oAuthVersion) { if (oAuthVersion == OAUTH1_VERSION) { OAuth1AccessToken oAuth1AccessToken = (OAuth1AccessToken) accessToken; - client.setAccessToken(accessToken); + tokenClient.setAccessToken(accessToken); + instantiateClient(consumerKey, consumerSecret, oAuth1AccessToken); editor.putString(OAuthConstants.TOKEN, oAuth1AccessToken.getToken()); editor.putString(OAuthConstants.TOKEN_SECRET, oAuth1AccessToken.getTokenSecret()); editor.putInt(OAuthConstants.VERSION, 1); editor.commit(); } else if (oAuthVersion == OAUTH2_VERSION) { OAuth2AccessToken oAuth2AccessToken = (OAuth2AccessToken) accessToken; - client.setAccessToken(accessToken); + + //TODO(rhu) - create client for OAuth2 cases + tokenClient.setAccessToken(accessToken); editor.putString(OAuthConstants.TOKEN, oAuth2AccessToken.getAccessToken()); editor.putString(OAuthConstants.SCOPE, oAuth2AccessToken.getScope()); editor.putString(OAuthConstants.REFRESH_TOKEN, oAuth2AccessToken.getRefreshToken()); @@ -106,16 +110,27 @@ public void onFailure(Exception e) { // Store preferences namespaced by the class and consumer key used this.prefs = this.context.getSharedPreferences("OAuth_" + apiInstance.getClass().getSimpleName() + "_" + consumerKey, 0); this.editor = this.prefs.edit(); - // Set access token in the client if already stored in preferences - if (this.checkAccessToken() != null) { - client.setAccessToken(this.checkAccessToken()); + // Set access token in the tokenClient if already stored in preferences + Token accessToken = this.checkAccessToken(); + if (accessToken != null) { + tokenClient.setAccessToken(accessToken); + instantiateClient(consumerKey, consumerSecret, accessToken); } } + public void instantiateClient(String consumerKey, String consumerSecret, Token token) { + + if (token instanceof OAuth1AccessToken) { + client = OAuthAsyncHttpClient.create(consumerKey, consumerSecret, (OAuth1AccessToken)(token)); + } else { + + } + + } // Fetches a request token and retrieve and authorization url // Should open a browser in onReceivedRequestToken once the url has been received public void connect() { - client.fetchRequestToken(); + tokenClient.fetchRequestToken(); } // Retrieves access token given authorization url @@ -123,7 +138,7 @@ public void authorize(Uri uri, OAuthAccessHandler handler) { this.accessHandler = handler; if (checkAccessToken() == null && uri != null) { // TODO: check UriServiceCallback with intent:// scheme - client.fetchAccessToken(getOAuth1RequestToken(), uri); + tokenClient.fetchAccessToken(getOAuth1RequestToken(), uri); } else if (checkAccessToken() != null) { // already have access token this.accessHandler.onLoginSuccess(); @@ -143,8 +158,8 @@ public Token checkAccessToken() { return null; } - protected OAuthAsyncHttpClient getClient() { - return client; + protected OAuthTokenClient getTokenClient() { + return tokenClient; } // Returns the request token stored during the request token phase @@ -165,7 +180,7 @@ protected String getApiUrl(String path) { // Removes the access tokens (for signing out) public void clearAccessToken() { - client.setAccessToken(null); + tokenClient.setAccessToken(null); editor.remove(OAuthConstants.TOKEN); editor.remove(OAuthConstants.TOKEN_SECRET); editor.remove(OAuthConstants.REFRESH_TOKEN); @@ -173,9 +188,9 @@ public void clearAccessToken() { editor.commit(); } - // Returns true if the client is authenticated; false otherwise. + // Returns true if the tokenClient is authenticated; false otherwise. public boolean isAuthenticated() { - return client.getAccessToken() != null; + return tokenClient.getAccessToken() != null; } // Sets the flags used when launching browser to authenticate through OAuth @@ -189,12 +204,4 @@ public static interface OAuthAccessHandler { public void onLoginFailure(Exception e); } - - public void enableProxy() { - client.setProxy(System.getProperty("http.proxyHost"), Integer.parseInt(System.getProperty("http.proxyPort"))); - } - - public void addHeader(String headerName, String headerValue) { - client.addHeader(headerName, headerValue); - } } diff --git a/app/src/main/java/com/codepath/oauth/OAuthLoginActionBarActivity.java b/library/src/main/java/com/codepath/oauth/OAuthLoginActionBarActivity.java similarity index 97% rename from app/src/main/java/com/codepath/oauth/OAuthLoginActionBarActivity.java rename to library/src/main/java/com/codepath/oauth/OAuthLoginActionBarActivity.java index beea737..def658f 100755 --- a/app/src/main/java/com/codepath/oauth/OAuthLoginActionBarActivity.java +++ b/library/src/main/java/com/codepath/oauth/OAuthLoginActionBarActivity.java @@ -1,10 +1,12 @@ package com.codepath.oauth; -import com.codepath.utils.GenericsUtil; - import android.content.Intent; import android.net.Uri; -import android.support.v7.app.AppCompatActivity; + +import androidx.appcompat.app.AppCompatActivity; + +import com.codepath.utils.GenericsUtil; + // This is the ActionBarActivity supportv7 version of LoginActivity public abstract class OAuthLoginActionBarActivity extends diff --git a/app/src/main/java/com/codepath/oauth/OAuthLoginActivity.java b/library/src/main/java/com/codepath/oauth/OAuthLoginActivity.java similarity index 97% rename from app/src/main/java/com/codepath/oauth/OAuthLoginActivity.java rename to library/src/main/java/com/codepath/oauth/OAuthLoginActivity.java index 53d4a8a..64fa354 100755 --- a/app/src/main/java/com/codepath/oauth/OAuthLoginActivity.java +++ b/library/src/main/java/com/codepath/oauth/OAuthLoginActivity.java @@ -2,7 +2,8 @@ import android.content.Intent; import android.net.Uri; -import android.support.v4.app.FragmentActivity; + +import androidx.fragment.app.FragmentActivity; import com.codepath.utils.GenericsUtil; diff --git a/app/src/main/java/com/codepath/oauth/OAuthLoginFragment.java b/library/src/main/java/com/codepath/oauth/OAuthLoginFragment.java similarity index 96% rename from app/src/main/java/com/codepath/oauth/OAuthLoginFragment.java rename to library/src/main/java/com/codepath/oauth/OAuthLoginFragment.java index 10645d2..9fe5c72 100755 --- a/app/src/main/java/com/codepath/oauth/OAuthLoginFragment.java +++ b/library/src/main/java/com/codepath/oauth/OAuthLoginFragment.java @@ -2,7 +2,8 @@ import android.net.Uri; import android.os.Bundle; -import android.support.v4.app.Fragment; + +import androidx.fragment.app.Fragment; import com.codepath.utils.GenericsUtil; diff --git a/library/src/main/java/com/codepath/oauth/OAuthTokenClient.java b/library/src/main/java/com/codepath/oauth/OAuthTokenClient.java new file mode 100755 index 0000000..b92c578 --- /dev/null +++ b/library/src/main/java/com/codepath/oauth/OAuthTokenClient.java @@ -0,0 +1,149 @@ +package com.codepath.oauth; + +import android.net.Uri; + +import com.github.scribejava.core.builder.ServiceBuilder; +import com.github.scribejava.core.builder.api.BaseApi; +import com.github.scribejava.core.exceptions.OAuthException; +import com.github.scribejava.core.model.OAuth1AccessToken; +import com.github.scribejava.core.model.OAuth1RequestToken; +import com.github.scribejava.core.model.OAuth2AccessToken; +import com.github.scribejava.core.model.OAuthAsyncRequestCallback; +import com.github.scribejava.core.model.OAuthConstants; +import com.github.scribejava.core.model.Token; +import com.github.scribejava.core.oauth.OAuth10aService; +import com.github.scribejava.core.oauth.OAuth20Service; +import com.github.scribejava.core.oauth.OAuthService; +import com.github.scribejava.httpclient.okhttp.OkHttpHttpClientConfig; + +/* + * OAuthTokenClient is responsible for managing the request and access token exchanges and then + * signing all requests with the OAuth signature after access token has been retrieved and stored. + * The client is based on AsyncHttpClient for async http requests and uses Scribe to manage the OAuth authentication. + */ +public class OAuthTokenClient { + + private BaseApi apiInstance; + private OAuthTokenHandler handler; + private Token accessToken; + private OAuthService service; + + // Requires the apiClass, consumerKey, consumerSecret and callbackUrl along with the TokenHandler + public OAuthTokenClient(BaseApi apiInstance, String consumerKey, String consumerSecret, String callbackUrl, + OAuthTokenHandler handler) { + this.apiInstance = apiInstance; + this.handler = handler; + if (callbackUrl == null) { callbackUrl = OAuthConstants.OUT_OF_BAND; }; + this.service = new ServiceBuilder() + .apiKey(consumerKey) + .apiSecret(consumerSecret).callback(callbackUrl) + .httpClientConfig(OkHttpHttpClientConfig.defaultConfig()) + .build(apiInstance); + } + + // Get a request token and the authorization url + // Once fetched, fire the onReceivedRequestToken for the request token handler + // Works for both OAuth1.0a and OAuth2 + public void fetchRequestToken() { + if (service.getVersion() == "1.0") { + final OAuth10aService oAuth10aService = (OAuth10aService) service; + oAuth10aService.getRequestTokenAsync(new OAuthAsyncRequestCallback() { + @Override + public void onCompleted(OAuth1RequestToken requestToken) { + String authorizeUrl = oAuth10aService.getAuthorizationUrl((OAuth1RequestToken) requestToken); + handler.onReceivedRequestToken(requestToken, authorizeUrl, service.getVersion()); + + } + + @Override + public void onThrowable(Throwable t) { + handler.onFailure(new Exception(t.getMessage())); + } + }); + } + if (service.getVersion() == "2.0") { + OAuth20Service oAuth20Service = (OAuth20Service) service; + String authorizeUrl = oAuth20Service.getAuthorizationUrl(null); + handler.onReceivedRequestToken(null, authorizeUrl, service.getVersion()); + } + } + + // Get the access token by exchanging the requestToken to the defined URL + // Once receiving the access token, fires the onReceivedAccessToken method on the handler + public void fetchAccessToken(final Token requestToken, final Uri uri) { + + Uri authorizedUri = uri; + + if (service.getVersion() == "1.0") { + // Use verifier token to fetch access token + + if (authorizedUri.getQuery().contains(OAuthConstants.VERIFIER)) { + String oauth_verifier = authorizedUri.getQueryParameter(OAuthConstants.VERIFIER); + OAuth1RequestToken oAuth1RequestToken = (OAuth1RequestToken) requestToken; + OAuth10aService oAuth10aService = (OAuth10aService) service; + + oAuth10aService.getAccessTokenAsync(oAuth1RequestToken, oauth_verifier, + new OAuthAsyncRequestCallback() { + + @Override + public void onCompleted(OAuth1AccessToken oAuth1AccessToken) { + setAccessToken(oAuth1AccessToken); + handler.onReceivedAccessToken(oAuth1AccessToken, service.getVersion()); + } + + @Override + public void onThrowable(Throwable e) { + handler.onFailure(new OAuthException(e.getMessage())); + } + }); + + } + else { // verifier was null + throw new OAuthException("No verifier code was returned with uri '" + uri + "' " + + "and access token cannot be retrieved"); + } + } else if (service.getVersion() == "2.0") { + if (authorizedUri.getQuery().contains(OAuthConstants.CODE)) { + String code = authorizedUri.getQueryParameter(OAuthConstants.CODE); + OAuth20Service oAuth20Service = (OAuth20Service) service; + oAuth20Service.getAccessToken(code, new OAuthAsyncRequestCallback() { + @Override + public void onCompleted(OAuth2AccessToken accessToken) { + setAccessToken(accessToken); + handler.onReceivedAccessToken(accessToken, service.getVersion()); + + } + + @Override + public void onThrowable(Throwable t) { + + } + }); + } + else { // verifier was null + handler.onFailure(new OAuthException("No code was returned with uri '" + uri + "' " + + "and access token cannot be retrieved")); + } + } + } + + // Set the access token used for signing requests + public void setAccessToken(Token accessToken) { + if (accessToken == null) { + this.accessToken = null; + } else { + this.accessToken = accessToken; + } + } + + public Token getAccessToken() { + return this.accessToken; + } + + // Defines the interface handler for different token handlers + public interface OAuthTokenHandler { + public void onReceivedRequestToken(Token requestToken, String authorizeUrl, String oAuthVersion); + public void onReceivedAccessToken(Token accessToken, String oAuthVersion); + public void onFailure(Exception e); + } +} \ No newline at end of file diff --git a/app/src/main/res/.gitkeep b/library/src/main/res/.gitkeep similarity index 100% rename from app/src/main/res/.gitkeep rename to library/src/main/res/.gitkeep diff --git a/app/src/main/res/layout/.gitkeep b/library/src/main/res/layout/.gitkeep similarity index 100% rename from app/src/main/res/layout/.gitkeep rename to library/src/main/res/layout/.gitkeep diff --git a/settings.gradle b/settings.gradle index e7b4def..d8f14a1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app' +include ':library'