Skip to content
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

Email OTP based verification at registration #720

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2220615
Merge pull request #1 from wso2-extensions/master
Gastro-Diron Jun 12, 2023
f4eda00
Moved the changes to a branch created on master
Gastro-Diron Jun 12, 2023
4ee00db
removed verificationMethod variable and duplicate methods
Gastro-Diron Jun 13, 2023
622a203
Update UserSelfRegistrationHandler.java
Gastro-Diron Jun 13, 2023
f51e3fb
seperated secretKey generation method to two methods
Gastro-Diron Jun 14, 2023
ceb74d3
Update Utils.java
Gastro-Diron Jun 15, 2023
13f5ff7
Assigned constant for OTPCode
Gastro-Diron Jun 19, 2023
4c4231c
Merge pull request #2 from Gastro-Diron/gastrojune14
Gastro-Diron Jun 19, 2023
c4b0c29
add unit test
Gastro-Diron Jun 22, 2023
1230ecc
Merge pull request #3 from Gastro-Diron/gastrojune22
Gastro-Diron Jun 28, 2023
7447dd7
Changes made after code review
Gastro-Diron Jun 29, 2023
ec66550
Reverted the changes in the resendcodeapiserviceimpl.java
Gastro-Diron Jun 29, 2023
ee2dc42
Revert the changes made in the resendcodeapiserviceimpl.java
Gastro-Diron Jun 29, 2023
5329407
change the visibility of triggerNotification to private
Gastro-Diron Jun 29, 2023
b4bca7c
Added the checkstyle to UserSelfRegistrationHandler.java
Gastro-Diron Jun 30, 2023
2d932b9
Added CheckStyle for Utils.java, IdentityRecoveryConstants.java, Self…
Gastro-Diron Jun 30, 2023
fa79de0
Mask the username
Gastro-Diron Jun 30, 2023
553c761
Revert the checkstyle
Gastro-Diron Jun 30, 2023
7d410d6
Change the description of the configuration
Gastro-Diron Jul 6, 2023
d4efc7c
Add checkstyle
Gastro-Diron Jul 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public class IdentityRecoveryConstants {
public static final String NOTIFICATION_TYPE_RESEND_ADMIN_FORCED_PASSWORD_RESET_WITH_OTP =
"resendAdminForcedPasswordResetWithOTP";
public static final String NOTIFICATION_TYPE_ACCOUNT_CONFIRM = "accountconfirmation";

public static final String NOTIFICATION_TYPE_ACCOUNT_CONFIRM_EMAIL_OTP = "accountConfirmationEmailOTP";
public static final String NOTIFICATION_TYPE_RESEND_ACCOUNT_CONFIRM = "resendaccountconfirmation";
public static final String NOTIFICATION_TYPE_EMAIL_CONFIRM = "emailconfirm";
public static final String NOTIFICATION_TYPE_LITE_USER_EMAIL_CONFIRM = "liteUserEmailConfirmation";
Expand All @@ -71,6 +73,8 @@ public class IdentityRecoveryConstants {
public static final String TEMPLATE_TYPE = "TEMPLATE_TYPE";
public static final String EMAIL_TEMPLATE_NAME = "templateName";
public static final String CONFIRMATION_CODE = "confirmation-code";
public static final String OTP_CODE = "OTPCode";
Gastro-Diron marked this conversation as resolved.
Show resolved Hide resolved

public static final String VERIFICATION_PENDING_EMAIL = "verification-pending-email";
public static final String NEW_EMAIL_ADDRESS = "new-email-address";
public static final String NOTIFY = "notify";
Expand Down Expand Up @@ -110,7 +114,6 @@ public class IdentityRecoveryConstants {
public static final String MOBILE_NUMBER_PENDING_VALUE_CLAIM =
"http://wso2.org/claims/identity/mobileNumber.pendingValue";
public static final String PREFERRED_CHANNEL_CLAIM = "http://wso2.org/claims/identity/preferredChannel";

public static final String ASK_PASSWORD_CLAIM = "http://wso2.org/claims/identity/askPassword";
public static final String ADMIN_FORCED_PASSWORD_RESET_CLAIM = "http://wso2.org/claims/identity/adminForcedPasswordReset";
public static final String TENANT_ADMIN_ASK_PASSWORD_CLAIM =
Expand Down Expand Up @@ -163,6 +166,7 @@ public class IdentityRecoveryConstants {
public static final String USER_ACCOUNT_RECOVERY = "UAR";

public static final int SMS_OTP_CODE_LENGTH = 6;
public static final int EMAIL_OTP_CODE_LENGTH = 6;
public static final String ENABLE_DETAILED_ERROR_RESPONSE = "Recovery.ErrorMessage.EnableDetailedErrorMessages";
// Recovery code given at the username and password recovery initiation.
public static final int RECOVERY_CODE_DEFAULT_EXPIRY_TIME = 1;
Expand Down Expand Up @@ -407,9 +411,9 @@ public enum ErrorMessages {

// UEV - User Email Verification.
ERROR_CODE_VERIFICATION_EMAIL_NOT_FOUND("UEV-10001", "Email address not found for email verification"),

INVALID_PASSWORD_RECOVERY_REQUEST("APR-10000", "Invalid Password Recovery Request");


private final String code;
private final String message;

Expand Down Expand Up @@ -537,6 +541,7 @@ public static class ConnectorConfig {
".Password.ReCaptcha.MaxFailedAttempts";
public static final String RECOVERY_CALLBACK_REGEX = "Recovery.CallbackRegex";
public static final String ENABLE_SELF_SIGNUP = "SelfRegistration.Enable";
public static final String ENABLE_EMAIL_OTP_VERIFICATION = "SelfRegistration.EmailOTPVerification.Enable";
Gastro-Diron marked this conversation as resolved.
Show resolved Hide resolved
public static final String ACCOUNT_LOCK_ON_CREATION = "SelfRegistration.LockOnCreation";
public static final String SEND_CONFIRMATION_NOTIFICATION = "SelfRegistration.SendConfirmationOnCreation";
public static final String SIGN_UP_NOTIFICATION_INTERNALLY_MANAGE = "SelfRegistration.Notification" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,16 @@ private void validateRequestAttributes(User recoveredUser, RecoveryScenarios sce
private void triggerNotification(User user, String notificationChannel, String templateName, String code,
String eventName, Property[] metaProperties) throws IdentityRecoveryException {

boolean emailOTPenabled = false;
try {
emailOTPenabled = Boolean.parseBoolean(Utils.getConnectorConfig(
IdentityRecoveryConstants.ConnectorConfig.ENABLE_EMAIL_OTP_VERIFICATION, user.getTenantDomain()));
} catch (IdentityEventException e) {
throw Utils.handleServerException(
IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_ERROR_GETTING_CONNECTOR_CONFIG,
user.getTenantDomain(), e);
}

HashMap<String, Object> properties = new HashMap<>();
properties.put(IdentityEventConstants.EventProperty.USER_NAME, user.getUserName());
properties.put(IdentityEventConstants.EventProperty.TENANT_DOMAIN, user.getTenantDomain());
Expand All @@ -237,7 +247,11 @@ private void triggerNotification(User user, String notificationChannel, String t
}
properties.put(IdentityEventConstants.EventProperty.NOTIFICATION_CHANNEL, notificationChannel);
if (StringUtils.isNotBlank(code)) {
properties.put(IdentityRecoveryConstants.CONFIRMATION_CODE, code);
if (emailOTPenabled) {
properties.put(IdentityRecoveryConstants.OTP_CODE, code);
} else {
properties.put(IdentityRecoveryConstants.CONFIRMATION_CODE, code);
}
}
if (metaProperties != null) {
for (Property metaProperty : metaProperties) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public Map<String, String> getPropertyNameMapping() {

Map<String, String> nameMapping = new HashMap<>();
nameMapping.put(IdentityRecoveryConstants.ConnectorConfig.ENABLE_SELF_SIGNUP, "User self registration");
nameMapping.put(IdentityRecoveryConstants.ConnectorConfig.ENABLE_EMAIL_OTP_VERIFICATION, "Email OTP based verification");
nameMapping.put(IdentityRecoveryConstants.ConnectorConfig.ACCOUNT_LOCK_ON_CREATION,
"Lock user account on creation");
nameMapping.put(IdentityRecoveryConstants.ConnectorConfig.SEND_CONFIRMATION_NOTIFICATION,
Expand Down Expand Up @@ -118,6 +119,8 @@ public Map<String, String> getPropertyDescriptionMapping() {
Map<String, String> descriptionMapping = new HashMap<>();
descriptionMapping.put(IdentityRecoveryConstants.ConnectorConfig.ENABLE_SELF_SIGNUP,
"Allow user's to self register to the system.");
descriptionMapping.put(IdentityRecoveryConstants.ConnectorConfig.ENABLE_EMAIL_OTP_VERIFICATION,
"Enable if email verification is done by sending an OTP to user's email.");
Gastro-Diron marked this conversation as resolved.
Show resolved Hide resolved
descriptionMapping.put(IdentityRecoveryConstants.ConnectorConfig.ACCOUNT_LOCK_ON_CREATION,
"Lock self registered user account until e-mail verification.");
descriptionMapping.put(IdentityRecoveryConstants.ConnectorConfig.SEND_CONFIRMATION_NOTIFICATION,
Expand Down Expand Up @@ -155,6 +158,7 @@ public String[] getPropertyNames() {

List<String> properties = new ArrayList<>();
properties.add(IdentityRecoveryConstants.ConnectorConfig.ENABLE_SELF_SIGNUP);
properties.add(IdentityRecoveryConstants.ConnectorConfig.ENABLE_EMAIL_OTP_VERIFICATION);
properties.add(IdentityRecoveryConstants.ConnectorConfig.ACCOUNT_LOCK_ON_CREATION);
properties.add(IdentityRecoveryConstants.ConnectorConfig.SEND_CONFIRMATION_NOTIFICATION);
properties.add(IdentityRecoveryConstants.ConnectorConfig.SIGN_UP_NOTIFICATION_INTERNALLY_MANAGE);
Expand All @@ -176,6 +180,7 @@ public String[] getPropertyNames() {
public Properties getDefaultPropertyValues(String tenantDomain) throws IdentityGovernanceException {

String enableSelfSignUp = "false";
String enableEmailOTPverification = "false";
String enableAccountLockOnCreation = "true";
String enableSendNotificationOnCreation = "false";
String enableNotificationInternallyManage = "true";
Expand All @@ -191,6 +196,8 @@ public Properties getDefaultPropertyValues(String tenantDomain) throws IdentityG

String selfSignUpProperty = IdentityUtil.getProperty(
IdentityRecoveryConstants.ConnectorConfig.ENABLE_SELF_SIGNUP);
String emailOTPverificationProprty = IdentityUtil.getProperty(
IdentityRecoveryConstants.ConnectorConfig.ENABLE_EMAIL_OTP_VERIFICATION);
String accountLockProperty = IdentityUtil.getProperty(
IdentityRecoveryConstants.ConnectorConfig.ACCOUNT_LOCK_ON_CREATION);
String sendNotificationOnCreationProperty = IdentityUtil.getProperty(
Expand Down Expand Up @@ -219,6 +226,9 @@ public Properties getDefaultPropertyValues(String tenantDomain) throws IdentityG
if (StringUtils.isNotEmpty(selfSignUpProperty)) {
enableSelfSignUp = selfSignUpProperty;
}
if (StringUtils.isNotEmpty(emailOTPverificationProprty)) {
enableEmailOTPverification = emailOTPverificationProprty;
}
if (StringUtils.isNotEmpty(accountLockProperty)) {
enableAccountLockOnCreation = accountLockProperty;
}
Expand Down Expand Up @@ -258,6 +268,7 @@ public Properties getDefaultPropertyValues(String tenantDomain) throws IdentityG

Map<String, String> defaultProperties = new HashMap<>();
defaultProperties.put(IdentityRecoveryConstants.ConnectorConfig.ENABLE_SELF_SIGNUP, enableSelfSignUp);
defaultProperties.put(IdentityRecoveryConstants.ConnectorConfig.ENABLE_EMAIL_OTP_VERIFICATION, enableEmailOTPverification);
defaultProperties.put(IdentityRecoveryConstants.ConnectorConfig.ACCOUNT_LOCK_ON_CREATION,
enableAccountLockOnCreation);
defaultProperties.put(IdentityRecoveryConstants.ConnectorConfig.SEND_CONFIRMATION_NOTIFICATION,
Expand Down Expand Up @@ -311,6 +322,9 @@ public Map<String, Property> getMetaData() {
meta.put(IdentityRecoveryConstants.ConnectorConfig.ENABLE_SELF_SIGNUP,
getPropertyObject(IdentityMgtConstants.DataTypes.BOOLEAN.getValue()));

meta.put(IdentityRecoveryConstants.ConnectorConfig.ENABLE_EMAIL_OTP_VERIFICATION,
getPropertyObject(IdentityMgtConstants.DataTypes.BOOLEAN.getValue()));

meta.put(IdentityRecoveryConstants.ConnectorConfig.ACCOUNT_LOCK_ON_CREATION,
getPropertyObject(IdentityMgtConstants.DataTypes.BOOLEAN.getValue()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.CarbonConstants;
import org.wso2.carbon.identity.application.common.model.IdentityProviderProperty;
import org.wso2.carbon.identity.application.common.model.User;
import org.wso2.carbon.identity.base.IdentityRuntimeException;
import org.wso2.carbon.identity.core.bean.context.MessageContext;
Expand All @@ -29,8 +30,7 @@
import org.wso2.carbon.identity.event.IdentityEventException;
import org.wso2.carbon.identity.event.event.Event;
import org.wso2.carbon.identity.event.handler.AbstractEventHandler;
import org.wso2.carbon.identity.governance.IdentityGovernanceUtil;
import org.wso2.carbon.identity.governance.IdentityMgtConstants;
import org.wso2.carbon.identity.governance.*;
Gastro-Diron marked this conversation as resolved.
Show resolved Hide resolved
import org.wso2.carbon.identity.governance.exceptions.notiification.NotificationChannelManagerClientException;
import org.wso2.carbon.identity.governance.exceptions.notiification.NotificationChannelManagerException;
import org.wso2.carbon.identity.governance.service.notification.NotificationChannelManager;
Expand Down Expand Up @@ -422,11 +422,21 @@ protected void triggerNotification(User user, String type, String code, Property
* @param eventName Name of the event
* @throws IdentityRecoveryException Error triggering notifications
*/
private void triggerNotification(User user, String notificationChannel, String code, Property[] props,
public void triggerNotification(User user, String notificationChannel, String code, Property[] props,
Gastro-Diron marked this conversation as resolved.
Show resolved Hide resolved
String eventName) throws IdentityRecoveryException {

boolean emailOTPenabled = false;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take this logic out of triggernotification

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we need to take this out of the TriggerNotification method then we need to pass this as an additional parameter to the method. If that is OK, I can take this out of the method and add an additional parameter for this method.

try {
emailOTPenabled = Boolean.parseBoolean(Utils.getConnectorConfig(
IdentityRecoveryConstants.ConnectorConfig.ENABLE_EMAIL_OTP_VERIFICATION, user.getTenantDomain()));
} catch (IdentityEventException e) {
throw Utils.handleServerException(
IdentityRecoveryConstants.ErrorMessages.ERROR_CODE_ERROR_GETTING_CONNECTOR_CONFIG,
user.getTenantDomain(), e);
}

if (log.isDebugEnabled()) {
log.debug("Sending self user registration notification user: " + user.getUserName());
log.debug("Sending account confirmation notification to user: " + user.getUserName());
Gastro-Diron marked this conversation as resolved.
Show resolved Hide resolved
}
HashMap<String, Object> properties = new HashMap<>();
properties.put(IdentityEventConstants.EventProperty.USER_NAME, user.getUserName());
Expand All @@ -438,12 +448,20 @@ private void triggerNotification(User user, String notificationChannel, String c
for (Property prop : props) {
properties.put(prop.getKey(), prop.getValue());
}
}if (StringUtils.isNotBlank(code)) {
Gastro-Diron marked this conversation as resolved.
Show resolved Hide resolved
Gastro-Diron marked this conversation as resolved.
Show resolved Hide resolved
if (emailOTPenabled) {
properties.put(IdentityRecoveryConstants.OTP_CODE, code);
} else {
properties.put(IdentityRecoveryConstants.CONFIRMATION_CODE, code);
}
}
if (StringUtils.isNotBlank(code)) {
properties.put(IdentityRecoveryConstants.CONFIRMATION_CODE, code);
}
properties.put(IdentityRecoveryConstants.TEMPLATE_TYPE,
if (emailOTPenabled) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check for the canhandle method to which events it can handle

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method is used only for the self user registration purpose. So we do not have to remove this logic out of TriggerNotification method.

properties.put(IdentityRecoveryConstants.TEMPLATE_TYPE,
IdentityRecoveryConstants.NOTIFICATION_TYPE_ACCOUNT_CONFIRM_EMAIL_OTP);
} else {
properties.put(IdentityRecoveryConstants.TEMPLATE_TYPE,
IdentityRecoveryConstants.NOTIFICATION_TYPE_ACCOUNT_CONFIRM);
}
Event identityMgtEvent = new Event(eventName, properties);
try {
IdentityRecoveryServiceDataHolder.getInstance().getIdentityEventService().handleEvent(identityMgtEvent);
Expand Down
Loading