Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API-calls to Layout Service that user does not has permission for are redirected to /sitecore/service/notfound.aspx #145

Open
BonNil opened this issue Jan 30, 2019 · 6 comments

Comments

Projects
None yet
2 participants
@BonNil
Copy link

commented Jan 30, 2019

Description

When a user is trying to access a route which they do not have access to in connected mode, the layout service call is redirected to login.aspx (http code 302), which in turn seems to be redirected to sitecore/service/notfound.aspx, which fails. In the JSS app this results in a "TypeError: Cannot read property 'sitecore' of null"-error in RouteHandler.js.

Expected behavior

Expected behaviour (in my opinion) would be to get a 403-response or JSON-response from the Layout Service saying that this item is inaccessible to the current user, and it would then be up to the client to decide how to handle this (show an access-denied page, redirect to custom login route, or similar).

Steps To Reproduce

  1. Create a route in Sitecore under your JSS app.
  2. Remove read-access to this route for the user that the JSS app is configured to use (prob. extranet\Anonymous.
  3. Start JSS app in connected mode: jss start:connected
  4. Open your browser and open your browser dev tools.
  5. Browse to the route you created.
  6. See error and see network call stack.

Possible Fix

Possible fix could be to change the behavior as described under the Expected behavior section, altough there are likely other ways to fix this as well. If the default behavior is not changed, it would atleast be good to have an easy way to change the default behavior.

Your Environment

  • Sitecore Version: 9.1
  • JSS Version: 11.0
  • Browser Name and version: Chrome 71.0.3578.98
  • Operating System and version (desktop or mobile): Windows 10
  • Link to your project (if available):

Screenshots

network_call_stack_access_denied

@BonNil BonNil changed the title API-calls to Layout Service that user does not has permission for are redirected to /sitecore/login.aspx API-calls to Layout Service that user does not has permission for are redirected to /sitecore/service/notfound.aspx Jan 30, 2019

@aweber1

This comment has been minimized.

Copy link
Member

commented Jan 30, 2019

Out of curiosity, after starting the app in connected mode, are you browsing to localhost:3000 in incognito mode or in a tab that would be sharing cookies where you are or were already logged into Sitecore?

This issue sounds familiar, but I'm not having any luck locating prior information. I think if you're not in incognito (or a separate browser) in this scenario, then Sitecore will handle the request and redirect to the login page before Layout Service has a chance to handle it.

@aweber1

This comment has been minimized.

Copy link
Member

commented Jan 30, 2019

Nevermind, that is not the problem. I found the prior issue I was thinking of and, while somewhat related, is not the same: https://sitecore.stackexchange.com/questions/13387/jss-start-connected-mode-issue/13388

My guess is something in the httpRequestBegin pipeline is preventing the request from reaching the Layout Service controller. More investigation is needed.

@aweber1

This comment has been minimized.

Copy link
Member

commented Jan 30, 2019

After some investigation, the behavior you're seeing is being caused Identity Server. Basically, when Layout Service (or likely any MVC controller) returns a 401 Unauthorized status, Identity Server will attempt to redirect to the login page, which is the 404 you are seeing.

This is not an issue if Identity Server is disabled in your Sitecore instance and are instead using the traditional Forms Authentication. In that scenario, the Layout Service controller sets Response.SuppressFormsAuthenticationRedirect = true which allows the 401 response to be delivered to the client without issue.

We've raised the issue with the Identity Server/Auth/Owin team to find a good long-term solution.

In the meantime, there are a couple workarounds (outlined below). Both revolve around a pipeline named owin.cookieAuthentication.applyRedirect declared in the App_Config\Sitecore\Owin.Authentication\Sitecore.Owin.Authentication.config file. This pipeline determines how to handle redirects for cookie authentication.

1. X-Requested-With header or querystring

Set the X-Requested-With header or querystring parameter to XMLHttpRequest for your Layout Service request(s). In the applyRedirect' pipeline mentioned above, there is a SkipAjaxRequestprocessor that will abort the pipeline if theX-Requested-Withheader or querystring parameter is set toXMLHttpRequest`. This workaround requires changes to an Layout Service data fetching code in your JSS app.

However, it is important to note that if you intend to set the X-Requested-With header, you will also need to add that header to the list of Access-Control-Allow-Headers that are configured for CORS access. Also, adding the X-Requested-With header to your requests will trigger a pre-flight OPTIONS request for any CORS request. This may not be desirable.

Also, if you intend to use the X-Requested-With querystring parameter, it's important to note that the IsAjaxRequest processor uses a StringComparison.Ordinal equality check. In other words, the comparison check is case-sensitive. So your querystring parameters would need to look like the following: ?item=[item path]&sc_apikey={api_key}&X-Requested-With=XMLHttpRequest

2. Custom applyRedirect pipeline processor

You can create a custom pipeline processor for the owin.cookieAuthentication.applyRedirect pipeline that essentially does something similar to the SkipAjaxRequest processor mentioned in option 1. But instead of looking for a custom header or querystring value, it will examine the request and if it is a Layout Service request then the pipeline will be aborted and no login redirect will occur.

Note: this has only been lightly tested as a means to solving this specific redirect issue. No guarantees about unintended side-effects.

Processor:

using Sitecore.Diagnostics;
using Sitecore.LayoutService.Mvc.Routing;
using Sitecore.Mvc.Presentation;
using Sitecore.Owin.Authentication.Pipelines.CookieAuthentication.ApplyRedirect;

namespace MyNamespace
{
    public class SkipLayoutServiceRequest : ApplyRedirectProcessor
    {
        protected readonly IRouteMapper RouteMapper;
		
        public SkipLayoutServiceRequest(IRouteMapper routeMapper)
        {
            Assert.ArgumentNotNull(routeMapper, "routeMapper");
            RouteMapper = routeMapper;
        }
		
        public override void Process(ApplyRedirectArgs args)
        {
            Assert.ArgumentNotNull(args, nameof(args));

            // Be sure to use `.CurrentOrNull` instead of `.Current`. When using `.Current`, the underlying ContextService.Peek method will throw an exception if
            // the ContextService stack is empty. With `.CurrentOrNull`, `Peek` will simply return null if the stack is empty.
            if (PageContext.CurrentOrNull != null && PageContext.Current.RequestContext != null && RouteMapper.IsLayoutServiceRoute(PageContext.Current.RequestContext))
            {
                args.Context.RedirectUri = null;
                args.AbortPipeline();
            }
        }
    }
}

Config patch:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
	<pipelines>
	  <owin.cookieAuthentication.applyRedirect>
		<processor
		  type="MyNamespace.SkipLayoutServiceRequest, MyLibrary" 
		  patch:before="*[@type='Sitecore.Owin.Authentication.Pipelines.CookieAuthentication.ApplyRedirect.SkipAjaxRequest, Sitecore.Owin.Authentication']"
		  resolve="true"
		/>
	  </owin.cookieAuthentication.applyRedirect>
	</pipelines>
  </sitecore>
</configuration>
@BonNil

This comment has been minimized.

Copy link
Author

commented Jan 31, 2019

Great, thanks for thorough explanation and for the workaround suggestions! I will try them out and also stay tuned for the long term solution.

Might get back to you if I have any questions on the workarounds.

@BonNil

This comment has been minimized.

Copy link
Author

commented Feb 7, 2019

Hi @aweber1! One slight modification I did to the second workaround suggestion:

Upon login, the SkipLayoutServiceRequest processor will cause an error, resulting in the user ending up on an error page (even though the login is succesful). PageContext.Current throws an excpetion when with the: Message: Attempt to retrieve context object of type 'Sitecore.Mvc.Presentation.PageContext' from empty stack.

Edit: .... And I just realized you just updated the code above with the exact thing I was about to tell you about xD

giphy

Edit 2: Aside from writing unnecessary comments, I also managed to close this ticket accidentally... I'll reopen it, sorry for the confusion.

@BonNil BonNil closed this Feb 7, 2019

@BonNil BonNil reopened this Feb 7, 2019

@aweber1

This comment has been minimized.

Copy link
Member

commented Feb 7, 2019

@BonNil ha, yes, I ran into that same error yesterday and updated the code sample, sneakily 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.