allBlogsList

Authentication using OpenID Connect in a Sitecore application_Continued

In my previous article Authentication using OpenID Connect in a Sitecore application, I have discussed the steps involved in configuring the Owin Katana Middleware. This article outlines on how we use consume this configuration to authenticate extranet anonymous users in a Sitecore MVC application using ClaimsIdentity.

SItecore_OpenIDConnect

  • Claims: It represents attributes about a subject by an issuer that are useful in the context of authentication and authorization operations.
    1. Subject here is an entity that is part of an identity. Some examples are user, application, service, device, etc.,
    2. Issuer is also considered as an entity that is part of an identity. Some examples are operating system, a service, an identity provider or a federation provider.
  • ClaimsIdentity: It’s a class with a concrete implementation of a claims-based identity. The identity is described by a collection of claims.
  • ClaimsPrincipal: An implementation that supports multiple claims-based identities.

Sitecore by default is configured to use FormsAuthenticationProvider defined under authentication node in Sitecore.config. This is applicable to all the domains, default, sitecore and extranet.

  <!-- AUTHENTICATION -->
<authentication defaultprovider="forms">
<providers>
<clear></clear>
<add name="forms" type="Sitecore.Security.Authentication.FormsAuthenticationProvider, Sitecore.Kernel"></add>
</providers>
</authentication>

CHANGES IN CODE:

As part of this post, we will update the site “website” which is set to run on extranet domain to use ClaimsAuthenticationProvider.

I have used an article Sitecore with ClaimsIdentity written by Chandra Prakash where he explains the steps involved in making Sitecore work with ClaimsIdentity. Give it a read as it will help understand the rest of my article.

I added all the methods mentioned in his article into a project which includes

  1. SwitchingAuthenticationProviderExtension: This class inherits from Sitecore.Security.Authentication.SwitchingAuthenticationProvider. This extension determines whether to use FormsAuthenticationProvider or ClaimsAuthenticationProvider based on the value of Sitecore.Context.Domain
  2. ClaimsAuthenticationProvider: This class inherits from Sitecore.Security.Authentication.MembershipAuthenticationProvider. This is used to authenticate the user based on the claimsIdentity. 
  3. ClaimsAuthenticationHelper: This class inherits from Sitecore.Security.Authentication.AuthenticationHelper. This is used to set the Active User in Sitecore.Context
  4. ClaimsTransformer: This class inherits from System.Security.Claims.ClaimsAuthenticationManager. This is used as a pipeline for applying processing logic like filtering, validation, etc., to the claims collection in the incoming principal before execution reaches your application code
  5. ClaimsTransformationHttpModule: I didn't use this class in my implementation

Apply the following configuration changes in web.config

  • Add the section inside <configuration> node

    <system.identitymodel.services>
        <federationconfiguration>
          <cookiehandler mode="Default" requiressl="false"></cookiehandler>
        </federationconfiguration>
      </system.identitymodel.services>
      <system.identitymodel>
        <identityconfiguration>
          <claimsauthenticationmanager type="Framework.Sc.Extensions.Security.ClaimsTransformer, Framework.Sc.Extensions"></claimsauthenticationmanager>
        </identityconfiguration>
      </system.identitymodel>
    
  • Add this like under <system.webserver><modules> at the top position

    <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" precondition="managedHandler"></add>
    
  • Add references to Windows Identity Foundation under <configuration><configsections>

        <!--WIF 4.5 sections -->
        <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"></section>
        <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"></section>  
    

Create a new custom Sitecore configuration patch which will switch the authentication provider based on the Sitecore.Context.Domain

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <authentication>
      <patch:attribute name="defaultProvider">switcher</patch:attribute>
      <providers>
        <add name="switcher" type="XC.Authentication.Authentication.Helpers.SwitchingAuthenticationProviderExtension, XC.Authentication" patch:after="processor[@type='Sitecore.Security.Authentication.FormsAuthenticationProvider, Sitecore.Kernel']" domainmap="switchingProviders/authentication"></add>
        <add name="claims" type="XC.Authentication.Authentication.Claims.ClaimsAuthenticationProvider, XC.Authentication" patch:after="processor[@type='Sitecore.Security.Authentication.FormsAuthenticationProvider, Sitecore.Kernel']"></add>
      </providers>
    </authentication>
    <switchingproviders>
      <authentication>
        <map provider="claims" storefullnames="true" wildcard="%" domain="extranet"></map>
        <map provider="forms" storefullnames="true" wildcard="%" domain="sitecore"></map>
        <map provider="forms" storefullnames="true" wildcard="%" domain="default"></map>
      </authentication>
    </switchingproviders>
  </sitecore>
</configuration>

Create a Sitecore Controller which will have the following ActionResult. One for Login and one for [HttpPost] SignInCallback

  • SignIn: Decorated with [XCAuthorize]. This will be used for Login
  • SignInCallback: Decorated with [HttpPost]. This will be used to read the response from Identity Server

The XCAuthorize is my custom class which inherits from System.Web.MVC.AuthorizeAttribute. I added a override to HandleUnauthoprizedRequest as follows

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            /*When the user is not authenticated, set a temp Cookie to store the requested URL
                Once authorized, we will clear that cookie
             */
            SetTempCookie(filterContext);
            
            base.HandleUnauthorizedRequest(filterContext);
        }
private void SetTempCookie(AuthorizationContext filterContext)
        {
            
            string returnUrl = filterContext.HttpContext.Request.QueryString["returnUrl"];
            
            var cookie = new HttpCookie("TempCookie");
            cookie.Values.Add("returnUrl", returnUrl);
            filterContext.HttpContext.Response.Cookies.Add(cookie);
        }

CHANGES IN SITECORE:

Under Sitecore/Content/Home (using Sample Item template)

  • Login: This uses a LoginController rendering decorated with [XCAuthorize] attribute
  • SecuredPage: This page has been denied read access for extranet\anonymous users
  • SignInCallback: This is used to read the response back from Identity Server and uses SignInCallbackController rendering and decorated with [HttpPost]
  • SignOut: This page is used for signing out users. This makes use of SignOutController rendering

Under /sitecore/layout/Renderings/XC Controller Renderings (using Controller rendering template)

  • LoginController
    • Controller: XCAuthentication
    • Controller Action: SignIn
  • SignInCallbackController
    • Controller: XCAuthentication
    • Controller Action: SignInCallback
  • SignOutController
    • Controller: XCAuthentication
    • Controller Action: Signout

Save changes and publish to your "web" database.

TESTING:

Let's go step by step to understand how the Claims based authentication works.

  • When an anonymous user makes a request to the SecuredPage in Sitecore, by default gets redirected to a /login page which is configured in the Site definition
    • The users requested URL ~/securedpage, since he doesn't have access, they will get redirected to the login page
    • When the request hits the login page, the URL is ~/login?returnUrl=/securedpage
    • The returnUrl value from the querystring will be stored as a temporary cookie as part of the XCAuthorize class before redirecting the user to Identity Server
  • Since LoginController ActionResult is decorated with [XCAuthorize] attribute, it will return a 401-unauthorized response which means that the access has been denied for these credentials
  • The Owin Katana Middleware intercepts this request and redirects the user to the Identity Server login page
  • Once the user credentials are validated, the request goes back to the client based on the value of the RedirectUri added in the middleware (I have set mine to go to /signincallback.aspx) Form_Post_Callback
  • The SignInCallback page will read the response from the form post. We will start by validating the id_token based on JWKS (JSOn Web Key Set) string provided as part of the IdentityServer discovery document. Once validated, it will returns the claims
  • On top of the list of the claims available, I will add a new claimType
    • Type of Claim = claimType.Name and value = extranet\bob (Sitecore.Context.Domain\username)
    • Type of Claim = "id_token" and the value is the id_token from the response. This will eventually be used when user signs out
  • A Federated Authentication session cookie (FedAuth, FedAuth1, etc.,) is added to the user’s session
  • Redirect the user to the requested URL based on the temporary cookie we created and delete the cookie

At this point, when the request is processing to load the ~/securedpage, the ClaimsAuthenticationProvider will check if there are any claims available as part of the session cookie. If yes, then it will authenticate the user and set the Sitecore.Context.User to extranet\bob

I have added some information on the SecuredPage which will show the Sitecore.Context information

Ideally, there will be a link for the user to signout from the website. But, I am explaining using a page request. When user makes a request to ~/signout, it will trigger the SignOut ActionResult in which

  • We logout the user from Sitecore.Context
  • Logoff the user from Identity Server
    • Get the id_token using the owin context
    • We need to pass in a value for post_logout_redirect_uri which matches the value configured under Clients on the IdentityServer Logout_FromIdentity_Server
    • This will log off the user from Identity Server and automatically redirect back to the application Redirect_to_HomePage

The example used in this blog post has been implemented on the latest version of Sitecore 8.2.3, a MVC application running on .NET Framework 4.5.2. This implementation has been tested and supports multisite solution.


Important Notes:

  • Go through the Identity Server documentation here
  • Understand how to install the Identity Server certificates here
  • Read about the System.Security.Claims here

References:


Attachments: