Skip to content

Commit f884b13

Browse files
authored
fix: totp import api (#915)
* fix: totp import api * fix: refactor
1 parent 9ef8d5f commit f884b13

File tree

3 files changed

+120
-43
lines changed

3 files changed

+120
-43
lines changed

src/main/java/io/supertokens/totp/Totp.java

+47-42
Original file line numberDiff line numberDiff line change
@@ -81,71 +81,76 @@ public static TOTPDevice registerDevice(Main main, String userId,
8181
}
8282
}
8383

84-
public static TOTPDevice createDevice(Main main, AppIdentifierWithStorage appIdentifierWithStorage, TOTPDevice device)
84+
public static TOTPDevice createDevice(Main main, AppIdentifierWithStorage appIdentifierWithStorage, String userId,
85+
String deviceName, int skew, int period, String secretKey, boolean verified,
86+
long createdAt)
8587
throws DeviceAlreadyExistsException, StorageQueryException, FeatureNotEnabledException,
8688
TenantOrAppNotFoundException {
8789

8890
Mfa.checkForMFAFeature(appIdentifierWithStorage, main);
8991

90-
TOTPSQLStorage totpStorage = appIdentifierWithStorage.getTOTPStorage();
91-
try {
92-
return totpStorage.startTransaction(con -> {
93-
try {
94-
TOTPDevice existingDevice = totpStorage.getDeviceByName_Transaction(con, appIdentifierWithStorage, device.userId, device.deviceName);
95-
if (existingDevice == null) {
96-
return totpStorage.createDevice_Transaction(con, appIdentifierWithStorage, device);
97-
} else if (!existingDevice.verified) {
98-
totpStorage.deleteDevice_Transaction(con, appIdentifierWithStorage, device.userId, device.deviceName);
99-
return totpStorage.createDevice_Transaction(con, appIdentifierWithStorage, device);
100-
} else {
101-
throw new StorageTransactionLogicException(new DeviceAlreadyExistsException());
92+
if (deviceName != null) {
93+
TOTPSQLStorage totpStorage = appIdentifierWithStorage.getTOTPStorage();
94+
try {
95+
return totpStorage.startTransaction(con -> {
96+
try {
97+
TOTPDevice existingDevice = totpStorage.getDeviceByName_Transaction(con, appIdentifierWithStorage, userId, deviceName);
98+
if (existingDevice == null) {
99+
return totpStorage.createDevice_Transaction(con, appIdentifierWithStorage, new TOTPDevice(
100+
userId, deviceName, secretKey, period, skew, verified, createdAt
101+
));
102+
} else if (!existingDevice.verified) {
103+
totpStorage.deleteDevice_Transaction(con, appIdentifierWithStorage, userId, deviceName);
104+
return totpStorage.createDevice_Transaction(con, appIdentifierWithStorage, new TOTPDevice(
105+
userId, deviceName, secretKey, period, skew, verified, createdAt
106+
));
107+
} else {
108+
throw new StorageTransactionLogicException(new DeviceAlreadyExistsException());
109+
}
110+
} catch (TenantOrAppNotFoundException | DeviceAlreadyExistsException e) {
111+
throw new StorageTransactionLogicException(e);
102112
}
103-
} catch (TenantOrAppNotFoundException | DeviceAlreadyExistsException e) {
104-
throw new StorageTransactionLogicException(e);
113+
});
114+
} catch (StorageTransactionLogicException e) {
115+
if (e.actualException instanceof DeviceAlreadyExistsException) {
116+
throw (DeviceAlreadyExistsException) e.actualException;
105117
}
106-
});
107-
} catch (StorageTransactionLogicException e) {
108-
if (e.actualException instanceof DeviceAlreadyExistsException) {
109-
throw (DeviceAlreadyExistsException) e.actualException;
118+
throw new StorageQueryException(e.actualException);
110119
}
111-
throw new StorageQueryException(e.actualException);
112120
}
113-
}
114121

115-
public static TOTPDevice registerDevice(AppIdentifierWithStorage appIdentifierWithStorage, Main main, String userId,
116-
String deviceName, int skew, int period)
117-
throws StorageQueryException, DeviceAlreadyExistsException, NoSuchAlgorithmException,
118-
FeatureNotEnabledException, TenantOrAppNotFoundException, StorageTransactionLogicException {
119-
120-
String secret = generateSecret();
121-
TOTPDevice device = new TOTPDevice(userId, deviceName, secret, period, skew, false, System.currentTimeMillis());
122122
TOTPSQLStorage totpStorage = appIdentifierWithStorage.getTOTPStorage();
123-
124-
if (deviceName != null) {
125-
return createDevice(main, appIdentifierWithStorage, device);
126-
}
127-
128-
// Find number of existing devices to set device name
129123
TOTPDevice[] devices = totpStorage.getDevices(appIdentifierWithStorage, userId);
130124
int verifiedDevicesCount = Arrays.stream(devices).filter(d -> d.verified).toArray().length;
131125

132126
while (true) {
133127
try {
134-
return createDevice(main, appIdentifierWithStorage, new TOTPDevice(
135-
device.userId,
128+
return createDevice(main, appIdentifierWithStorage,
129+
userId,
136130
"TOTP Device " + verifiedDevicesCount,
137-
device.secretKey,
138-
device.period,
139-
device.skew,
140-
device.verified,
141-
device.createdAt
142-
));
131+
skew,
132+
period,
133+
secretKey,
134+
verified,
135+
createdAt
136+
);
143137
} catch (DeviceAlreadyExistsException e){
144138
}
145139
verifiedDevicesCount++;
146140
}
147141
}
148142

143+
public static TOTPDevice registerDevice(AppIdentifierWithStorage appIdentifierWithStorage, Main main, String userId,
144+
String deviceName, int skew, int period)
145+
throws StorageQueryException, DeviceAlreadyExistsException, NoSuchAlgorithmException,
146+
FeatureNotEnabledException, TenantOrAppNotFoundException, StorageTransactionLogicException {
147+
148+
String secretKey = generateSecret();
149+
150+
return createDevice(main, appIdentifierWithStorage, userId, deviceName, skew, period, secretKey, false,
151+
System.currentTimeMillis());
152+
}
153+
149154
private static void checkAndStoreCode(TenantIdentifierWithStorage tenantIdentifierWithStorage, Main main,
150155
String userId, TOTPDevice[] devices,
151156
String code)

src/main/java/io/supertokens/webserver/api/totp/ImportTotpDeviceAPI.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,11 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
8989
appIdentifierWithStorage = getAppIdentifierWithStorage(req);
9090
}
9191

92-
Totp.createDevice(super.main, appIdentifierWithStorage, new TOTPDevice(userId, deviceName, secretKey, period, skew, true, System.currentTimeMillis()));
92+
TOTPDevice createdDevice = Totp.createDevice(super.main, appIdentifierWithStorage,
93+
userId, deviceName, skew, period, secretKey, true, System.currentTimeMillis());
9394

9495
result.addProperty("status", "OK");
96+
result.addProperty("deviceName", createdDevice.deviceName);
9597
super.sendJsonResponse(200, result, resp);
9698
} catch (DeviceAlreadyExistsException e) {
9799
result.addProperty("status", "DEVICE_ALREADY_EXISTS_ERROR");

src/test/java/io/supertokens/test/totp/api/ImportTotpDeviceAPITest.java

+70
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ public void testApi() throws Exception {
150150
Utils.getCdiVersionStringLatestForTests(),
151151
"totp");
152152
assert res.get("status").getAsString().equals("OK");
153+
assertEquals("d1", res.get("deviceName").getAsString());
153154

154155
// try again with same device name:
155156
JsonObject res2 = HttpRequestForTesting.sendJsonPOSTRequest(
@@ -188,4 +189,73 @@ public void testApi() throws Exception {
188189
process.kill();
189190
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
190191
}
192+
193+
@Test
194+
public void testApiWithoutDeviceName() throws Exception {
195+
String[] args = {"../"};
196+
197+
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
198+
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
199+
200+
if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) {
201+
return;
202+
}
203+
204+
FeatureFlag.getInstance(process.main)
205+
.setLicenseKeyAndSyncFeatures(TotpLicenseTest.OPAQUE_KEY_WITH_MFA_FEATURE);
206+
FeatureFlagTestContent.getInstance(process.main)
207+
.setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{EE_FEATURES.MFA});
208+
209+
if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) {
210+
return;
211+
}
212+
213+
{
214+
String secret = "ZNPARPDTO6BFVSOFM3BPJGORPYTNTDSF";
215+
216+
JsonObject body = new JsonObject();
217+
body.addProperty("secretKey", "");
218+
body.addProperty("userId", "user-id");
219+
body.addProperty("secretKey", secret);
220+
body.addProperty("skew", 0);
221+
body.addProperty("period", 30);
222+
223+
JsonObject res = HttpRequestForTesting.sendJsonPOSTRequest(
224+
process.getProcess(),
225+
"",
226+
"http://localhost:3567/recipe/totp/device/import",
227+
body,
228+
1000,
229+
1000,
230+
null,
231+
Utils.getCdiVersionStringLatestForTests(),
232+
"totp");
233+
assert res.get("status").getAsString().equals("OK");
234+
assertEquals("TOTP Device 0", res.get("deviceName").getAsString());
235+
}
236+
237+
{ // Check for device already exists
238+
String secret = "ZNPARPDTO6BFVSOFM3BPJGORPYTNTDSF";
239+
240+
JsonObject body = new JsonObject();
241+
body.addProperty("secretKey", "");
242+
body.addProperty("userId", "user-id");
243+
body.addProperty("secretKey", secret);
244+
body.addProperty("skew", 0);
245+
body.addProperty("period", 30);
246+
body.addProperty("deviceName", "TOTP Device 0");
247+
248+
JsonObject res = HttpRequestForTesting.sendJsonPOSTRequest(
249+
process.getProcess(),
250+
"",
251+
"http://localhost:3567/recipe/totp/device/import",
252+
body,
253+
1000,
254+
1000,
255+
null,
256+
Utils.getCdiVersionStringLatestForTests(),
257+
"totp");
258+
assert res.get("status").getAsString().equals("DEVICE_ALREADY_EXISTS_ERROR");
259+
}
260+
}
191261
}

0 commit comments

Comments
 (0)