|
64 | 64 | import com.google.firebase.auth.PhoneAuthOptions; |
65 | 65 | import com.google.firebase.auth.PhoneAuthProvider; |
66 | 66 | import com.google.firebase.auth.PhoneMultiFactorAssertion; |
| 67 | +import com.google.firebase.auth.TotpMultiFactorAssertion; |
| 68 | +import com.google.firebase.auth.TotpMultiFactorGenerator; |
67 | 69 | import com.google.firebase.auth.PhoneMultiFactorGenerator; |
68 | 70 | import com.google.firebase.auth.PhoneMultiFactorInfo; |
| 71 | +import com.google.firebase.auth.TotpSecret; |
69 | 72 | import com.google.firebase.auth.TwitterAuthProvider; |
70 | 73 | import com.google.firebase.auth.UserInfo; |
71 | 74 | import com.google.firebase.auth.UserProfileChangeRequest; |
@@ -107,6 +110,7 @@ class ReactNativeFirebaseAuthModule extends ReactNativeFirebaseModule { |
107 | 110 |
|
108 | 111 | private final HashMap<String, MultiFactorResolver> mCachedResolvers = new HashMap<>(); |
109 | 112 | private final HashMap<String, MultiFactorSession> mMultiFactorSessions = new HashMap<>(); |
| 113 | + private final HashMap<String, TotpSecret> mTotpSecrets = new HashMap<>(); |
110 | 114 |
|
111 | 115 | // storage for anonymous phone auth credentials, used for linkWithCredentials |
112 | 116 | // https://github.com/invertase/react-native-firebase/issues/4911 |
@@ -154,6 +158,7 @@ public void invalidate() { |
154 | 158 |
|
155 | 159 | mCachedResolvers.clear(); |
156 | 160 | mMultiFactorSessions.clear(); |
| 161 | + mTotpSecrets.clear(); |
157 | 162 | } |
158 | 163 |
|
159 | 164 | @ReactMethod |
@@ -1130,6 +1135,29 @@ public void getSession(final String appName, final Promise promise) { |
1130 | 1135 | }); |
1131 | 1136 | } |
1132 | 1137 |
|
| 1138 | + @ReactMethod |
| 1139 | + public void unenrollMultiFactor( |
| 1140 | + final String appName, |
| 1141 | + final String factorUID, |
| 1142 | + final Promise promise) { |
| 1143 | + FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); |
| 1144 | + FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); |
| 1145 | + firebaseAuth |
| 1146 | + .getCurrentUser() |
| 1147 | + .getMultiFactor() |
| 1148 | + .unenroll(factorUID) |
| 1149 | + .addOnCompleteListener( |
| 1150 | + task -> { |
| 1151 | + if (!task.isSuccessful()) { |
| 1152 | + rejectPromiseWithExceptionMap(promise, task.getException()); |
| 1153 | + return; |
| 1154 | + } |
| 1155 | + |
| 1156 | + promise.resolve(null); |
| 1157 | + }); |
| 1158 | + |
| 1159 | + } |
| 1160 | + |
1133 | 1161 | @ReactMethod |
1134 | 1162 | public void verifyPhoneNumberWithMultiFactorInfo( |
1135 | 1163 | final String appName, final String hintUid, final String sessionKey, final Promise promise) { |
@@ -1280,6 +1308,42 @@ public void finalizeMultiFactorEnrollment( |
1280 | 1308 | }); |
1281 | 1309 | } |
1282 | 1310 |
|
| 1311 | + @ReactMethod |
| 1312 | + public void finalizeTotpEnrollment( |
| 1313 | + final String appName, |
| 1314 | + final String totpSecret, |
| 1315 | + final String verificationCode, |
| 1316 | + @Nullable final String displayName, |
| 1317 | + final Promise promise) { |
| 1318 | + |
| 1319 | + TotpSecret secret = mTotpSecrets.get(totpSecret); |
| 1320 | + if (secret == null) { |
| 1321 | + rejectPromiseWithCodeAndMessage( |
| 1322 | + promise, "invalid-multi-factor-secret", "can't find secret for provided key"); |
| 1323 | + return; |
| 1324 | + } |
| 1325 | + |
| 1326 | + TotpMultiFactorAssertion assertion = |
| 1327 | + TotpMultiFactorGenerator.getAssertionForEnrollment(secret, verificationCode); |
| 1328 | + |
| 1329 | + FirebaseApp firebaseApp = FirebaseApp.getInstance(appName); |
| 1330 | + FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp); |
| 1331 | + |
| 1332 | + firebaseAuth |
| 1333 | + .getCurrentUser() |
| 1334 | + .getMultiFactor() |
| 1335 | + .enroll(assertion, displayName) |
| 1336 | + .addOnCompleteListener( |
| 1337 | + task -> { |
| 1338 | + if (!task.isSuccessful()) { |
| 1339 | + rejectPromiseWithExceptionMap(promise, task.getException()); |
| 1340 | + return; |
| 1341 | + } |
| 1342 | + |
| 1343 | + promise.resolve(null); |
| 1344 | + }); |
| 1345 | + } |
| 1346 | + |
1283 | 1347 | /** |
1284 | 1348 | * This method is intended to resolve a {@link PhoneAuthCredential} obtained through a |
1285 | 1349 | * multi-factor authentication flow. A credential can either be obtained using: |
@@ -1335,6 +1399,75 @@ public void resolveMultiFactorSignIn( |
1335 | 1399 | resolveMultiFactorCredential(credential, session, promise); |
1336 | 1400 | } |
1337 | 1401 |
|
| 1402 | + @ReactMethod |
| 1403 | + public void resolveTotpSignIn( |
| 1404 | + final String appName, |
| 1405 | + final String sessionKey, |
| 1406 | + final String uid, |
| 1407 | + final String oneTimePassword, |
| 1408 | + final Promise promise) { |
| 1409 | + |
| 1410 | + final MultiFactorAssertion assertion = |
| 1411 | + TotpMultiFactorGenerator.getAssertionForSignIn(uid, oneTimePassword); |
| 1412 | + |
| 1413 | + final MultiFactorResolver resolver = mCachedResolvers.get(sessionKey); |
| 1414 | + if (resolver == null) { |
| 1415 | + // See https://firebase.google.com/docs/reference/node/firebase.auth.multifactorresolver for |
| 1416 | + // the error code |
| 1417 | + rejectPromiseWithCodeAndMessage( |
| 1418 | + promise, |
| 1419 | + "invalid-multi-factor-session", |
| 1420 | + "No resolver for session found. Is the session id correct?"); |
| 1421 | + return; |
| 1422 | + } |
| 1423 | + |
| 1424 | + |
| 1425 | + resolver |
| 1426 | + .resolveSignIn(assertion) |
| 1427 | + .addOnCompleteListener( |
| 1428 | + task -> { |
| 1429 | + if (task.isSuccessful()) { |
| 1430 | + AuthResult authResult = task.getResult(); |
| 1431 | + promiseWithAuthResult(authResult, promise); |
| 1432 | + } else { |
| 1433 | + promiseRejectAuthException(promise, task.getException()); |
| 1434 | + } |
| 1435 | + }); |
| 1436 | + |
| 1437 | + } |
| 1438 | + |
| 1439 | + @ReactMethod |
| 1440 | + public void generateTotpSecret( |
| 1441 | + final String appName, |
| 1442 | + final String sessionKey, |
| 1443 | + final Promise promise) { |
| 1444 | + |
| 1445 | + final MultiFactorSession session = mMultiFactorSessions.get(sessionKey); |
| 1446 | + if (session == null) { |
| 1447 | + rejectPromiseWithCodeAndMessage( |
| 1448 | + promise, |
| 1449 | + "invalid-multi-factor-session", |
| 1450 | + "No resolver for session found. Is the session id correct?"); |
| 1451 | + return; |
| 1452 | + } |
| 1453 | + |
| 1454 | + TotpMultiFactorGenerator |
| 1455 | + .generateSecret(session) |
| 1456 | + .addOnCompleteListener( |
| 1457 | + task -> { |
| 1458 | + if (task.isSuccessful()) { |
| 1459 | + TotpSecret totpSecret = task.getResult(); |
| 1460 | + String totpSecretKey = totpSecret.getSharedSecretKey(); |
| 1461 | + mTotpSecrets.put(totpSecretKey, totpSecret); |
| 1462 | + WritableMap result = Arguments.createMap(); |
| 1463 | + result.putString("secretKey", totpSecretKey); |
| 1464 | + promise.resolve(result); |
| 1465 | + } else { |
| 1466 | + promiseRejectAuthException(promise, task.getException()); |
| 1467 | + } |
| 1468 | + }); |
| 1469 | + } |
| 1470 | + |
1338 | 1471 | @ReactMethod |
1339 | 1472 | public void confirmationResultConfirm( |
1340 | 1473 | String appName, final String verificationCode, final Promise promise) { |
|
0 commit comments