Tuesday, October 20, 2015

Recover Accounts in WSO2 Identity Server When Username is Forgotten

When you use WSO2 Identity Server for managing user accounts, there can be situations where you need to know the username of an where you have forgotten the username. In such cases, you can use the ‘Account ID Recovery’ [1] flow of UserInformationRecoveryService [2] API. This example provides a step by step guide for setting up this feature and to use it for recovering the username of an account. For the demonstration, WSO2 Identity Server 5.0.0 is used with Service Pack 1 installed.

First step is to set the following properties in IS_HOME/repository/conf/security/identity­-mgt.properties file.

Identity.Listener.Enable=true
Notification.Sending.Internally.Managed=true
Captcha.Verification.Internally.Managed=false

The Identity.Listener.Enable is used to enable the Identity Management Event Listener which is used to perform User Information Recovery flows. Notification.Sending.Internally.Managed property decides whether to send notifications (i.e emails) from Identity Server itself or not. In this example, I am configuring the Identity Server to send emails. Captcha.Verification.Internally.Managed property is used to decide whether to enable captcha verification for identifying the user as a human. For this demonstration this value is set to false for demonstration purposes. If this is set to true, [3] can be followed to know more information.

Next step is to configure the email account details that is used for sending emails from Identity Server. For that we need to add the configuration in IS_HOME/repository/conf/axis2/axis2.xml file. Here I am using a gmail account for sending emails. Given below is the configuration added to axis2.xml file. If your email provider is different, you can set the configurations accordingly.

    <transportSender name="mailto"
                    class="org.apache.axis2.transport.mail.MailTransportSender">
       <parameter name="mail.smtp.from">[email protected]</parameter>
       <parameter name="mail.smtp.user">[email protected]</parameter>
       <parameter name="mail.smtp.password">mypassword</parameter>
       <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>
    </transportSender>

Once these configurations are set, start the Identity Server. If the server is already running, restart.

Login to the Management Console of Identity Server.

Next step is to configure the ‘Account Id Recovery’ email template which is the template used to generate the email sent at the time of username recovery. Go to Configure -> Email Templates in Management Console and select the ‘Account Id Recovery’ template.

In the email template, you can add placeholders which will be replaced by the user account attributes when sending the email. Here the important placeholder to use is the {user-name} which will be replaced by the username of the user who is calling the recovery flow. So when the email is received, the user can get to know about the username.

Now all the configurations are correctly set. Next step is to test the username recovery flow. For that here I am creating a new user with name ‘tharindu’.

For demonstrating purpose, I’m assigning the ‘admin’ role for this user, however it is not related to testing this feature.

Once the user is created, go to the ‘User Profile’ of the user.

You can fill the attributes of the user and save the profile. Here the most important attribute is the email address because when we try to recover the username of the account later, an email will be sent to the email address we given in the profile of the user.

In this ‘Account Id Recovery’ flow, we need following three operations in UserInformationRecoveryService API.

  1. getUserIdentitySupportedClaims()
  2. getCaptcha()
  3. verifyAccount()

Here, by calling getUserIdentitySupportedClaims() method, we can retrieve all the claims that are appearing in the user profile (claims that are marked as ‘supported by default’ are appearing in the user profile. More information in [4]).
This is a sample SOAP request for calling the getUserIdentitySupportedClaims() method. Here, as the claim dialect, we need to send http://wso2.org/claims.

<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>

As the response, we receive following.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
     <ns:getUserIdentitySupportedClaimsResponse xmlns:ns="http://services.mgt.identity.carbon.wso2.org" xmlns:ax2293="http://mgt.identity.carbon.wso2.org/xsd" xmlns:ax2295="http://beans.mgt.captcha.carbon.wso2.org/xsd" xmlns:ax2297="http://beans.mgt.identity.carbon.wso2.org/xsd" xmlns:ax2301="http://base.identity.carbon.wso2.org/xsd" xmlns:ax2298="http://dto.mgt.identity.carbon.wso2.org/xsd">
        <ns:return xsi:type="ax2298:UserIdentityClaimDTO" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
           <ax2298:claimUri>http://wso2.org/claims/telephone</ax2298:claimUri>
           <ax2298:claimValue xsi:nil="true"/>
        </ns:return>
        <ns:return xsi:type="ax2298:UserIdentityClaimDTO" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
           <ax2298:claimUri>http://wso2.org/claims/mobile</ax2298:claimUri>
           <ax2298:claimValue xsi:nil="true"/>
        </ns:return>
        <ns:return xsi:type="ax2298:UserIdentityClaimDTO" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
           <ax2298:claimUri>http://wso2.org/claims/country</ax2298:claimUri>
           <ax2298:claimValue xsi:nil="true"/>
        </ns:return>
        <ns:return xsi:type="ax2298:UserIdentityClaimDTO" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
           <ax2298:claimUri>http://wso2.org/claims/streetaddress</ax2298:claimUri>
           <ax2298:claimValue xsi:nil="true"/>
        </ns:return>
        <ns:return xsi:type="ax2298:UserIdentityClaimDTO" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
           <ax2298:claimUri>http://wso2.org/claims/url</ax2298:claimUri>
           <ax2298:claimValue xsi:nil="true"/>
        </ns:return>
        <ns:return xsi:type="ax2298:UserIdentityClaimDTO" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
           <ax2298:claimUri>http://wso2.org/claims/givenname</ax2298:claimUri>
           <ax2298:claimValue xsi:nil="true"/>
        </ns:return>
        <ns:return xsi:type="ax2298:UserIdentityClaimDTO" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
           <ax2298:claimUri>http://wso2.org/claims/emailaddress</ax2298:claimUri>
           <ax2298:claimValue xsi:nil="true"/>
        </ns:return>
        <ns:return xsi:type="ax2298:UserIdentityClaimDTO" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
           <ax2298:claimUri>http://wso2.org/claims/im</ax2298:claimUri>
           <ax2298:claimValue xsi:nil="true"/>
        </ns:return>
        <ns:return xsi:type="ax2298:UserIdentityClaimDTO" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
           <ax2298:claimUri>http://wso2.org/claims/organization</ax2298:claimUri>
           <ax2298:claimValue xsi:nil="true"/>
        </ns:return>
        <ns:return xsi:type="ax2298:UserIdentityClaimDTO" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
           <ax2298:claimUri>http://wso2.org/claims/lastname</ax2298:claimUri>
           <ax2298:claimValue xsi:nil="true"/>
        </ns:return>
     </ns:getUserIdentitySupportedClaimsResponse>
  </soapenv:Body>
</soapenv:Envelope>

From the above SOAP response, we can get to know all the claims that are supported in the user profile. We can select claim Uris from above and send later when we verify the user account.

The next step in the flow is getCaptcha() method which is needed only if we set the Captcha.Verification.Internally.Managed property value to true in identity-mgt.properties file. If you have enabled captcha verification, you can refer [5] to know more information.

Next step is to call the verifyAccount() method. In the request we need to specify the tenant domain of the user so that the user will be searched in this tenant domain. Here I am giving the value as carbon.super as the user is in the super tenant. If the user is in a different tenant, we can specify the particular tenant domain name here. Also for finding the exact user, we need to provide one or more claims where in user profiles, Identity Server will filter the particular user account based on the claims we provide here. Here I am giving only the http://wso2.org/claims/emailaddress claim because normally for user accounts, email address is unique.

Here is a sample SOAP request when captcha verification is not enabled.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.mgt.identity.carbon.wso2.org" xmlns:xsd="http://dto.mgt.identity.carbon.wso2.org/xsd" xmlns:xsd1="http://beans.mgt.captcha.carbon.wso2.org/xsd">
  <soapenv:Header/>
  <soapenv:Body>
     <ser:verifyAccount>
        <!--One or more repetitions:-->
        <ser:claims>
           <xsd:claimUri>http://wso2.org/claims/emailaddress</xsd:claimUri>
           <xsd:claimValue>[email protected]</xsd:claimValue>
        </ser:claims>

        <ser:tenantDomain>carbon.super</ser:tenantDomain>
     </ser:verifyAccount>
  </soapenv:Body>
</soapenv:Envelope>

If captcha verification is enabled, the SOAP request would be like below where it contains the captcha details as well.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.mgt.identity.carbon.wso2.org" xmlns:xsd="http://dto.mgt.identity.carbon.wso2.org/xsd" xmlns:xsd1="http://beans.mgt.captcha.carbon.wso2.org/xsd">
  <soapenv:Header/>
  <soapenv:Body>
     <ser:verifyAccount>
        <!One or more repetitions:-->
        <ser:claims>
           <xsd:claimUri>http://wso2.org/claims/emailaddress</xsd:claimUri>
           <xsd:claimValue>[email protected]</xsd:claimValue>
        </ser:claims>

        <ser:captcha> 
<xsd1:imagePath>registry/resource/_system/config/repository/components/org.wso2.carbon.captcha-images/dc832c96-3ed2-45e2-adfe-fcb0ef341ce3.jpg</xsd1:imagePath>
           <xsd1:secretKey>dc832c96-3ed2-45e2-adfe-fcb0ef341ce3</xsd1:secretKey>
           <xsd1:userAnswer>8dy54</xsd1:userAnswer>
        </ser:captcha>

        <ser:tenantDomain>carbon.super</ser:tenantDomain>
     </ser:verifyAccount>
  </soapenv:Body>
</soapenv:Envelope>

Upon successful verification, we get the following response. as the value for verified, we will receive true.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
     <ns:verifyAccountResponse xmlns:ns="http://services.mgt.identity.carbon.wso2.org">
        <ns:return xsi:type="ax2297:VerificationBean" xmlns:ax2293="http://mgt.identity.carbon.wso2.org/xsd" xmlns:ax2295="http://beans.mgt.captcha.carbon.wso2.org/xsd" xmlns:ax2297="http://beans.mgt.identity.carbon.wso2.org/xsd" xmlns:ax2298="http://dto.mgt.identity.carbon.wso2.org/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ax2301="http://base.identity.carbon.wso2.org/xsd">
           <ax2297:error xsi:nil="true"/>
           <ax2297:key xsi:nil="true"/>
           <ax2297:notificationData xsi:nil="true"/>
           <ax2297:redirectPath xsi:nil="true"/>
           <ax2297:userId xsi:nil="true"/>
           <ax2297:verified>true</ax2297:verified>
        </ns:return>
     </ns:verifyAccountResponse>
  </soapenv:Body>
</soapenv:Envelope>

When we check the inbox of the email account given for the user, I can see the email is correctly received according to the template defined. From the body of the email, I can find the username associated with the account.



Notes :

In this example, for verifying the account, I used the email address claim (http://wso2.org/claims/emailaddress) . But if there are more than one user account having the same email address (or the same value for the claim you are using for verifying the account), you will receive the response as below because Identity Server cannot uniquely identify the user.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
     <ns:verifyAccountResponse xmlns:ns="http://services.mgt.identity.carbon.wso2.org">
        <ns:return xsi:type="ax2295:VerificationBean" xmlns:ax2293="http://mgt.identity.carbon.wso2.org/xsd" xmlns:ax2295="http://beans.mgt.identity.carbon.wso2.org/xsd" xmlns:ax2296="http://dto.mgt.identity.carbon.wso2.org/xsd" xmlns:ax2299="http://beans.mgt.captcha.carbon.wso2.org/xsd" xmlns:ax2302="http://base.identity.carbon.wso2.org/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
           <ax2295:error>User not found</ax2295:error>
           <ax2295:key xsi:nil="true"/>
           <ax2295:notificationData xsi:nil="true"/>
           <ax2295:redirectPath xsi:nil="true"/>
           <ax2295:userId xsi:nil="true"/>
           <ax2295:verified>false</ax2295:verified>
        </ns:return>
     </ns:verifyAccountResponse>
  </soapenv:Body>
</soapenv:Envelope>

However in such case, you can use some other claim where no multiple users have the same value. For example assume we have three users as following.

user1 {email = [email protected], mobile=11111111, country=USA}
user2 {email = [email protected], mobile=22222222, country=LK}
user3 {email = [email protected], mobile=33333333, country=UK}

Here the mobile number is unique for users but there can be users with same email address. Here we can use the claim associated with the mobile number (i.e http://wso2.org/claims/mobile ) for verifying the account.

To be more specific, you can add a new claim like ‘Employee Identification Number (EIN)’ which cannot have duplicate values for multiple users and use this claim for verifying the user account.

You can even use multiple claims for verifying the user account. If we consider above 3 users, both user1 and user2 have same email addresses, but their country is different. So if we use both the email and country claims together, we can verify the account correctly. Here is an example for the SOAP request.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://services.mgt.identity.carbon.wso2.org" xmlns:xsd="http://dto.mgt.identity.carbon.wso2.org/xsd" xmlns:xsd1="http://beans.mgt.captcha.carbon.wso2.org/xsd">
  <soapenv:Header/>
  <soapenv:Body>
     <ser:verifyAccount>
        <!--One or more repetitions:-->
        <ser:claims>
           <xsd:claimUri>http://wso2.org/claims/emailaddress</xsd:claimUri>
           <xsd:claimValue>[email protected]</xsd:claimValue>
        </ser:claims>
        <ser:claims>
           <xsd:claimUri>http://wso2.org/claims/country</xsd:claimUri>
           <xsd:claimValue>USA</xsd:claimValue>
        </ser:claims>

        <ser:tenantDomain>carbon.super</ser:tenantDomain>
     </ser:verifyAccount>
  </soapenv:Body>
</soapenv:Envelope>

Since no multiple users have the same values for this combination of claims, Identity Server can identify the user uniquely and verify the account.



References



Tharindu Edirisinghe
Identity Server Team
WSO2

No comments:

Post a Comment