Thursday, November 20, 2014

Pin It


Get Gadget

User Self Registration & Account Confirmation with WSO2 Identity Server 5.0.0

If you have any enterprise application, registration of users and confirming there account and emails is a primary task. If your platform's identity server is WSO2 Identity Server, those tasks are built in and very easy to manage. In this article I am going to explain how we can enable User Self Registration & Account Confirmation using WSO2 Identity Server 5.0.0.

As usual the necessary API methods are exposed as a admin level SOAP API in WSO2 Identity Server. First of all you need to enable admin service visible to outside using carbon.xml. Then go to https://idnetity-server-host:idnetity-server-port/services/UserInformationRecoveryService?wsdl. (Default  idnetity-server-port = 9443) Then you can see the WSDL for the UserInformationRecoveryService which expose the API method to do User Self Registration & Account Confirmation. The important methods for our task are,
  • getUserIdentitySupportedClaims() - This method returns the claims URIs needed to register a user in WSO2 Identity Server
  • registerUser() - This method will register a user in WSO2 Identity Server
  • getCaptcha() - This methods help to generate captcha to verify the user is human
  • confirmUserSelfRegistration() - This method will confirm the user registration
Then I need to explain how to configure WSO2 Identity Server to do this process and steps of the process.  The key point of the process are,
  • First of all you need to know what are the data AKA claims needs to register a user in  WSO2 Identity Server. You can get those details by simple calling getUserIdentitySupportedClaims() method of the  UserInformationRecoveryService. The SOAP Request will look like,
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.mgt.identity.carbon.wso2.org">
       <soapenv:Header/>
       <soapenv:Body>
          <ser:getUserIdentitySupportedClaims>
             <ser:dialect>http://wso2.org/claims</ser:dialect>
          </ser:getUserIdentitySupportedClaims>
       </soapenv:Body>
    </soapenv:Envelope> 
Actually if you are aware of the claims you need not to call this method.
  • Then you need to call the registerUser method. The SOAP request will look like this,
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:ser="http://services.mgt.identity.carbon.wso2.org" xmlns:xsd="http://dto.mgt.identity.carbon.wso2.org/xsd"> <soap:Header/> <soap:Body> <ser:registerUser> <ser:userName>user</ser:userName> <ser:password>password</ser:password> <ser:claims> <xsd:claimUri>http://wso2.org/claims/emailaddress</xsd:claimUri> <xsd:claimValue>email</xsd:claimValue> </ser:claims> <ser:claims> <xsd:claimUri>http://wso2.org/claims/givenname</xsd:claimUri> <xsd:claimValue>firstname</xsd:claimValue> </ser:claims> <ser:claims> <xsd:claimUri>http://wso2.org/claims/lastname</xsd:claimUri> <xsd:claimValue>familyname</xsd:claimValue> </ser:claims> <ser:claims> <xsd:claimUri>http://wso2.org/claims/organization</xsd:claimUri> <xsd:claimValue>organization</xsd:claimValue> </ser:claims> <ser:tenantDomain></ser:tenantDomain> </ser:registerUser> </soap:Body> </soap:Envelope>
  • As soon as the user is registered they will be not allowed to login. They need to confirm there email accounts. To do that we need to configure IS like this. Open the WSO2_Identity_Server_Folder/repository/conf/security/identity­-mgt.properties. In that add this entry,
Authentication.Policy.Account.Lock.On.Creation=true
This will guarantee that user cant login as soon as they register. They need to verify there email.
  • As soon as user register, we need to send an email to user to confirm there account. That can be done with in the IS. To do that open the identity­-mgt.properties file again and add these entries.'
Identity.Listener.Enable=true
Notification.Sending.Internally.Managed=true
Notification.Expire.Time=7200
Notification.Sending.Enable=true
Authentication.Policy.Enable=true 
  • To send emails, you need to enable mailto transport in  WSO2 Identity Server. To do that open WSO2_Identity_Server_Folder/repository/conf/axis2/axis2.xml file. In that un-comment following entries and change to suitable SMTP configurations. 
<transportSender name="mailto" class="org.apache.axis2.transport.mail.MailTransportSender"> <parameter name="mail.smtp.host">smtp.gmail.com</parameter> <parameter name="mail.smtp.port">587</parameter> <parameter name="mail.smtp.starttls.enable">true</parameter> <parameter name="mail.smtp.auth">true</parameter> <parameter name="mail.smtp.user">synapse.demo.0</parameter> <parameter name="mail.smtp.password">mailpassword</parameter> <parameter name="mail.smtp.from">synapse.demo.0@gmail.com</parameter> </transportSender>
  • The account if confirmed when the confirmUserSelfRegistration() method is called. So in the account confirmation email we need to send a link to invoke that method. In my example I am sending a link to a Java servlet which we will call this method.
  • To send account registration email, we need to create a template. We can do that by editing  WSO2_Identity_Server_Folder/repository/conf/email/email­-admin­-config.xml. Edit the accountConfirmation entry. It will look like this,
<configuration type="accountConfirmation"> <targetEpr></targetEpr> <subject>XYZ Registration - Confirm Account Registration</subject> <body> Hi {first-name}, You have created an account in XYZ with following user name User Name: {user-name} Please click the following link to complete the registration. If clicking the link doesn't seem to work, you can copy and paste the link into your browser's address window. https://xyz.com/confirmRegistration?confirmationCode={confirmation-code}&amp;userName={user-name} </body> <footer> Best Regards, XYZ Inc. www.xyz.com </footer> <redirectPath></redirectPath> </configuration>
The given link will call the servlet which I have mentioned above.
  • Before confirm the registration we can check whether this call is made by a human. To do that we can use a captcha. The captcha based validation process is like this,
- Get captcha image from IS
- Captcha will be returned with a image and a secret key for image
- Send the users input for captcha along with secret key while we call confirmation method
To get a captcha we can call getCaptcha method of above service. The SOAP request will look like this,
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.mgt.identity.carbon.wso2.org">
    <soapenv:Header/>
    <soapenv:Body>
        <ser:getCaptcha/>
    </soapenv:Body>
</soapenv:Envelope> 
  • If we don't need captcha based validation we can configure it. To do that edit the following entry in WSO2_Identity_Server_Folder/repository/conf/security/identity­-mgt.properties file.
Captcha.Verification.Internally.Managed=true
  • Then you need to call the confirmUserSelfRegistration method to complete the registration. The SOAP request looks like this,
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.mgt.identity.carbon.wso2.org" xmlns:xsd="http://beans.mgt.captcha.carbon.wso2.org/xsd"> <soapenv:Header/> <soapenv:Body> <ser:confirmUserSelfRegistration> <ser:username>xyz</ser:username> <ser:code>xyz</ser:code> <ser:captcha> <xsd:imagePath>xyz</xsd:imagePath> <xsd:secretKey>xyz</xsd:secretKey> <xsd:userAnswer>xyz</xsd:userAnswer> </ser:captcha> <ser:tenantDomain></ser:tenantDomain> </ser:confirmUserSelfRegistration> </soapenv:Body> </soapenv:Envelope>
If we are not using captcha validation we don't need to send those parameters.
  • After a successful call the above method will complete the registration. Then user will be able to login. The account will be unlocked.
  • All the above methods should be called wiht Basic Auth Header with admin user name and password.
Lets look at how to do this with JAVA. To call the SOAP service we can use the STUB provided in side the product. That is org.wso2.carbon.identity.mgt.stub. Using that we can call the UserInformationRecoveryService easily. Thisis how you can call user registration service,

import org.apache.axis2.AxisFault;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.transport.http.HttpTransportProperties;
import org.emojot.webUI.Config;
import org.wso2.carbon.identity.mgt.stub.UserInformationRecoveryServiceIdentityMgtServiceExceptionException;
import org.wso2.carbon.identity.mgt.stub.UserInformationRecoveryServiceStub;
import org.wso2.carbon.identity.mgt.stub.beans.VerificationBean;
import org.wso2.carbon.identity.mgt.stub.dto.UserIdentityClaimDTO;
import java.rmi.RemoteException;
import java.util.Map;
import java.util.Properties;

public class UserRegistration  {

    public static void main(String[] args) {

        UserInformationRecoveryServiceStub stub=null;
        HttpTransportProperties.Authenticator authenticator = null;
        Map properties=null;

        String serviceURL = Config.isURL + "/services/UserInformationRecoveryService";

        try {
            stub = new UserInformationRecoveryServiceStub(null, serviceURL);
            ServiceClient client = stub._getServiceClient();
            Options options = client.getOptions();

            authenticator = new HttpTransportProperties.Authenticator();
            authenticator.setUsername(Config.isAdminUserName);
            authenticator.setPassword(Config.isAdminPassword);

            properties = new Properties();
            properties.put(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, authenticator);

            options.setProperties(properties);

        } catch (AxisFault axisFault) {
            System.out.println(axisFault.getMessage());
            System.out.println(axisFault.getDetail().toString());
        }

        String userName="username1";
        String password="password1";
        String emailaddress="username1@XYZ.com";
        String givenname="username1";
        String lastname="username1";
        String organization="XYZ";

            if(stub!=null){
                try {
                    UserIdentityClaimDTO claimEmailaddress=new UserIdentityClaimDTO();
                    claimEmailaddress.setClaimUri(UserMgtConstants.claimEmailaddress);
                    claimEmailaddress.setClaimValue(emailaddress);

                    UserIdentityClaimDTO claimGivenname=new UserIdentityClaimDTO();
                    claimGivenname.setClaimUri(UserMgtConstants.claimGivenname);
                    claimGivenname.setClaimValue(givenname);

                    UserIdentityClaimDTO claimLastname=new UserIdentityClaimDTO();
                    claimLastname.setClaimUri(UserMgtConstants.claimLastname);
                    claimLastname.setClaimValue(lastname);

                    UserIdentityClaimDTO claimOrganization=new UserIdentityClaimDTO();
                    claimOrganization.setClaimUri(UserMgtConstants.claimOrganization);
                    claimOrganization.setClaimValue(organization);

                    UserIdentityClaimDTO claims[]=new UserIdentityClaimDTO[]{claimEmailaddress,claimGivenname,claimLastname,claimOrganization};

                    VerificationBean verificationBean= stub.registerUser(userName, password, claims, null, null);
                    System.out.println("Successfully Registered the User -"+verificationBean.getUserId()+" Verification Key -"+verificationBean.getKey());

                } catch (UserInformationRecoveryServiceIdentityMgtServiceExceptionException e) {
                    System.out.println(e.getMessage());
                    System.out.println(e.getFaultMessage());
                } catch (RemoteException e) {
                    System.out.println(e.getMessage());
                }
            }else{
                System.out.println("Internal Error - Stub Creation Failed");
            }       
    }
}


After that you need to call the confirmation method. That can be also done like this. Here I am not using the internal captcha validation.

import org.apache.axis2.AxisFault;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.transport.http.HttpTransportProperties;
import org.emojot.webUI.Config;
import org.wso2.carbon.identity.mgt.stub.UserInformationRecoveryServiceIdentityMgtServiceExceptionException;
import org.wso2.carbon.identity.mgt.stub.UserInformationRecoveryServiceStub;
import org.wso2.carbon.identity.mgt.stub.beans.VerificationBean;
import org.wso2.carbon.identity.mgt.stub.dto.UserIdentityClaimDTO;
import java.rmi.RemoteException;
import java.util.Map;
import java.util.Properties;

public class RegistrationConfirm {

     public static void main(String[] args) {

        UserInformationRecoveryServiceStub stub=null;
        HttpTransportProperties.Authenticator authenticator = null;
        Map properties=null;

        String serviceURL = Config.isURL + "/services/UserInformationRecoveryService";
        try {

            stub = new UserInformationRecoveryServiceStub(null, serviceURL);
            ServiceClient client = stub._getServiceClient();
            Options options = client.getOptions();

            authenticator = new HttpTransportProperties.Authenticator();
            authenticator.setUsername(Config.isAdminUserName);
            authenticator.setPassword(Config.isAdminPassword);

            properties = new Properties();
            properties.put(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, authenticator);

            options.setProperties(properties);
        } catch (AxisFault axisFault) {
            System.out.println(axisFault.getMessage());
            System.out.println(axisFault.getDetail().toString());
        }

        String userName="username1";
        String confirmationCode="13677e9d-e3c8-4a78-a6e0-569163e66fba";

            if(stub!=null){
                try {
                    VerificationBean verificationBean= stub.confirmUserSelfRegistration(userName, confirmationCode, null,null);
                    System.out.println("Successfully Registered the User-" + userName + " Registration Confirmed -" + verificationBean.getVerified());
                } catch (UserInformationRecoveryServiceIdentityMgtServiceExceptionException e) {
                    System.out.println(e.getMessage());
                    System.out.println(e.getFaultMessage());
                } catch (RemoteException e) {
                    System.out.println(e.getMessage());
                }
            }else{
                System.out.println("Internal Error - Stub Creation Failed");
            }
    }
}

Hoep this post helps you. If you have any query please contact me.

    6 comments:

    1. I'm having a problem when I call confirmUserSelfRegistration method. I have configured IS to not need captcha, but If I always get 18004 error.

      I call confirmUserSelfRegistration method like that:





      username@mail.com
      example-code-in-IS











      Any idea?

      Thanks,
      Sergio.

      ReplyDelete
      Replies
      1. Sorry, here you are the request: http://pastebin.com/aDR0hq3P

        Delete
      2. Can you remove,







        and try again. Also give the response to look at what has happened.

        Delete
      3. Can you remove the captcha xml tags!

        Delete
    2. org.wso2.carbon.identity.mgt.stub.UserInformationRecoveryServiceIdentityMgtServiceExceptionException, is not providing any error messaging at all. If an already existing user is added to the system, this exception is thrown and it does not provide any error message whether it was due to the user already exists or missing parmaters etc..

      Appreciate any help here..

      --Venkata

      ReplyDelete
      Replies
      1. I am also a user of WSO2 IS. I had the same issue. Better you report this to there dev mailing list.I think that will be a good improvement suggestion.

        Delete