The Challenge
Oftentimes in the world of Dynamics CRM, the need arises for non-CRM users to gain access to CRM data. A common solution is the implementation of a web portal which allows these users to perform certain actions on applicable CRM data. Microsoft offers two portal solutions: the Customer Portal and the Partner Relationship Management Portal. These are managed solutions, can be easily imported into any CRM environment, and come with detailed instructions on how to quickly and efficiently deploy.
However, like any construct that is general in its scope, the aforementioned portal solutions do have some caveats. The two solutions install extraneous features onto CRM that are not necessary for portal development, often confusing CRM users and requiring the need to go back and hide all of the excessive customizations. Furthermore, by default, the two portals both use Windows Live for authentication (requiring users without Windows Live to register for an account for authentication) and are hard to style (more often than not, a portal page should have consistent styling with the rest of a website’s pages).
The Solution
With the lack of modularity and abundance of unrequired features from the Customer Portal and the Partner Relationship Management Portal, I sought a more customizable approach in developing portals for our clients.
Without further ado, here are some of the cool things that you will be able to do after reading this blog post:
- Use the Portal Controls to create a form that on submit will create a new record in CRM.
- Allow for form-based authentication against a password field in CRM.
- Allow for Read and Update Privileges on a form after authentication.
Getting Started:
You will need:
- Microsoft Visual Studio with .NET Framework 4 (In my example I will be using C#).
- Microsoft Dynamics CRM 2011/2013 SDK
- CRM 2011/2013 Organization (In this example I will be using CRM 2011/2013 Online)
Included with the blog post is a copy of the example solution which will contain the source code for my project. Download it here.
- Create a new ASP.NET Empty Web Application with .NET Framework 4 as the target framework and Visual C# as the Type for your project:
- Right click the project and add references to the following dll’s:
- Microsoft.Xrm.Sdk.dll
- Microsoft.Xrm.Portal.dll
- Microsoft.Xrm.Client.dll
- System.Runtime.Serialization.dll
- System.Data.Services.dll
- System.Data.Services.Client.dll
- You will need to use the CrmSvcUtil.exe (located in the CRM 2011 SDK, so if you do not have a copy of the SDK, make sure to download it) to generate the early bound types for CRM. The portal will need early bound types with the Microsoft.Xrm.Client.CodeGeneration.dll extension.
- From the command prompt, navigate to the sdk/bin/ folder where the CrmSvcUtil is located, and run the following command, replacing the url, domain (if on premise), username, and password.
CrmSvcUtil.exe /codeCustomization:”Microsoft.Xrm.Client.CodeGeneration.CodeCustomization, Microsoft.Xrm.Client.CodeGeneration” /out:Xrm.cs /url:http://Crm/Contoso/XRMServices/2011/Organization.svc /domain:CONTOSO /username:administrator /password:pass@word1 /namespace:Xrm /serviceContextName:XrmServiceContext /serviceContextPrefix:
- This command generates the C# source file Xrm.cs in the sdk/bin/ folder.
- For more information on early bound types, check out this article
- From the command prompt, navigate to the sdk/bin/ folder where the CrmSvcUtil is located, and run the following command, replacing the url, domain (if on premise), username, and password.
- Right click the csproject (in my example, the csproject is called BlogWebPortal) and navigate to Add, then to Existing Item. Select the Xrm.cs file that you had previously generated and your Solution Explorer should look like this:
-
To be able to use the Microsoft Dynamics CRM platform and its different Web Services from the portal, you will need to use a valid, enabled CRM user’s credentials for the connection string in the Web.config file.
a. As a best practice for security reasons, this user should have the minimum amount of privileges in CRM. In this example, the portal will only be dealing with creating new leads and updating existing leads. So, in your organization, you should create a new user with a new security role whose sole purpose is to serve the portal. While you can get away with using any user with the minimum set of privileges, it is not a best practice and could compromise the security of your environment.
b. Therefore, in this example, I have created a CRM user—Portal User—whose security role has only the privileges shown below, and this user will be the one used to authenticate against CRM.
c. In addition to the security settings shown above, the security role that the Portal User is assigned to should have the read privilege enabled on the customization tab for Attribute
Map, Entity, Field, Relationship, Service Endpoint, and View otherwise the portal will err out:
Use the Portal Controls to create a form that on submit will create a new Lead record in CRM:
- The Web.config file should have the minimum configuration; and make sure to replace the connection string with the connection string for your organization: <configuration> <configSections> <section name=“microsoft.xrm.client“type=“Microsoft.Xrm.Client.Configuration.CrmSection, Microsoft.Xrm.Client“/> </configSections> <connectionStrings> <add name=“PortalDemo“connectionString=“Url=https://blog2013demo.crm.dynamics.com; Username=portaluser@blog2013demo.onmicrosoft.com; Password=pass@word1“/> </connectionStrings> <microsoft.xrm.client> <contexts> <add name=“PortalDemo“type=“Xrm.XrmServiceContext“ /> </contexts> </microsoft.xrm.client> <system.web> <compilationdebug=“true“targetFramework=“4.0“ /> <pages> <controls> <add tagPrefix=“crm“namespace=“Microsoft.Xrm.Portal.Web.UI.WebControls“ assembly=“Microsoft.Xrm.Portal“ /> </controls> </pages> </system.web> </configuration>
- Create a view in CRM for Leads that will specify the fields that will show up on the portal form. The fields will be ordered in the same sequenced on the portal as they are in CRM
- CRM Lead View (system view named Portal View)
- Portal Form
- Right click on your project. Navigate to Add New and then add a blank aspx page called CreateLead.aspx to the project. Your Solution Explorer should look like this:
- On the aspx web page (CreateLead.aspx), you will add two custom controls that have been made available by including the Microsoft.Xrm.Portal.dll as a reference, a CrmDataSource and a CrmEntityFormView that uses the data source. Your resulting aspx code will look something like this:
<html xmlns=”http://www.w3.org/1999/xhtml”> <head runat=”server”> <title></title> </head> <body> <form id=”form1″ runat=”server”> <div> <crm:CrmDataSource ID=”WebFormDataSource” runat=”server”/> <crm:CrmEntityFormView runat=”server” DataSourceID=”WebFormDataSource” ID=”NewLeadView” EntityName=”lead” SavedQueryName=”Portal View” ValidationGroup=”validators”> </crm:CrmEntityFormView> </div> </form> </body> </html>
- Crm:CrmDataSource is a custom control included as a reference from the Microsoft.Xrm.Portal.dll, so add a reference to the controls on the codebehind file for your aspx page—using Microsoft.Xrm.Portal.Web.UI.WebControls;.
- Build first, then navigate to and browse your new form (CreateLead.aspx), fill out the information and submit, and voila! A new lead is created in CRM!
Portal Form:
New Lead is Created in CRM (after Submit button click):
Allow for form-based authentication against a password field in CRM:
- At a high level, form-based authentication is the notion of checking against a username and password to implement security on certain items. In terms of the portal and CRM, you want to assign a username and password to non-CRM users, so that they can log in, read and update their information (covered later on in this blog post).
- For authentication, you will need a username field along with a password field. In this example, since I am dealing only with leads, I can simply create a new username field and a new password field for the lead record. So create new fields for username in password as Single Line of Text for the lead:
- To allow for form-based authentication in an ASP.net application, it is necessary then, to explicitly state in the Web.config that this is the case. Under the system.web node, add the following:
<authentication mode=“Forms“> <forms name=“.ASPXAUTH” loginUrl=“Login.aspx“ protection=“All” timeout=“30” path=“/“ requireSSL=“false” slidingExpiration=“true“ cookieless=“UseDeviceProfile” domain=“” enableCrossAppRedirects=“false“> </forms> </authentication> <authorization> <deny users=“?“/> <allow users=“*“/> </authorization>
- The important bit for the snippet from the Web.config file above indicates that loginUrl=“Login.aspx”. Also, the authorization node indicates that all users will be denied access unless they have passed the form-based authentication. Therefore, it is also necessary to allow users access to the existing page (CreateLead.aspx). To do this, you simply add this snippet after the system.web node:
<location path=“CreateLead.aspx“> <system.web> <authorization> <allow users=“*“/> </authorization> </system.web> </location>
**As a reference, you can always examine the Web.config included in the project.
- Create a new empty web form called Login.aspx (in the same manner as the CreateLead.aspx page). Your Solution Explorer should look something like this:
- Add an ASP login control on the Login.aspx page:
<form id=”form1″ runat=”server”> <div> <asp:Login ID=”Login1″ runat=”server” OnAuthenticate=”Login1_Authenticate”> </asp:Login> </div> </form>
- Since you have an OnAuthenticate event (Login1_Authenticate) specified for theLogin control, add a method called Login1_Authenticate to the C# code-behind page for the Login.aspx.cs:
protected void Login1_Authenticate(object sender, System.Web.UI.WebControls.AuthenticateEventArgs e) { }
- Now, basically, you want to authenticate against the new fields that you created in CRM, username and password. As those fields were newly added, you will need to regenerate the Xrm.cs file using the CrmSvcUtil tool (Step 3 of Getting Started) which is one caveat of early-bound types, but that is a topic for another day.
CrmSvcUtil.exe /codeCustomization:”Microsoft.Xrm.Client.CodeGeneration.CodeCustomization, Microsoft.Xrm.Client.CodeGeneration” /out:Xrm.cs /url:http://Crm/Contoso/XRMServices/2011/Organization.svc /domain:CONTOSO /username:administrator /password:pass@word1 /namespace:Xrm /serviceContextName:XrmServiceContext /serviceContextPrefix:Xrm
- You need an instance of an IOrganizationService interface to be able to use the CRM Web Services, so the quickstart example from the CRM 2011 SDK contains some methods that will easily allow us to instantiate an OrganizationService which is simply a wrapper class around the IOrganizationService class. Also, you will need to instantiate the OrganizationService across different pages so take this time to create a class called PortalPage which inherits from the aspx page (public class PortalPage : System.Web.UI.Page) which will contain two methods from the quickstart example—GetServiceConfiguration() and isValidConnectionString(). Reference the PortalPage.cs file from the included solution for specific source code. Your Solution Explorer should now look something like this:
- Now, navigate back to Login.aspx and modify it to inherit from the PortalPage so that it can use the methods that were added from quickstart. Also you will need to add using directives to some CRM 2011 SDK references along with System.Configuration:
- Create a function validateLead which will be triggered On Authenticate of the ASP.net Login control and fetch the username and password to see if it exists or not. For simplicity, we will assume that the combination of a Username and Password is unique. validateLead will fetch the Username and Password from the Lead table and if you return a result then set the event Authenticated flag to true, otherwise set it to false. Reference the Login.aspx.cs file from the included solution for specific source code. There exist two validateLead functions in the source code, the commented out version refers to this version while the uncommented function is used in the next blog section.
- That’s it, you’re done! You can play around with your code and test the authentication is working by building and browsing to Login.aspx, putting breakpoints in your code, or try stepping through your code, but in the next section of the blog, you will be able to implement authentication one step further by allowing a portal user to read and update his/her information.
Allow for Read and Update Privileges on a form after Authentication:
- Create another blank .aspx web form called EditLead.aspx and have it also inherit from PortalPage and add the same using directives as you did for Login.aspx in the previous blog section:
Your Solution Explorer should look something like this:
- You will need to allow users to update some fields from the lead. To do this, you must modify our existing validateLead function on Login.aspx page which currently returns true or false to return a GUID if it finds the lead record. You can then use this GUID as a part of the query string when you redirect to EditLead.aspx as the criteria for fetching the rest of the lead data. Reference the Login.aspx.cs file from the included solution for specific source code. As previously mentioned, there exist two validateLead functions in the source code, the uncommented capitalized version refers to this function.
- You will want to redirect to the EditLead.aspx page with the GUID as a parameter of the query string, as mentioned in the previous step. Therefore, in Login.aspx, first add a using directive to System.Web.Security so that you can use a method from the FormsAuthentications class. Right after the event gets authenticated, set the authentication ticket for the logged in user, and redirect to the EditLead.aspx page:
if (leadGuid != Guid.Empty) { e.Authenticated = true; FormsAuthentication.SetAuthCookie(Login1.UserName, false); Response.Redirect(“EditLead.aspx?LeadID=” + leadGuid.ToString()); }
- In my example, I will display First Name and Last Name as plain text, and allow for the editing of topic in the form of the text box. Therefore you first need to add a First Name label, Last Name Label, Topic Label, Topic Text Box, and a button for update. Add these to the EditLead.aspx page. As always, you can reference the source code for EditLead.aspx
- Now that the fields are set on the aspx web page, and the id of the lead is passed as a parameter in query string, you can use that lead id to fetch the fields for the logged in user and to subsequently populate the fields on the EditLead.aspx form. To do this, add a function for Page_Init and in the body of Page_Init, fetch the lead and its fields then populate the First Name Last Name labels and Topic TextBox. Reference the EditLead.aspx.cs file for specific source code. Now, if you try logging with a valid credential, you should be able to see the fields on the portal field. In CRM:
On Portal Form (after Authentication):
- You can now see that the first name and last name labels are being populated with the first name and last name from the lead and the topic text box contains the same information as CRM. This is all good, but now you will add functionality to the Update button so that the Topic field in CRM can be updated to something else. To do this, you’ll need to add an OnClick event for the Update Button and in this event, update the topic for the lead. For good measure, add a new label that will display if the event is successful. Reference EditLead.aspx and EditLead.aspx.cs for specific source code. If you try updating the textbox with Rollerblades instead of Rollerskates, you will see that the record has been updated in CRM.
On Portal Form (on Update button click):
In CRM (on Update button click):
And there it is! You should now be able to do the following:
- Use the Portal Controls to create a form that on submit will create a new record in CRM.
- Allow for forms-based authentication against a password field in CRM.
- Allow for Read and Update Privileges on a form after authentication.
Feel free to call should you have any questions or comments.