1

We have 5 websites (e.g. A, B, C, D and E) and with separate clientId in Auth0. We have relevant IdentityProvidersProcessor for these applications. Sometimes when cms is left idle, upon reloading the cms, the IdentityProvidersProcessor for the last website executes e.g E and takes the cms user to the external login page. This has also happened for other websites when the user are logged in with Auth0, but not frequently.

Wondering if anyone faced such anomaly or have any insights on fixing this issue.

Thank you!

1 Answer 1

0

I’ve had this exact same issue. It only happen when you visit a page in Sitecore which has an existing filePath. For example /sitecore/admin/showconfig.aspx

The problem happens in the Sitecore.Pipelines.HttpRequest.ExecuteRequest Process method:

public override void Process(HttpRequestArgs args)
{
  Assert.ArgumentNotNull((object) args, nameof (args));
  SiteContext site = Context.Site;
  if (site != null && !this.SiteManager.CanEnter(site.Name, (Account) this.ContextUser))
  {
    this.HandleSiteAccessDenied(site, args);
  }
  else
  {
    PageContext page = Context.Page;
    Assert.IsNotNull((object) page, "No page context in processor.");
    string filePath = page.FilePath;
    if (filePath.Length > 0)
    {
      if (WebUtil.IsExternalUrl(filePath))
      {
        args.HttpContext.Response.Redirect(filePath, true);
      }
      else
      {
        if (string.Compare(filePath, HttpContext.Current.Request.Url.LocalPath, StringComparison.InvariantCultureIgnoreCase) == 0)
          return;
        args.HttpContext.RewritePath(filePath, args.HttpContext.Request.PathInfo, args.Url.QueryString, false);
      }
    }
    else if (Context.Item == null)
      this.HandleItemNotFound(args);
    else
      this.HandleLayoutNotFound(args);
  }
}

As you can see if there is a filepath (filePath.Length > 0) it does some logic, but it will never reach this code:

else if (Context.Item == null)
  this.HandleItemNotFound(args);

That is the code which will send the user to the correct login page if they don't have access (cms is left idle).

private void HandleItemNotFound(HttpRequestArgs args)
{
  string localPath = args.LocalPath;
  string name = Context.User.Name;
  bool flag = false;
  bool loginPage = false;
  string url1 = Settings.ItemNotFoundUrl;
  if (args.PermissionDenied)
  {
    flag = true;
    url1 = this.GetNoAccessUrl(out loginPage);
  }
  SiteContext site = Context.Site;
  string str = site != null ? site.Name : string.Empty;
  List<string> stringList = new List<string>((IEnumerable<string>) new string[6]{ "item", localPath, "user", name, "site", str });
  if (Settings.Authentication.SaveRawUrl)
    stringList.AddRange((IEnumerable<string>) new string[2]
    {
      "url",
      Context.RawUrl
    });
  string url2 = WebUtil.AddQueryString(url1, stringList.ToArray());
  if (!flag)
  {
    Log.Warn(string.Format("Request is redirected to document not found page. Requested url: {0}, User: {1}, Website: {2}", (object) Context.RawUrl, (object) name, (object) str), (object) this);
    this.RedirectOnItemNotFound(url2);
  }
  else
  {
    if (loginPage)
    {
      Log.Warn(string.Format("Request is redirected to login page. Requested url: {0}, User: {1}, Website: {2}", (object) Context.RawUrl, (object) name, (object) str), (object) this);
      this.RedirectToLoginPage(url2);
    }
    Log.Warn(string.Format("Request is redirected to access denied page. Requested url: {0}, User: {1}, Website: {2}", (object) Context.RawUrl, (object) name, (object) str), (object) this);
    this.RedirectOnNoAccess(url2);
  }
}

You should be able to reproduce the issue with these steps:

  1. Login into Sitecore.
  2. Visit a page which has a filePath like /sitecore/admin/showconfig.aspx
  3. Stay on the page without refreshing until the cms becomes idle.
  4. If you are not logged in anymore and you refresh it will redirect you the wrong identity provider.

If you do the above steps, but you try to visit a page without filePath (for example /sitecore) instead you will be redirect to the correct identity provider (Sitecore Identity) because that URL doesn't have a filePath. Sometimes /sitecore does a 302 redirect to another page which does have a filePath. So it might also happen than.

I've fixed this by adding an extra pipeline:

public class FilePathAuthentication : HttpRequestProcessor
{
    private readonly IUserContextFactory userContextFacotFactory;

    private readonly ISiteContextFactory siteContextFactory;

    private readonly IPageContextFactory pageContextFactory;

    private readonly IContextWrapper contextWrapper;

    private readonly ISiteManager siteManager;

    private readonly ILogger logger;

    public List<string> IgnorePaths { get; set; }

    /// <summary>
    /// In the ExecuteRequest pipeline the HandleItemNotFound check is not done when filepaths match.
    /// The HandleItemNotFound check is required to redirect to the correct login page (identity provider).
    /// This pipeline will make sure that this check still done for filepaths.
    /// </summary>
    public FilePathAuthentication(
        IUserContextFactory userContextFacotFactory,
        ISiteContextFactory siteContextFactory,
        IPageContextFactory pageContextFactory,
        IContextWrapper contextWrapper,
        ISiteManager siteManager,
        ILogger logger)
    {
        this.userContextFacotFactory = userContextFacotFactory;
        this.siteContextFactory = siteContextFactory;
        this.pageContextFactory = pageContextFactory;
        this.contextWrapper = contextWrapper;
        this.siteManager = siteManager;
        this.logger = logger;
        this.IgnorePaths = new List<string>();
    }

    public override void Process(HttpRequestArgs args)
    {
        var userContext = this.userContextFacotFactory.Create();

        // Only check for the login page if not authenticated.
        if (!userContext.IsAuthenticated)
        {
            var pageContext = this.pageContextFactory.Create();
            var filePath = pageContext.FilePath;

            // Make sure there is a filepath.
            if (filePath.Length > 0)
            {
                // Ignore certain physical paths from the config.
                if (this.IgnorePaths.Any(path =>
                    path.Equals(filePath, StringComparison.InvariantCultureIgnoreCase)))
                {
                    return;
                }

                // This is the check in ExecuteRequest which skips redirect to the login page.
                // So we only need to continue after this check here. Otherwise it should be done in ExecuteRequest.
                if (string.Compare(filePath, contextWrapper?.CurrentRequestUrlLocalPath, StringComparison.InvariantCultureIgnoreCase) == 0)
                {
                    // Get the login page for the correct identity provider.
                    var loginPage = GetNoAccessUrl(userContext);
                    if (!string.IsNullOrEmpty(loginPage))
                    {
                        var stringList = new List<string>
                        {
                            "url", WebUtil.NormalizeUrl(contextWrapper?.CurrentRequestRawUrl)
                        };
                        // Create a url to the identity provider with a return url to the protected page.
                        var redirectUrl = WebUtil.AddQueryString(loginPage, stringList.ToArray());
                        RedirectToLoginPage(redirectUrl);
                    }
                }
            }
        }
    }

    private string GetNoAccessUrl(IUserContext userContext)
    {
        var siteContext = this.siteContextFactory.Create();

        if (siteContext == null) return string.Empty;

        // Check if there is a login page which we can redirect to.
        // Also check if the physical folder contains sitecore. Those are the only paths which could be protected.
        var hasLoginPage = siteContext.SiteLoginPage.Length > 0 && siteContext.SitePhysicalFolder.Contains("sitecore");

        // Check if the user can enter the site, but is not authenticated yet.
        var canEnter = this.siteManager.CanEnter(siteContext.SiteName) && !userContext.IsAuthenticated;

        if (hasLoginPage && canEnter)
        {
            this.logger.Info("Redirecting to login page \"" + siteContext.SiteLoginPage + "\".");

            // Return the url of the login page which should redirect to the correct identity provider.
            return siteContext.SiteLoginPage;
        }

        return string.Empty;
    }

    private void RedirectToLoginPage(string url)
    {
        var urlString = new UrlString(url);
        urlString["returnUrl"] = WebUtil.GetRawUrl();
        contextWrapper.Response.Redirect(urlString.ToString(), true);
    }
}

This is the config:

<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <pipelines>
            <httpRequestBegin>
                <processor type="Project.Foundation.Identity.Pipelines.FilePathAuthentication, Project.Foundation.Identity"
                           patch:after="processor[@type='Sitecore.Pipelines.HttpRequest.ExecuteRequest, Sitecore.Kernel']"
                           resolve="true">
                    <ignorePaths hint="list">
                        <ignorePath>/sitecore/default.aspx</ignorePath>
                        <ignorePath>/sitemap.xml</ignorePath>
                        <ignorePath>/layouts/system/visitoridentification.js</ignorePath>
                        <ignorePath>/layouts/system/vichecker.aspx</ignorePath>
                        <ignorePath>/layouts/system/visitoridentificationcss.aspx</ignorePath>
                        <ignorePath>/sitecore/shell/webservice/service2.asmx</ignorePath>
                    </ignorePaths>
                </processor>
            </httpRequestBegin>
        </pipelines>
    </sitecore>
</configuration>

If you add this pipeline the issue should be fixed and you will always be redirect to the correct identity provider. Even if you visit a page which has a filePath like /sitecore/admin/showconfig.aspx.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.