-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Request Twitter email #452
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
c40fa42
d81b5f9
7af8e9b
4dec62e
7d7c582
ed4b11b
bff53a5
24a2628
086e5dd
639b9d1
f537d1b
6695453
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
import android.content.Intent; | ||
import android.os.Parcel; | ||
import android.os.Parcelable; | ||
import android.support.annotation.NonNull; | ||
import android.support.annotation.Nullable; | ||
|
||
import com.firebase.ui.auth.ui.ExtraConstants; | ||
|
@@ -25,39 +26,37 @@ | |
* A container that encapsulates the result of authenticating with an Identity Provider. | ||
*/ | ||
public class IdpResponse implements Parcelable { | ||
|
||
private final String mProviderId; | ||
@Nullable | ||
private final String mEmail; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this should be nullable: we always know the email address and provider id and since having it be nullable caused a crash, I made it @nonnull. |
||
private final String mToken; | ||
private final String mSecret; | ||
private final int mErrorCode; | ||
|
||
public IdpResponse(int errorCode) { | ||
private IdpResponse(int errorCode) { | ||
this(null, null, null, null, errorCode); | ||
} | ||
|
||
public IdpResponse(String providerId, String email) { | ||
this(providerId, email, null, null); | ||
public IdpResponse(@NonNull String providerId, @NonNull String email) { | ||
this(providerId, email, null, null, ResultCodes.OK); | ||
} | ||
|
||
public IdpResponse(String providerId, @Nullable String email, @Nullable String token) { | ||
this(providerId, email, token, null); | ||
public IdpResponse(@NonNull String providerId, @NonNull String email, @NonNull String token) { | ||
this(providerId, email, token, null, ResultCodes.OK); | ||
} | ||
|
||
public IdpResponse( | ||
String providerId, | ||
@Nullable String email, | ||
@Nullable String token, | ||
@Nullable String secret) { | ||
@NonNull String providerId, | ||
@NonNull String email, | ||
@NonNull String token, | ||
@NonNull String secret) { | ||
this(providerId, email, token, secret, ResultCodes.OK); | ||
} | ||
|
||
public IdpResponse( | ||
private IdpResponse( | ||
String providerId, | ||
@Nullable String email, | ||
@Nullable String token, | ||
@Nullable String secret, | ||
String email, | ||
String token, | ||
String secret, | ||
int errorCode) { | ||
mProviderId = providerId; | ||
mEmail = email; | ||
|
@@ -87,11 +86,17 @@ public IdpResponse[] newArray(int size) { | |
/** | ||
* Get the type of provider. e.g. {@link AuthUI#GOOGLE_PROVIDER} | ||
*/ | ||
@Nullable | ||
public String getProviderType() { | ||
return mProviderId; | ||
} | ||
|
||
/** | ||
* Get the email used to sign in. | ||
*/ | ||
public String getEmail() { | ||
return mEmail; | ||
} | ||
|
||
/** | ||
* Get the token received as a result of logging in with the specified IDP | ||
*/ | ||
|
@@ -108,14 +113,6 @@ public String getIdpSecret() { | |
return mSecret; | ||
} | ||
|
||
/** | ||
* Get the email used to sign in. | ||
*/ | ||
@Nullable | ||
public String getEmail() { | ||
return mEmail; | ||
} | ||
|
||
/** | ||
* Get the error code for a failed sign in | ||
*/ | ||
|
@@ -157,6 +154,6 @@ public static Intent getIntent(IdpResponse response) { | |
} | ||
|
||
public static Intent getErrorCodeIntent(int errorCode) { | ||
return new Intent().putExtra(ExtraConstants.EXTRA_IDP_RESPONSE, new IdpResponse(errorCode)); | ||
return getIntent(new IdpResponse(errorCode)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,15 +10,29 @@ | |
import com.firebase.ui.auth.R; | ||
import com.google.firebase.auth.AuthCredential; | ||
import com.google.firebase.auth.TwitterAuthProvider; | ||
import com.google.gson.GsonBuilder; | ||
import com.twitter.sdk.android.Twitter; | ||
import com.twitter.sdk.android.core.Callback; | ||
import com.twitter.sdk.android.core.Result; | ||
import com.twitter.sdk.android.core.TwitterAuthConfig; | ||
import com.twitter.sdk.android.core.TwitterCore; | ||
import com.twitter.sdk.android.core.TwitterException; | ||
import com.twitter.sdk.android.core.TwitterSession; | ||
import com.twitter.sdk.android.core.identity.TwitterAuthClient; | ||
import com.twitter.sdk.android.core.internal.TwitterApi; | ||
import com.twitter.sdk.android.core.internal.network.OkHttpClientHelper; | ||
import com.twitter.sdk.android.core.models.BindingValues; | ||
import com.twitter.sdk.android.core.models.BindingValuesAdapter; | ||
import com.twitter.sdk.android.core.models.SafeListAdapter; | ||
import com.twitter.sdk.android.core.models.SafeMapAdapter; | ||
import com.twitter.sdk.android.core.models.User; | ||
|
||
import io.fabric.sdk.android.Fabric; | ||
import okhttp3.OkHttpClient; | ||
import retrofit2.Call; | ||
import retrofit2.Retrofit; | ||
import retrofit2.converter.gson.GsonConverterFactory; | ||
import retrofit2.http.GET; | ||
|
||
public class TwitterProvider extends Callback<TwitterSession> implements IdpProvider { | ||
private static final String TAG = "TwitterProvider"; | ||
|
@@ -46,7 +60,7 @@ public String getProviderId() { | |
|
||
@Override | ||
public void setAuthenticationCallback(IdpCallback callback) { | ||
this.mCallbackObject = callback; | ||
mCallbackObject = callback; | ||
} | ||
|
||
@Override | ||
|
@@ -60,8 +74,36 @@ public void startLogin(Activity activity) { | |
} | ||
|
||
@Override | ||
public void success(Result<TwitterSession> result) { | ||
mCallbackObject.onSuccess(createIdpResponse(result.data)); | ||
public void success(final Result<TwitterSession> sessionResult) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Twitter caused a leak by storing our callback in a result receiver:
and their Anyway, I dug through the source code and ripped Twitter's GET request so that we can handle that stuff ourselves. Now if we didn't get an email, we know whether or not the dev hasn't requested the email permission in the twitter dashboard or if the Twitter user just doesn't have an email. @samtstern Since we now have control over what's going on, we can decide what to do in the two failure cases. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @SUPERCILEX I appreciate all the investigation but I have a few reservations about this approach:
As for the leak, I think we can work around that ourselves by making the callback a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @samtstern Whoa, you just blew my mind! The main reason I didn't want to use |
||
new Retrofit.Builder() | ||
.client(getClient(sessionResult)) | ||
.baseUrl(new TwitterApi().getBaseHostUrl()) | ||
.addConverterFactory(getFactory()) | ||
.build() | ||
.create(EmailService.class) | ||
.getEmail() | ||
.enqueue(new Callback<User>() { | ||
@Override | ||
public void success(Result<User> result) { | ||
String email = result.data.email; | ||
if (email == null) { | ||
TwitterProvider.this.failure( | ||
new TwitterException("Your application may not have access to" | ||
+ " email addresses or the user may not have an email address. To request" | ||
+ " access, please visit https://support.twitter.com/forms/platform.")); | ||
} else if (email.equals("")) { | ||
TwitterProvider.this.failure( | ||
new TwitterException("This user does not have an email address.")); | ||
} else { | ||
mCallbackObject.onSuccess(createIdpResponse(sessionResult.data, email)); | ||
} | ||
} | ||
|
||
@Override | ||
public void failure(TwitterException exception) { | ||
TwitterProvider.this.failure(exception); | ||
} | ||
}); | ||
} | ||
|
||
@Override | ||
|
@@ -70,21 +112,39 @@ public void failure(TwitterException exception) { | |
mCallbackObject.onFailure(new Bundle()); | ||
} | ||
|
||
private IdpResponse createIdpResponse(TwitterSession twitterSession) { | ||
private IdpResponse createIdpResponse(TwitterSession twitterSession, String email) { | ||
return new IdpResponse( | ||
TwitterAuthProvider.PROVIDER_ID, | ||
null, | ||
email, | ||
twitterSession.getAuthToken().token, | ||
twitterSession.getAuthToken().secret); | ||
} | ||
|
||
private OkHttpClient getClient(Result<TwitterSession> sessionResult) { | ||
return OkHttpClientHelper.getOkHttpClient( | ||
sessionResult.data, | ||
TwitterCore.getInstance().getAuthConfig(), | ||
TwitterCore.getInstance().getSSLSocketFactory()); | ||
} | ||
|
||
private GsonConverterFactory getFactory() { | ||
return GsonConverterFactory.create( | ||
new GsonBuilder() | ||
.registerTypeAdapterFactory(new SafeListAdapter()) | ||
.registerTypeAdapterFactory(new SafeMapAdapter()) | ||
.registerTypeAdapter(BindingValues.class, new BindingValuesAdapter()) | ||
.create()); | ||
} | ||
|
||
interface EmailService { | ||
@GET("/1.1/account/verify_credentials.json?include_email=true?include_entities=true?skip_status=true") | ||
Call<User> getEmail(); | ||
} | ||
|
||
public static AuthCredential createAuthCredential(IdpResponse response) { | ||
if (!response.getProviderType().equalsIgnoreCase(TwitterAuthProvider.PROVIDER_ID)){ | ||
if (!response.getProviderType().equalsIgnoreCase(TwitterAuthProvider.PROVIDER_ID)) { | ||
return null; | ||
} | ||
return TwitterAuthProvider.getCredential( | ||
response.getIdpToken(), | ||
response.getIdpSecret()); | ||
return TwitterAuthProvider.getCredential(response.getIdpToken(), response.getIdpSecret()); | ||
} | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries about these, Twitter's already includes them and I'm just upgrading it for them: