diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bd6fbd7..84ddb2af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [2.10.0] - 2021-12-16 +## [2.10.0] - 2021-11-16 -### Added +# Added +- Passwordless interface - User deletion methods ## [2.9.0] - 2021-08-08 diff --git a/build.gradle b/build.gradle index 048da4ee..db9240b5 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "2.10.0" +version = "2.11.0" repositories { mavenCentral() diff --git a/jar/plugin-interface-2.10.0.jar b/jar/plugin-interface-2.11.0.jar similarity index 56% rename from jar/plugin-interface-2.10.0.jar rename to jar/plugin-interface-2.11.0.jar index e9f9604b..477f8c0c 100644 Binary files a/jar/plugin-interface-2.10.0.jar and b/jar/plugin-interface-2.11.0.jar differ diff --git a/src/main/java/io/supertokens/pluginInterface/RECIPE_ID.java b/src/main/java/io/supertokens/pluginInterface/RECIPE_ID.java index 59c8f56c..a08586cc 100644 --- a/src/main/java/io/supertokens/pluginInterface/RECIPE_ID.java +++ b/src/main/java/io/supertokens/pluginInterface/RECIPE_ID.java @@ -20,7 +20,7 @@ public enum RECIPE_ID { EMAIL_PASSWORD("emailpassword"), THIRD_PARTY("thirdparty"), SESSION("session"), - EMAIL_VERIFICATION("emailverification"), JWT("jwt"); + EMAIL_VERIFICATION("emailverification"), JWT("jwt"), PASSWORDLESS("passwordless"); private final String name; diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessCode.java b/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessCode.java new file mode 100644 index 00000000..28783c8f --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessCode.java @@ -0,0 +1,69 @@ +package io.supertokens.pluginInterface.passwordless; + +public class PasswordlessCode { + public final String id; + public final String deviceIdHash; + public final String linkCodeHash; + public final Long createdAt; + + public PasswordlessCode(String id, String deviceIdHash, String linkCodeHash, Long createdAt) { + this.id = id; + this.deviceIdHash = deviceIdHash; + this.linkCodeHash = linkCodeHash; + this.createdAt = createdAt; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((createdAt == null) ? 0 : createdAt.hashCode()); + result = prime * result + ((deviceIdHash == null) ? 0 : deviceIdHash.hashCode()); + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + ((linkCodeHash == null) ? 0 : linkCodeHash.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof PasswordlessCode)) { + return false; + } + PasswordlessCode other = (PasswordlessCode) obj; + if (createdAt == null) { + if (other.createdAt != null) { + return false; + } + } else if (!createdAt.equals(other.createdAt)) { + return false; + } + if (deviceIdHash == null) { + if (other.deviceIdHash != null) { + return false; + } + } else if (!deviceIdHash.equals(other.deviceIdHash)) { + return false; + } + if (id == null) { + if (other.id != null) { + return false; + } + } else if (!id.equals(other.id)) { + return false; + } + if (linkCodeHash == null) { + if (other.linkCodeHash != null) { + return false; + } + } else if (!linkCodeHash.equals(other.linkCodeHash)) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessDevice.java b/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessDevice.java new file mode 100644 index 00000000..d5237570 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessDevice.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.passwordless; + +public class PasswordlessDevice { + public final String deviceIdHash; + public final String email; + public final String phoneNumber; + public final String linkCodeSalt; + public final int failedAttempts; + + public PasswordlessDevice(String deviceIdHash, String email, String phoneNumber, String linkCodeSalt, + int failedAttempts) { + this.deviceIdHash = deviceIdHash; + this.email = email; + this.phoneNumber = phoneNumber; + this.failedAttempts = failedAttempts; + this.linkCodeSalt = linkCodeSalt; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessStorage.java b/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessStorage.java new file mode 100644 index 00000000..19e6da46 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessStorage.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.passwordless; + +import io.supertokens.pluginInterface.authRecipe.AuthRecipeStorage; +import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException; +import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateUserIdException; +import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.passwordless.exception.DuplicateCodeIdException; +import io.supertokens.pluginInterface.passwordless.exception.DuplicateDeviceIdHashException; +import io.supertokens.pluginInterface.passwordless.exception.DuplicateLinkCodeHashException; +import io.supertokens.pluginInterface.passwordless.exception.DuplicatePhoneNumberException; +import io.supertokens.pluginInterface.passwordless.exception.UnknownDeviceIdHash; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public interface PasswordlessStorage extends AuthRecipeStorage { + void createDeviceWithCode(@Nullable String email, @Nullable String phoneNumber, @Nonnull String linkCodeSalt, + PasswordlessCode code) throws StorageQueryException, DuplicateDeviceIdHashException, + DuplicateCodeIdException, DuplicateLinkCodeHashException; + + void createCode(PasswordlessCode code) + throws StorageQueryException, UnknownDeviceIdHash, DuplicateCodeIdException, DuplicateLinkCodeHashException; + + void createUser(UserInfo user) throws StorageQueryException, DuplicateEmailException, DuplicatePhoneNumberException, + DuplicateUserIdException; + + void deletePasswordlessUser(String userId) throws StorageQueryException; + + PasswordlessDevice getDevice(String deviceIdHash) throws StorageQueryException; + + PasswordlessDevice[] getDevicesByEmail(@Nonnull String email) throws StorageQueryException; + + PasswordlessDevice[] getDevicesByPhoneNumber(@Nonnull String phoneNumber) throws StorageQueryException; + + PasswordlessCode[] getCodesOfDevice(String deviceIdHash) throws StorageQueryException; + + PasswordlessCode[] getCodesBefore(long time) throws StorageQueryException; + + PasswordlessCode getCode(String codeId) throws StorageQueryException; + + PasswordlessCode getCodeByLinkCodeHash(String linkCode) throws StorageQueryException; + + UserInfo getUserById(String userId) throws StorageQueryException; + + UserInfo getUserByEmail(@Nonnull String email) throws StorageQueryException; + + UserInfo getUserByPhoneNumber(@Nonnull String phoneNumber) throws StorageQueryException; +} \ No newline at end of file diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/UserInfo.java b/src/main/java/io/supertokens/pluginInterface/passwordless/UserInfo.java new file mode 100644 index 00000000..0bd392db --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/UserInfo.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.passwordless; + +import javax.annotation.Nullable; + +import io.supertokens.pluginInterface.RECIPE_ID; +import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo; + +public class UserInfo extends AuthRecipeUserInfo { + public final String email; + public final String phoneNumber; + + public UserInfo(String id, @Nullable String email, @Nullable String phoneNumber, long timeJoined) { + super(id, timeJoined); + + if (email == null && phoneNumber == null) { + throw new IllegalArgumentException("Both email and phoneNumber cannot be null"); + } + + this.email = email; + this.phoneNumber = phoneNumber; + } + + @Override + public RECIPE_ID getRecipeId() { + return RECIPE_ID.PASSWORDLESS; + } + + @Override + public boolean equals(Object other) { + if (other instanceof UserInfo) { + UserInfo otherUserInfo = (UserInfo) other; + return ((otherUserInfo.email == null && this.email == null) || otherUserInfo.email.equals(this.email)) + && ((otherUserInfo.phoneNumber == null && this.phoneNumber == null) + || otherUserInfo.phoneNumber.equals(this.phoneNumber)) + && otherUserInfo.id.equals(this.id) && otherUserInfo.timeJoined == this.timeJoined; + } + return false; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/exception/DuplicateCodeIdException.java b/src/main/java/io/supertokens/pluginInterface/passwordless/exception/DuplicateCodeIdException.java new file mode 100644 index 00000000..daf1306c --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/exception/DuplicateCodeIdException.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.passwordless.exception; + +import io.supertokens.pluginInterface.emailpassword.exceptions.EmailPasswordException; + +public class DuplicateCodeIdException extends EmailPasswordException { + private static final long serialVersionUID = 6848053563771647272L; +} diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/exception/DuplicateDeviceIdHashException.java b/src/main/java/io/supertokens/pluginInterface/passwordless/exception/DuplicateDeviceIdHashException.java new file mode 100644 index 00000000..20f38cfe --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/exception/DuplicateDeviceIdHashException.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.passwordless.exception; + +import io.supertokens.pluginInterface.emailpassword.exceptions.EmailPasswordException; + +public class DuplicateDeviceIdHashException extends EmailPasswordException { + private static final long serialVersionUID = 6848053563771647272L; +} diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/exception/DuplicateLinkCodeHashException.java b/src/main/java/io/supertokens/pluginInterface/passwordless/exception/DuplicateLinkCodeHashException.java new file mode 100644 index 00000000..9357420c --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/exception/DuplicateLinkCodeHashException.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.passwordless.exception; + +import io.supertokens.pluginInterface.emailpassword.exceptions.EmailPasswordException; + +public class DuplicateLinkCodeHashException extends EmailPasswordException { + private static final long serialVersionUID = 6848053563771647272L; +} diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/exception/DuplicatePhoneNumberException.java b/src/main/java/io/supertokens/pluginInterface/passwordless/exception/DuplicatePhoneNumberException.java new file mode 100644 index 00000000..ae768db8 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/exception/DuplicatePhoneNumberException.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.passwordless.exception; + +import io.supertokens.pluginInterface.emailpassword.exceptions.EmailPasswordException; + +public class DuplicatePhoneNumberException extends EmailPasswordException { + private static final long serialVersionUID = 6848053563771647272L; +} diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/exception/DuplicateUserIdException.java b/src/main/java/io/supertokens/pluginInterface/passwordless/exception/DuplicateUserIdException.java new file mode 100644 index 00000000..56b7449d --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/exception/DuplicateUserIdException.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.passwordless.exception; + +import io.supertokens.pluginInterface.emailpassword.exceptions.EmailPasswordException; + +public class DuplicateUserIdException extends EmailPasswordException { + private static final long serialVersionUID = 6848053563771647272L; +} diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/exception/UnknownDeviceIdHash.java b/src/main/java/io/supertokens/pluginInterface/passwordless/exception/UnknownDeviceIdHash.java new file mode 100644 index 00000000..8c40b90c --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/exception/UnknownDeviceIdHash.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.passwordless.exception; + +import io.supertokens.pluginInterface.emailpassword.exceptions.EmailPasswordException; + +public class UnknownDeviceIdHash extends EmailPasswordException { + private static final long serialVersionUID = 6848053563771647272L; +} diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java new file mode 100644 index 00000000..b703a075 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.passwordless.sqlStorage; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException; +import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; +import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.passwordless.PasswordlessCode; +import io.supertokens.pluginInterface.passwordless.PasswordlessDevice; +import io.supertokens.pluginInterface.passwordless.PasswordlessStorage; +import io.supertokens.pluginInterface.passwordless.exception.DuplicatePhoneNumberException; +import io.supertokens.pluginInterface.sqlStorage.SQLStorage; +import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; + +public interface PasswordlessSQLStorage extends PasswordlessStorage, SQLStorage { + PasswordlessDevice getDevice_Transaction(TransactionConnection con, String deviceIdHash) + throws StorageQueryException; + + void incrementDeviceFailedAttemptCount_Transaction(TransactionConnection con, String deviceIdHash) + throws StorageQueryException; + + PasswordlessCode[] getCodesOfDevice_Transaction(TransactionConnection con, String deviceIdHash) + throws StorageQueryException; + + void deleteDevice_Transaction(TransactionConnection con, String deviceIdHash) throws StorageQueryException; + + void deleteDevicesByPhoneNumber_Transaction(TransactionConnection con, String phoneNumber) + throws StorageQueryException; + + void deleteDevicesByEmail_Transaction(TransactionConnection con, String email) throws StorageQueryException; + + PasswordlessCode getCodeByLinkCodeHash_Transaction(TransactionConnection con, String linkCodeHash) + throws StorageQueryException; + + void deleteCode_Transaction(TransactionConnection con, String codeId) throws StorageQueryException; + + void updateUserEmail_Transaction(TransactionConnection con, @Nonnull String userId, @Nullable String email) + throws StorageQueryException, UnknownUserIdException, DuplicateEmailException; + + void updateUserPhoneNumber_Transaction(TransactionConnection con, @Nonnull String userId, + @Nullable String phoneNumber) + throws StorageQueryException, UnknownUserIdException, DuplicatePhoneNumberException; +}