Skip to content

Commit bcc941a

Browse files
authored
Merge pull request #6 from anotherdev/pk/feature-account-linking
Feature account linking
2 parents b43e07b + 14749c4 commit bcc941a

File tree

9 files changed

+130
-24
lines changed

9 files changed

+130
-24
lines changed

buildsystem/dependencies.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ ext {
22
// Manifest version information!
33
def versionMajor = 0
44
def versionMinor = 1
5-
def versionPatch = 4
5+
def versionPatch = 5
66
def versionBuild = 0 // bump for dogfood builds, public betas, etc.
77

88
// Eko SDK version

firebase-auth-rest/core/src/main/java/com/anotherdev/firebase/auth/AuthError.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@ public class AuthError {
3333
AuthError() {
3434
}
3535

36-
AuthError(int code, String message) {
36+
AuthError(int code, @Nullable Throwable cause) {
3737
this.code = code;
38-
this.message = message;
38+
if (cause != null) {
39+
this.cause = cause.getCause();
40+
this.message = cause.getMessage();
41+
}
3942
}
4043

4144
public int getCode() {
@@ -96,7 +99,7 @@ public static AuthError fromThrowable(@Nullable Throwable t) {
9699
}
97100
cause = cause.getCause();
98101
}
99-
return t != null ? new AuthError(ERROR_CLIENT, t.toString()) : UNKNOWN;
102+
return new AuthError(ERROR_CLIENT, t);
100103
}
101104

102105
private static boolean isClientSideNetworkError(@NonNull Throwable t) {

firebase-auth-rest/core/src/main/java/com/anotherdev/firebase/auth/FirebaseAuth.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import androidx.annotation.NonNull;
44
import androidx.annotation.Nullable;
55

6+
import com.anotherdev.firebase.auth.provider.EmailAuthCredential;
67
import com.anotherdev.firebase.auth.provider.IdpAuthCredential;
78
import com.anotherdev.firebase.auth.rest.api.model.SendPasswordResetEmailRequest;
89
import com.anotherdev.firebase.auth.rest.api.model.SendPasswordResetEmailResponse;
@@ -43,6 +44,10 @@ public interface FirebaseAuth {
4344
@CheckReturnValue
4445
Single<SignInResponse> signInWithEmailAndPassword(@NonNull String email, @NonNull String password);
4546

47+
@NonNull
48+
@CheckReturnValue
49+
Single<SignInResponse> signInWithEmailAndPassword(@NonNull EmailAuthCredential credential);
50+
4651
@NonNull
4752
@CheckReturnValue
4853
Single<SignInResponse> signInWithCredential(@NonNull IdpAuthCredential credential);
@@ -51,6 +56,10 @@ public interface FirebaseAuth {
5156
@CheckReturnValue
5257
Single<SignInResponse> signInWithCustomToken(@NonNull String customToken);
5358

59+
@NonNull
60+
@CheckReturnValue
61+
Single<SignInResponse> linkWithCredential(@NonNull FirebaseUser user, @NonNull EmailAuthCredential credential);
62+
5463
@NonNull
5564
@CheckReturnValue
5665
Single<SignInResponse> linkWithCredential(@NonNull FirebaseUser user, @NonNull IdpAuthCredential credential);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.anotherdev.firebase.auth.internal;
2+
3+
import androidx.annotation.NonNull;
4+
import androidx.annotation.Nullable;
5+
6+
import com.anotherdev.firebase.auth.util.Strings;
7+
import com.google.gson.JsonObject;
8+
9+
import okhttp3.MediaType;
10+
import okhttp3.Protocol;
11+
import okhttp3.Request;
12+
import okhttp3.ResponseBody;
13+
import retrofit2.HttpException;
14+
import retrofit2.Response;
15+
16+
public class CustomFirebaseExceptions {
17+
18+
@NonNull
19+
public static HttpException createCustom(int code, @Nullable String message) {
20+
JsonObject error = createFirebaseExceptionJson(code, message);
21+
MediaType mediaType = MediaType.parse("application/json; charset=UTF-8");
22+
okhttp3.Response rawResponse = new okhttp3.Response.Builder()
23+
.code(code)
24+
.message(Strings.nullToEmpty(message))
25+
.protocol(Protocol.QUIC)
26+
.request(new Request.Builder().url("http://localhost/").build())
27+
.build();
28+
ResponseBody errorBody = ResponseBody.create(error.toString(), mediaType);
29+
Response<?> errorResponse = Response.error(errorBody, rawResponse);
30+
return new HttpException(errorResponse);
31+
}
32+
33+
@NonNull
34+
private static JsonObject createFirebaseExceptionJson(int code, @Nullable String message) {
35+
JsonObject error = new JsonObject();
36+
error.addProperty("code", code);
37+
error.addProperty("message", message);
38+
JsonObject json = new JsonObject();
39+
json.add("error", error);
40+
return json;
41+
}
42+
}

firebase-auth-rest/core/src/main/java/com/anotherdev/firebase/auth/provider/IdpAuthCredential.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public interface IdpAuthCredential extends AuthCredential {
1212
@NonNull
1313
String getPostBody();
1414

15+
@Deprecated
1516
default boolean returnIdpCredential() {
1617
return true;
1718
}

firebase-auth-rest/core/src/main/java/com/anotherdev/firebase/auth/rest/RestAuthProvider.java

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@
1111
import com.anotherdev.firebase.auth.data.Data;
1212
import com.anotherdev.firebase.auth.data.model.FirebaseUserImpl;
1313
import com.anotherdev.firebase.auth.data.model.UserProfile;
14+
import com.anotherdev.firebase.auth.internal.CustomFirebaseExceptions;
15+
import com.anotherdev.firebase.auth.provider.EmailAuthCredential;
16+
import com.anotherdev.firebase.auth.provider.EmailAuthProvider;
1417
import com.anotherdev.firebase.auth.provider.IdpAuthCredential;
1518
import com.anotherdev.firebase.auth.rest.api.RestAuthApi;
1619
import com.anotherdev.firebase.auth.rest.api.model.ExchangeTokenRequest;
1720
import com.anotherdev.firebase.auth.rest.api.model.GetAccountInfoRequest;
1821
import com.anotherdev.firebase.auth.rest.api.model.GetAccountInfoResponse;
22+
import com.anotherdev.firebase.auth.rest.api.model.ImmutableSignInWithEmailPasswordRequest;
1923
import com.anotherdev.firebase.auth.rest.api.model.ImmutableSignInWithIdpRequest;
2024
import com.anotherdev.firebase.auth.rest.api.model.SendPasswordResetEmailRequest;
2125
import com.anotherdev.firebase.auth.rest.api.model.SendPasswordResetEmailResponse;
@@ -25,6 +29,7 @@
2529
import com.anotherdev.firebase.auth.rest.api.model.SignInWithIdpRequest;
2630
import com.anotherdev.firebase.auth.util.IdTokenParser;
2731
import com.anotherdev.firebase.auth.util.RxUtil;
32+
import com.anotherdev.firebase.auth.util.Strings;
2833
import com.f2prateek.rx.preferences2.Preference;
2934
import com.google.android.gms.tasks.Task;
3035
import com.google.android.gms.tasks.TaskCompletionSource;
@@ -116,14 +121,15 @@ public Single<SignInResponse> createUserWithEmailAndPassword(@NonNull String ema
116121
@NonNull
117122
@Override
118123
public Single<SignInResponse> signInWithEmailAndPassword(@NonNull String email, @NonNull String password) {
119-
SignInWithEmailPasswordRequest request = SignInWithEmailPasswordRequest.builder()
120-
.email(email)
121-
.password(password)
122-
.build();
123-
return RestAuthApi.auth()
124-
.signInWithEmailAndPassword(request)
125-
.map(this::saveCurrentUser)
126-
.map(this::getAccountInfo);
124+
EmailAuthCredential credential = EmailAuthProvider.getCredential(email, password);
125+
return signInWithEmailAndPassword(credential);
126+
}
127+
128+
@NonNull
129+
@Override
130+
public Single<SignInResponse> signInWithEmailAndPassword(@NonNull EmailAuthCredential credential) {
131+
ImmutableSignInWithEmailPasswordRequest.Builder builder = SignInWithEmailPasswordRequest.builder();
132+
return performSignInWithEmailAndPassword(builder, credential);
127133
}
128134

129135
@NonNull
@@ -145,15 +151,35 @@ public Single<SignInResponse> signInWithCustomToken(@NonNull String customToken)
145151
.map(this::getAccountInfo);
146152
}
147153

154+
@NonNull
155+
@Override
156+
public Single<SignInResponse> linkWithCredential(@NonNull FirebaseUser user, @NonNull EmailAuthCredential credential) {
157+
ImmutableSignInWithEmailPasswordRequest.Builder builder = SignInWithEmailPasswordRequest.builder()
158+
.idToken(user.getIdToken());
159+
return performSignInWithEmailAndPassword(builder, credential);
160+
}
161+
162+
private Single<SignInResponse> performSignInWithEmailAndPassword(@NonNull ImmutableSignInWithEmailPasswordRequest.Builder builder,
163+
@NonNull EmailAuthCredential credential) {
164+
SignInWithEmailPasswordRequest request = builder
165+
.email(credential.getEmail())
166+
.password(credential.getPassword())
167+
.build();
168+
return RestAuthApi.auth()
169+
.signInWithEmailAndPassword(request)
170+
.map(this::saveCurrentUser)
171+
.map(this::getAccountInfo);
172+
}
173+
148174
@NonNull
149175
@Override
150176
public Single<SignInResponse> linkWithCredential(@NonNull FirebaseUser user, @NonNull IdpAuthCredential credential) {
151-
String idToken = user.getIdToken();
152-
ImmutableSignInWithIdpRequest.Builder builder = SignInWithIdpRequest.builder().idToken(idToken);
177+
ImmutableSignInWithIdpRequest.Builder builder = SignInWithIdpRequest.builder()
178+
.idToken(user.getIdToken());
153179
return performSignInWithCredential(builder, credential);
154180
}
155181

156-
private Single<SignInResponse> performSignInWithCredential(ImmutableSignInWithIdpRequest.Builder builder,
182+
private Single<SignInResponse> performSignInWithCredential(@NonNull ImmutableSignInWithIdpRequest.Builder builder,
157183
@NonNull IdpAuthCredential credential ) {
158184
SignInWithIdpRequest request = builder
159185
.requestUri(credential.getRequestUri(this))
@@ -162,7 +188,23 @@ private Single<SignInResponse> performSignInWithCredential(ImmutableSignInWithId
162188
return RestAuthApi.auth()
163189
.signInWithCredential(request)
164190
.map(this::saveCurrentUser)
165-
.map(this::getAccountInfo);
191+
.map(this::getAccountInfo)
192+
.onErrorResumeNext(e -> {
193+
final String originalMessage = Strings.nullToEmpty(e.getMessage());
194+
if (e instanceof IllegalStateException
195+
&& originalMessage.contains("required attributes are not set")
196+
&& originalMessage.contains("refreshToken")) {
197+
Timber.i(e, "Firebase return 200 but without refresh token");
198+
String baseErrorMessage = String.format("Sign in with %s failed.", credential.getProvider());
199+
StringBuilder error = new StringBuilder(baseErrorMessage);
200+
if (!isSignedIn()) {
201+
error.append(" Maybe the account is not properly linked.");
202+
}
203+
return Single.error(CustomFirebaseExceptions.createCustom(400, error.toString()));
204+
} else {
205+
return Single.error(e);
206+
}
207+
});
166208
}
167209

168210
@NonNull

firebase-auth-rest/core/src/main/java/com/anotherdev/firebase/auth/rest/api/model/SignInWithEmailPasswordRequest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
@Value.Immutable
99
@Value.Style(strictBuilder = true)
1010
@Gson.TypeAdapters
11-
public interface SignInWithEmailPasswordRequest extends SignInRequest {
11+
public interface SignInWithEmailPasswordRequest extends SignInRequest, OptionalIdTokenRequest {
1212

1313
@SerializedName("email")
1414
String getEmail();

firebase-auth-rest/core/src/main/java/com/anotherdev/firebase/auth/rest/api/model/SignInWithIdpRequest.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.anotherdev.firebase.auth.rest.api.model;
22

3-
import androidx.annotation.Nullable;
4-
53
import com.google.gson.annotations.SerializedName;
64

75
import org.immutables.gson.Gson;
@@ -10,11 +8,7 @@
108
@Value.Immutable
119
@Value.Style(strictBuilder = true)
1210
@Gson.TypeAdapters
13-
public interface SignInWithIdpRequest extends SignInRequest, IdTokenRequest {
14-
15-
@Nullable
16-
@Override
17-
String getIdToken();
11+
public interface SignInWithIdpRequest extends SignInRequest, OptionalIdTokenRequest {
1812

1913
@SerializedName("requestUri")
2014
String getRequestUri();
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.anotherdev.firebase.auth.util;
2+
3+
import androidx.annotation.NonNull;
4+
import androidx.annotation.Nullable;
5+
6+
public final class Strings {
7+
8+
private Strings() {}
9+
10+
11+
@NonNull
12+
public static String nullToEmpty(@Nullable String string) {
13+
return (string == null) ? "" : string;
14+
}
15+
}

0 commit comments

Comments
 (0)