Steps:

  1. Enable gateway for first request per session
  2. Browse to a page on your site that would trigger gateway (like a node)
  3. Infinite redirect loop

This happens because Drupal is caching the 302 redirect to the CAS server during the gateway request. So if you go to /node/2, our module returns a TrustedRedirectResponse object to redirect the browser to the CAS server to check if the user is logged in. Drupal is caching that redirect response object for requests to /node/2.

So what happens is that CAS server returns the browser to /casservice, which will redirect the user back to their previous URL, which is /node/2. The cached response is loaded and we start the loop.

I didn't think Drupal would save a redirect response to the page cache, but that's what it's doing. So we need to figure out why and what we need to fix.

Comments

bkosborne created an issue. See original summary.

bkosborne’s picture

This page indicates that the TrustedRedirectResponse we're using extends CacheableSecuredRedirectResponse, which explains our problem.

I think we'll need to create a response object of SecuredRedirectResponse instead. I can look into this tonight.

yalet’s picture

Status: Active » Needs review
StatusFileSize
new644 bytes

I only tried this for gateway, not also for forced login. Does this work?

bkosborne’s picture

That seems kind of hacky though right?

For forced login, where going to X path on your website will always result in a redirect to the CAS server, then we should be able to use a cached redirect response as long as it has a cache tag tied to our config (so when the config changes, the cached redirect is flushed).

For gateway, we just never want to cache the redirect at all, since any given page can redirect for a user. It all depends on what page an anonymous user lands first (that's the page that will redirect). Def don't want that redirect cached. It seems appropriate to create a redirect response that doesn't implement CacheableSecuredRedirectResponse so it's not cached. Not sure quite yet. Sorry I haven't spent more time on this yet.

bkosborne’s picture

Expanding more on what I originally wrote:

Caching the initial redirect responses is actually desirable. For both the gateway and forced login features, it will always be the case that hitting that URL should redirect an anonymous user to the CAS server for the gateway check. We shave some time off these redirects by having them cached.

The problem is that when our service controller is done handling the response from the CAS server, it needs to redirect the user back to the page they were originally on, and that page is now cached as a redirect to CAS server.

To prevent the Page Cache module from returning the cached redirect for the page, we can implement our own HTTP middleware that will invoke the Drupal backend to actually render the page before the Page Cache middleware has a chance to execute (ours would execute first). However:

(1) There's nothing in the request that would let us know "hey - don't render the cached redirect for this, instead render the real page". The only way to do that is add a query string param but I don't think site owners would want all their users being redirected to the page with an extra param.
(2) There's no way to control what external cacheing systems like Varnish (this solution would only work for ppl using Page Cache module).
(3) While we are caching the redirect response (which is good), we aren't caching the truly expensive response - the actual page. This would always be Drupal fully bootstrapping and rendering the response.

The better approach would be to never cache the redirect response, and instead allow the actual Drupal page to be cached. To do this, instead of using a subscriber to redirect for gateway & forced login (which is too late since Page Cache intervenes earlier), we create an HTTP middleware that executes before the Page Cache middleware. All the logic we have in the subscriber would be into this middleware. The only problem I see with this approach is that it still would not work with external page caches. But I guess that's OK - we would just say it won't work.

All that said, for this initial release, I guess I'm OK making these redirects non-cacheable. We can revisit this more complex scenario that supports the Page Cache module later down the road.

For this patch, we'll want to disable cacheing for both Gateway and Forced Redirect responses, and also add some inline help text for these features that indicates they are not compatible with the Page Cache module.

mikejw’s picture

Can confirm that this is the issue for ForcedLogin - I copy and pasted your fix into the forced login handler and it works fine (as expected).

mikejw’s picture

StatusFileSize
new2.47 KB

Here's a patch - I added in a NOTE to the gateway and forced login fieldsets in settings - maybe it should be highlighted more though?

Status: Needs review » Needs work

The last submitted patch, 7: cas-gateway-forced-2607818-7-8.patch, failed testing.

The last submitted patch, 7: cas-gateway-forced-2607818-7-8.patch, failed testing.

mikejw’s picture

Status: Needs work » Needs review
StatusFileSize
new2.54 KB

Sorry, slight error in that patch.

bkosborne’s picture

It's actually not true that using these features will disable page caching. Instead it will cause unexpected behavior: The first user to hit the page that's has gateway or forced login enabled will be properly redirected to CAS server. All subsequent users will not be redirected at all. So the feature is essentially broken when the Page Cache module is enabled. I'll update patch with that info.

bkosborne’s picture

StatusFileSize
new4.15 KB

Here's the updated patch. I was running into problems with the Dynamic Page Cache module as well, since it has a subscriber that was running BEFORE ours was and returning a response before we had a chance to. So I had to change the priority of our listener to execute before it as well.

I tested gateway and forced login and it all works as expected now.

yalet’s picture

This looks good to me.

bkosborne’s picture

Status: Needs review » Fixed

Committed

  • bkosborne committed de87439 on
    git commit -m 'Issue #2607818 by mikejw, bkosborne, yalet: Page cache...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.