WSO2 Identity server provides the capability to recover user accounts with the help of security questions. This feature is important when we forget the password and need some evidence that the actual user who had forgotton the password is indeed the right owner of the account.
There are three such ways in which we can set these challenge questions for the users. You can read more details about each of these methods in this documentation.
<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">
<soapenv:Header/>
<soapenv:Body>
<ser:setChallengeQuestionsOfUser>
<ser:userName>shenavi</ser:userName>
<!--Zero or more repetitions:-->
<ser:challengesDTOs>
<xsd:answer>pasta</xsd:answer>
<xsd:id>http://wso2.org/claims/challengeQuestion1</xsd:id>
<xsd:order>0</xsd:order>
<xsd:primary>false</xsd:primary>
<xsd:question>Favorite food ?</xsd:question>
<xsd:verfied>false</xsd:verfied>
</ser:challengesDTOs>
<ser:challengesDTOs>
<xsd:answer>cricket</xsd:answer>
<xsd:id>http://wso2.org/claims/challengeQuestion2</xsd:id>
<xsd:order>1</xsd:order>
<xsd:primary>false</xsd:primary>
<xsd:question>Favorite sport ?</xsd:question>
<xsd:verfied>false</xsd:verfied>
</ser:challengesDTOs>
There are three such ways in which we can set these challenge questions for the users. You can read more details about each of these methods in this documentation.
- From the UserIdentityManagementAdminService SOAP API
- By manually creating registry resources for questions
- From Identity Server Management Console
In this blog I will be guiding you through how to use the UserIdentityManagementAdminService SOAP API to set the challenge questions. But we will be doing this with a slight twist. We are going to set these questions using an API created in the WSO2 ESB with the help of some custom sequences.
For the setting of the challenge questions using the admin service this is the soap message which is expected by the IS as the request payload.
<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">
<soapenv:Header/>
<soapenv:Body>
<ser:setChallengeQuestionsOfUser>
<ser:userName>shenavi</ser:userName>
<!--Zero or more repetitions:-->
<ser:challengesDTOs>
<xsd:answer>pasta</xsd:answer>
<xsd:id>http://wso2.org/claims/challengeQuestion1</xsd:id>
<xsd:order>0</xsd:order>
<xsd:primary>false</xsd:primary>
<xsd:question>Favorite food ?</xsd:question>
<xsd:verfied>false</xsd:verfied>
</ser:challengesDTOs>
<ser:challengesDTOs>
<xsd:answer>cricket</xsd:answer>
<xsd:id>http://wso2.org/claims/challengeQuestion2</xsd:id>
<xsd:order>1</xsd:order>
<xsd:primary>false</xsd:primary>
<xsd:question>Favorite sport ?</xsd:question>
<xsd:verfied>false</xsd:verfied>
</ser:challengesDTOs>
</ser:setChallengeQuestionsOfUser>
</soapenv:Body>
</soapenv:Envelope>
<sequence name="ForEachSequence" xmlns="http://ws.apache.org/ns/synapse">
<foreach expression="//challengesDTOArray/challengesDTOs"
id="testtForEach" xmlns:ns="http://org.apache.synapse/xsd">
<sequence>
<payloadFactory media-type="xml">
<format>
<ser:challengesDTOs
xmlns:ser="http://services.mgt.identity.carbon.wso2.org" xmlns:xsd="http://dto.mgt.identity.carbon.wso2.org/xsd">
<xsd:answer>$1</xsd:answer>
<xsd:error>$2</xsd:error>
<xsd:id>$3</xsd:id>
<xsd:key>$4</xsd:key>
<xsd:order>$5</xsd:order>
<xsd:primary>$6</xsd:primary>
<xsd:question>$7</xsd:question>
<xsd:verfied>$8</xsd:verfied>
</ser:challengesDTOs>
</format>
<args>
<arg evaluator="json" expression="$.challengesDTOs.answer"/>
<arg evaluator="json" expression="$.challengesDTOs.error"/>
<arg evaluator="json" expression="$.challengesDTOs.id"/>
<arg evaluator="json" expression="$.challengesDTOs.key"/>
<arg evaluator="json" expression="$.challengesDTOs.order"/>
<arg evaluator="json" expression="$.challengesDTOs.primary"/>
<arg evaluator="json" expression="$.challengesDTOs.question"/>
<arg evaluator="json" expression="$.challengesDTOs.verfied"/>
</args>
</payloadFactory>
</sequence>
</foreach>
<log level="full"/>
</sequence>
<sequence name="sendInSequence" xmlns="http://ws.apache.org/ns/synapse">
<sequence key="ForEachSequence"/>
<payloadFactory media-type="xml">
<format>
<soapenv:Envelope
xmlns:ser="http://services.mgt.identity.carbon.wso2.org"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://dto.mgt.identity.carbon.wso2.org/xsd">
<soapenv:Header/>
<soapenv:Body>
<ser:setChallengeQuestionsOfUser>
<ser:userName>$1</ser:userName>
<!--Zero or more repetitions:-->
$2
</ser:setChallengeQuestionsOfUser>
</soapenv:Body>
</soapenv:Envelope>
</format>
<args>
<arg evaluator="xml" expression="//jsonObject/userName" xmlns:ns="http://org.apache.synapse/xsd"/>
<arg evaluator="xml"
expression="//jsonObject/challengesDTOArray"
xmlns:ns="http://org.apache.synapse/xsd" xmlns:ser="http://services.mgt.identity.carbon.wso2.org"/>
</args>
</payloadFactory>
<xslt key="transformPayload"/>
<log level="full"/>
<header name="SOAPAction" scope="transport" value="urn:setChallengeQuestionsOfUser"/>
<property name="messageType" scope="axis2" type="STRING" value="application/soap+xml"/>
<property
expression="fn:concat('Basic ', base64Encode('admin:admin'))"
name="Authorization" scope="transport" xmlns:ns="http://org.apache.synapse/xsd"/>
<send>
<endpoint>
<address format="soap12" trace="disable" uri="https://localhost:9443/services/UserIdentityManagementAdminService"/>
</endpoint>
</send>
</sequence>
7. Next create a simple out sequence and name it as sendOutSequence and add the contents below to it.
<sequence name="sendOutSequence" xmlns="http://ws.apache.org/ns/synapse">
<log level="custom">
<property name="OutSequence" value="*** In the out sequence ***"/>
</log>
<send/>
</sequence>
9. Now in order to test the scenario set up the claims as per the documentation [1] and create a user as "testUser" in the identity server.
10. Save the payload below in a file and name is as sampleRequest.txt.
</soapenv:Body>
</soapenv:Envelope>
And this is the soap payload which is returned on success of setting the challenge questions
3. Start both the ESB and the IS servers
4. In the esb create a local entry [1] with an inline xml and add the contents below to it and save it. Mention the name of the local entry key as transformPayload. You can follow this link on how to add a local entry
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//challengesDTOArray">
<xsl:copy-of select="@*|node()"/>
</xsl:template>
</xsl:stylesheet>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Body> <ns:setChallengeQuestionsOfUserResponsexmlns:ns="http://services.mgt.identity.carbon.wso2.org"> <ns:return xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/> </ns:setChallengeQuestionsOfUserResponse> </soapenv:Body> </soapenv:Envelope> |
If you need to set the challenge questions of a particular user by passiing the set of questions and the input parameters as a JSON string and need to write some transformation logic so that it constructs the needed payload then you will need to do a message transformation in order to get it done.
This is the information which we have about the user. And the answers to the challenge questions as a JSON string. Let's see how we can pass this data and convert it to a format in which it will send the request to the UserIdentityManagementAdminService and set the challenge questions for the user.
{ "userName": "testUser", "challengesDTOArray":{ "challengesDTOs": [{ "answer": "pasta", "error": "false", "id": "http://wso2.org/claims/challengeQuestion1", "key": "true", "order": "0", "primary": "false", "question": "Favorite food ?", "verfied": "false" }, { "answer": "cricket", "error": "false", "id": "http://wso2.org/claims/challengeQuestion2", "key": "true", "order": "1", "primary": "false", "question": "Favorite sport ?", "verfied": "false" } ] } }
Now that you have an idea of what we need to achieve let's take a look at how it can be done.For this tutorial I will be using the IS 5.1.0 version and the ESB 4.9.0 but this feature is supported for older versions as well. You can download these products from http://wso2.com/products/identity-server/ and http://wso2.com/products/enterprise-service-bus/.
So let's get started.
So let's get started.
Steps
1. Let's name the IS distribution as IS_HOME and the esb distribution as ESB_HOME. Set the port offset of the ESB server to 1.
2. You must set the following property to false in the <IS_HOME>/repository/conf/carbon.xml file to view the WSDLs of the services.
<
HideAdminServiceWSDLs
>false</
HideAdminServiceWSDLs
>
3. Start both the ESB and the IS servers
4. In the esb create a local entry [1] with an inline xml and add the contents below to it and save it. Mention the name of the local entry key as transformPayload. You can follow this link on how to add a local entry
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//challengesDTOArray">
<xsl:copy-of select="@*|node()"/>
</xsl:template>
</xsl:stylesheet>
5. Create a new sequence in the ESB and name it as ForEachSequence. This sequence would iterate through the provided json payload above and construct the challengesDTOs elements iteratively. The following is the contents of the sequence ForEachSequence.xml.
<sequence name="ForEachSequence" xmlns="http://ws.apache.org/ns/synapse">
<foreach expression="//challengesDTOArray/challengesDTOs"
id="testtForEach" xmlns:ns="http://org.apache.synapse/xsd">
<sequence>
<payloadFactory media-type="xml">
<format>
<ser:challengesDTOs
xmlns:ser="http://services.mgt.identity.carbon.wso2.org" xmlns:xsd="http://dto.mgt.identity.carbon.wso2.org/xsd">
<xsd:answer>$1</xsd:answer>
<xsd:error>$2</xsd:error>
<xsd:id>$3</xsd:id>
<xsd:key>$4</xsd:key>
<xsd:order>$5</xsd:order>
<xsd:primary>$6</xsd:primary>
<xsd:question>$7</xsd:question>
<xsd:verfied>$8</xsd:verfied>
</ser:challengesDTOs>
</format>
<args>
<arg evaluator="json" expression="$.challengesDTOs.answer"/>
<arg evaluator="json" expression="$.challengesDTOs.error"/>
<arg evaluator="json" expression="$.challengesDTOs.id"/>
<arg evaluator="json" expression="$.challengesDTOs.key"/>
<arg evaluator="json" expression="$.challengesDTOs.order"/>
<arg evaluator="json" expression="$.challengesDTOs.primary"/>
<arg evaluator="json" expression="$.challengesDTOs.question"/>
<arg evaluator="json" expression="$.challengesDTOs.verfied"/>
</args>
</payloadFactory>
</sequence>
</foreach>
<log level="full"/>
</sequence>
6. Create another sequence which would be the in sequence we would be using for the API. Name the sequence as sendInSequence and add the contents of the in sequence as mentioned below. This in sequence will call the ForEachSequence within it and then construct the final payload format needed to be sent to the identity server to set the user's challenge questions. Note that from this sequences we are calling the service https://localhost:9443/services/UserIdentityManagementAdminService which will accept and set these challenge questions.
<sequence name="sendInSequence" xmlns="http://ws.apache.org/ns/synapse">
<sequence key="ForEachSequence"/>
<payloadFactory media-type="xml">
<format>
<soapenv:Envelope
xmlns:ser="http://services.mgt.identity.carbon.wso2.org"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://dto.mgt.identity.carbon.wso2.org/xsd">
<soapenv:Header/>
<soapenv:Body>
<ser:setChallengeQuestionsOfUser>
<ser:userName>$1</ser:userName>
<!--Zero or more repetitions:-->
$2
</ser:setChallengeQuestionsOfUser>
</soapenv:Body>
</soapenv:Envelope>
</format>
<args>
<arg evaluator="xml" expression="//jsonObject/userName" xmlns:ns="http://org.apache.synapse/xsd"/>
<arg evaluator="xml"
expression="//jsonObject/challengesDTOArray"
xmlns:ns="http://org.apache.synapse/xsd" xmlns:ser="http://services.mgt.identity.carbon.wso2.org"/>
</args>
</payloadFactory>
<xslt key="transformPayload"/>
<log level="full"/>
<header name="SOAPAction" scope="transport" value="urn:setChallengeQuestionsOfUser"/>
<property name="messageType" scope="axis2" type="STRING" value="application/soap+xml"/>
<property
expression="fn:concat('Basic ', base64Encode('admin:admin'))"
name="Authorization" scope="transport" xmlns:ns="http://org.apache.synapse/xsd"/>
<send>
<endpoint>
<address format="soap12" trace="disable" uri="https://localhost:9443/services/UserIdentityManagementAdminService"/>
</endpoint>
</send>
</sequence>
<sequence name="sendOutSequence" xmlns="http://ws.apache.org/ns/synapse">
<log level="custom">
<property name="OutSequence" value="*** In the out sequence ***"/>
</log>
<send/>
</sequence>
Now that we have created the needed sequences to carry out this task. Lets create an API so we can invoke this API and create the recovery questions in the IS.
8. Create an API with a URI template of "/*" and with the http POST method and name it as ChallengeQuestionsAPI. The contents of the API is included below.
<api xmlns="http://ws.apache.org/ns/synapse" name="ChallengeQuestionsAPI" context="/info">
<resource methods="POST" uri-template="/*" inSequence="sendInSequence" outSequence="sendOutSequence"/>
</api>
{ "userName": "testUser", "challengesDTOArray":{ "challengesDTOs": [{ "answer": "pasta", "error": "false", "id": "http://wso2.org/claims/challengeQuestion1", "key": "true", "order": "0", "primary": "false", "question": "Favorite food ?", "verfied": "false" }, { "answer": "cricket", "error": "false", "id": "http://wso2.org/claims/challengeQuestion2", "key": "true", "order": "1", "primary": "false", "question": "Favorite sport ?", "verfied": "false" } ] } }
11. Then you can invoke the API using the command below and observe that the recovery questions have been updated for the user testUser created before.
curl -k -X POST --header 'Content-Type: application/json' -d '@/(path_to_file)/sampleRequest.txt' -k -v http://172.17.0.1:8281/info
12. Next in order to verify this has been created lets log in with the new user to the identity dashboard using this link. And select the 'Account Recovery' option. You will see the challenge questions has been set successfully.
We have successfully set the challenge questions of the User in the identity server using an API created from ESB. Hope this post was useful to you.
References.
[1] https://docs.wso2.com/display/IS510/Password+Recovery#PasswordRecovery-Recoveryusingchallengequestions
[2] https://docs.wso2.com/display/ESB490/Adding+a+Local+Entry
[1] https://docs.wso2.com/display/IS510/Password+Recovery#PasswordRecovery-Recoveryusingchallengequestions
[2] https://docs.wso2.com/display/ESB490/Adding+a+Local+Entry
Comments
Post a Comment