In the case of this client's site, this includes Sitecore's analytics tracking cookie, Sitecore's language cookie (although technically okay to emit because it's only a session cookie) and a host of other tracking cookies emitted from different renderings.
In order to approach this in a way that prevents the need to edit logic across the solution, we came up with a solution that has two primary components:
- A Cookie Consent Provider - This provider allows us to interface with a third party system for determining if consent is required, not required or has already been provided. Additionally, by implementing a provider, we can easily switch out to another provider at some point in the future and not have to re-write logic.
- An MVC filter - By creating an MVC filter that checks for consent, we can simply decorate methods in our controllers to control the emitting of code/cookies without having to affect any logic already contained in those methods.
Cookie Consent Provider
The provider is based on the standard Microsoft provider model. I create a base class that all providers will inherit from. In the base class, I have methods to get and set the user's consent to use cookies.
I won't review the details of my provider implementation but know that it's pretty vanilla with a provider that implements the base class along with the necessary configuration elements in code and the web.config.
MVC Filter
I chose to use an "AuthorizeAttribute" filter to allow me to suppress the execution of controller methods. The authorize attribute returns a simple "true" or "false" value indicating if the calling method has the required permissions to execute.
The two primary areas to concern ourselves with in the filter are the "AuthorizeCore" and "HandleUnauthorizedRequest" methods.
The two primary areas to concern ourselves with in the filter are the "AuthorizeCore" and "HandleUnauthorizedRequest" methods.
In "AuthorizeCore", we are checking the need for consent. It returns a bool value indicating if the current request can be executed. Also note a bit of tricky logic there regarding the "InvertLogic" parameter. Basically, I have times when I need to execute the calling method when the result of the "AuthorizeCore" check is false. By passing in this parameter, I can invert the logic to return the desired results. In my case, I want to execute a controller rendering that outputs my cookie consent banner when consent is required. I can just decorate my method with the additional parameter:
In "HandleUnauthorizedRequest" (the code that is executed when "AuthorizeCore" returns "false") we are setting an AuthorizationContext Result. In my case, we wanted to output an HTML comment that indicates the calling method along with the status so that we could easily confirm that the system is functioning normally when we view the source of the page.
In "HandleUnauthorizedRequest" (the code that is executed when "AuthorizeCore" returns "false") we are setting an AuthorizationContext Result. In my case, we wanted to output an HTML comment that indicates the calling method along with the status so that we could easily confirm that the system is functioning normally when we view the source of the page.
Putting it Together
As mentioned, the goal here is to suppress cookies from being emitted before the user has given consent. The two Sitecore-specific cookies that I am blocking are the 'analytics' and 'lang' cookies. I block these by inserting pipeline processors that check my consent provider.
Note: Always use patch include files and refrain from editing the Sitecore.config file directly.
Note: Always use patch include files and refrain from editing the Sitecore.config file directly.
It's also worth noting that I have a configurable 'AnalyticsCookiePrefix' parameter that I pass into the "SuppressAnalyticsCookie" processor. If the cookie name ever changes in the future, we can edit the value in config and not have to touch the code.
In those processors, I make a call to the consent provider to see if I need to suppress the cookies or not. If I do, I search the current Response and remove the cookies. If consent has already been provided on a previous request, the code is skipped.
Note: Via Sitecore.Stackexchange, it was recommended that I not disable the Tracker. I disabled it anyways, because even though it might be legit to have the cookie written on the first load per EU specifications, my client's requirement was that it was not to be written at all until consent is provided. Additionally, I wanted to avoid editing the web.config file so instead of utilizing the HTTP module method, I wrote a pipeline processor. (Yeah, I know I edited the web.config with the consent provider . . . )
So the processors take care of the Sitecore-specific cookies that are written. For any controller methods that end up writing scripts that emit cookies, I simply decorate the method with my Authorize Attribute override and no further code changes are required!
In those processors, I make a call to the consent provider to see if I need to suppress the cookies or not. If I do, I search the current Response and remove the cookies. If consent has already been provided on a previous request, the code is skipped.
Note: Via Sitecore.Stackexchange, it was recommended that I not disable the Tracker. I disabled it anyways, because even though it might be legit to have the cookie written on the first load per EU specifications, my client's requirement was that it was not to be written at all until consent is provided. Additionally, I wanted to avoid editing the web.config file so instead of utilizing the HTTP module method, I wrote a pipeline processor. (Yeah, I know I edited the web.config with the consent provider . . . )
So the processors take care of the Sitecore-specific cookies that are written. For any controller methods that end up writing scripts that emit cookies, I simply decorate the method with my Authorize Attribute override and no further code changes are required!
Additional Resources
How to Write a Provider Model
MVC Filters