Friday, January 18, 2013

Customer Portal – switching authentication from LiveId to Forms

I have got a requirement from one of customers create customer portal. It was my first experience with portals so I decided not to build custom website but to install and configure Customer Portal. I would not write how to install and configure this solution – it is well described. Once it was configured I had a phone conference with customer and first requirement and main challenge for me was to remove LiveId authentication and replace it with other authentication.
I googled and found following article that described how to solve the same issue for CRM 4.0 that was written by MVP fellow Dylan Haskins (thanks a lot, I owe you a beer). Here are steps to replace LiveId authentication with Forms authentication (I assume that you’ve already configured Customer Portal):

1. Open web.config file and find following text:
<authentication mode="None"/>

Replace it with:

<authentication mode="Forms">
  <forms name=".ASPXAUTH" loginUrl="login"
   defaultUrl="default.aspx" protection="All" timeout="30" path="/"
   requireSSL="false" slidingExpiration="true"
   cookieless="UseDeviceProfile" domain=""
   enableCrossAppRedirects="false">
  </forms>
</authentication>

2. Open Pages/login.aspx file and replace content of page with following markup:

<%@ Page Language="C#" MasterPageFile="~/MasterPages/Default.master" AutoEventWireup="True" CodeBehind="Login.aspx.cs" Inherits="Site.Pages.Login" %>

<asp:Content ID="Content1" ContentPlaceHolderID="ContentBottom" runat="server">
        <asp:Login
            ID="Login1" runat="server" onauthenticate="Login1_Authenticate">
        </asp:Login>
</asp:Content>

3. Open Pages/login.aspx.cs file and replace whole code with following:

using System;
using System.Web.Security;
using Xrm;
using System.Linq;
using Microsoft.Xrm.Portal;
using Microsoft.Xrm.Portal.Configuration;
using Microsoft.Xrm.Portal.Access;
using Microsoft.Xrm.Portal.Cms;
using Microsoft.Xrm.Portal.Core;

namespace Site.Pages
{
    public partial class Login : PortalPage
    {

        private Contact _loginContact;
        protected Contact LoginContact
        {
            get
            {
                if (_loginContact != null)
                {
                    return _loginContact;
                }

                _loginContact = XrmContext.ContactSet
                        .FirstOrDefault(c => c.Adx_username == Login1.UserName
                            && (c.Adx_password == Login1.Password));

                XrmContext.Detach(_loginContact);
                return _loginContact;
            }
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            if ((User != null && User.Identity != null) && User.Identity.IsAuthenticated)
            {
                var redirectUrl = !string.IsNullOrEmpty(Request.QueryString["ReturnUrl"])
                    ? Request["ReturnUrl"]
                    : !string.IsNullOrEmpty(Request.QueryString["URL"])
                        ? Request["URL"]
                        : "/";

                Response.Redirect(redirectUrl);
            }
        }

        protected void Login1_Authenticate(object sender, System.Web.UI.WebControls.AuthenticateEventArgs e)
        {
            if (LoginContact == null)
            {
                e.Authenticated = false;
            }
            else
            {
                if (LoginContact.Adx_username == Login1.UserName)
                {
                    if (LoginContact.Adx_changepasswordatnextlogon.Value)
                    {
                        var portal = PortalCrmConfigurationManager.CreatePortalContext();
                        var website = (Adx_website)portal.Website;
                        var page = (Adx_webpage)portal.ServiceContext.GetPageBySiteMarkerName(portal.Website, "ChangePassword");

                        string redirectURL = page.Adx_PartialUrl + "?UserName=" + Server.UrlEncode(Login1.UserName) + 
                            "&Password=" + Server.UrlEncode(Login1.Password);
                        Response.Redirect(redirectURL);
                    }
                    else
                    {
                        LoginContact.Adx_LastSuccessfulLogon = DateTime.Now;

                        XrmContext.Attach(LoginContact);
                        XrmContext.UpdateObject(LoginContact);
                        XrmContext.SaveChanges();
                        XrmContext.Detach(LoginContact);

                        e.Authenticated = true;
                        FormsAuthentication.RedirectFromLoginPage(Login1.UserName, true);
                    }
                }
                else
                {
                    e.Authenticated = false;
                }
            }

        }
    }
}

4. In Page folder create Logout.aspx page. Open codebehind – Logout.aspx.cs and put following code inside:

using System;
using System.Linq;
using Microsoft.Xrm.Portal.Access;
using Microsoft.Xrm.Portal.Cms;
using Microsoft.Xrm.Portal.Core;
using Microsoft.Xrm.Portal.Web;
using Xrm;
using System.Web.UI.WebControls;
using Site.Library;
using System.Web.Security;
using Microsoft.Xrm.Portal.Configuration;

namespace Site.Pages
{
    public partial class Logout : PortalPage
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            FormsAuthentication.SignOut();

            var portal = PortalCrmConfigurationManager.CreatePortalContext();
            var website = (Adx_website)portal.Website;
            var page = (Adx_webpage)portal.ServiceContext.GetPageBySiteMarkerName(portal.Website, "Home");
            Response.Redirect(page.Adx_PartialUrl);
        }
    }
}

5. Open MasterPages/Default.master and find following markup:

<asp:LinkButton ID="LogoutLink" runat="server" Text='<%$ Snippet: links/logout, Logout %>' OnClick="LogoutLink_Click"/>

Replace it with following markup:

<asp:HyperLink ID="LogoutLink" runat="server" Text='<%$ Snippet: links/logout, Logout %>' NavigateUrl="/Pages/logout.aspx"/>

6. Clean and build your website.

7. For test purposes create a contact in CRM:


8. Open Customer Portal and try to log in:





That’s it and in case you are too lazy to do provided steps you can find source code of a project here: