ASP.NET Core 8.0 - Cookie Consent
This article will describe the implementation of Microsoft. AspNetCore. CookiePolicy to request user consent for non-essential cookies. You should review the earlier articles of the Cookies And Claims Project series. Registered users can download the ASP.NET Core 8.0 - Cookies And Claims Project for free.
Cookies And Claims Project and Article Series
Free project download for registered users!
I developed the Cookies And Claims Project (CACP) to demonstrate a simple cookie authentication scheme and claim-based authorization with a clear and modifiable design. The CACP is developed with Visual Studio 2022 and the MS Long Term Support (LTS) version .NET 8.0 framework. All Errors, Warnings, and Messages from Code Analysis have been mitigated. The CACP implements utilities like an AES Cipher, Password Hasher, native JavaScript client form validation, password requirements UI, Bootstrap Native message modal generator, loading/spinner generator, SignalR online user count, and an automatic idle logout for users with administration permissions.
- ASP.NET Core 8.0 - Cookies And Claims
- ASP.NET Core 8.0 - Cookie Authentication
- ASP.NET Core 8.0 - Remember Me Or Not
- ASP.NET Core 8.0 - Authorized Access
- ASP.NET Core 8.0 - Administrator Claim
- ASP.NET Core 8.0 - Admin Idle Logout
- ASP.NET Core 8.0 - Cookie Consent
- ASP.NET Core 8.0 - SignalR Online User Count
- ASP.NET Core 8.0 - AES Cipher
- ASP.NET Core 8.0 - Password Hasher
- ASP.NET Core 8.0 - Message Modal Generator
Updated as the articles become published.
The CACP implements a cookie policy banner and CheckConsentNeeded option from Microsoft. AspNetCore. CookiePolicy to request user consent for non-essential cookies. See EU General Data Protection Regulation (GDPR) support in ASP.NET Core. This article updates the implementation of the previous articles I published, ASP.NET Core 3.1 - Cookie Consent and ASP.NET Core 5.0 - Cookie Consent and GDPR to include the function to revoke the cookie consent.
Configure the CheckConsentNeeded option to check if consent policies should be evaluated on the request. Add the UseCookiePolicy extension to apply the CheckConsentNeeded option to the HTTP request pipeline.
Program.cs:
builder.Services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = Microsoft.AspNetCore.Http.SameSiteMode.Strict; });
app.UseCookiePolicy();
I updated the way I pass the CanTrack consent flag to JavaScript. I declare a mustAcceptCookies JavaScript variable only when the consent banner is displayed. I moved the _CookieConsentPartial below the RenderBody div in _Layout. cshtml to apply mobile friendly style from site.css.
_CookieConsentPartial.cshtml:
@{ var consentFeature = Context.Features.Get<ITrackingConsentFeature>(); var showBanner = !consentFeature?.CanTrack ?? false; var cookieString = consentFeature?.CreateConsentCookie(); } @if (showBanner) { <div id="CookieConsent" class="alert alert-info fade show border border-primary" role="alert"> I request your consent to use non-essential cookies to capture usage metrics like screen size and time zone. <a asp-page="/Privacy">Learn More</a>. <button type="button" class="btn btn-primary d-block mt-1 mx-auto" data-bs-dismiss="alert" data-cookie-string="@cookieString"> Accept </button> </div> <script> // Declared globally when TrackingConsentFeature is enabled and the user has not consented. // Restricts the use of non-essential cookies. var mustAcceptCookies = true; (function () { var button = document.querySelector("#CookieConsent button[data-cookie-string]"); button.addEventListener("click", function (event) { document.cookie = button.dataset.cookieString; mustAcceptCookies = false; if(location.pathname.toLowerCase() == '/privacy'){ location.reload(); return false; } }, false); })(); </script> }
The European Union's General Data Protection Regulation (GDPR) Article 7.3 states:
The data subject shall have the right to withdraw his or her consent at any time. The withdrawal of consent shall not affect the lawfulness of processing based on consent before its withdrawal. Prior to giving consent, the data subject shall be informed thereof. It shall be as easy to withdraw as to give consent.
The CACP implements a cookie consent withdraw method when the user has consented to non-essential cookies.
The Revoke Cookie Consent button posts to the Privacy Policy page to withdraw from the feature and delete any non-essential cookies.
Privacy.cshtml.cs:
public IActionResult OnPostWithdraw() { HttpContext.Features.Get<ITrackingConsentFeature>()?.WithdrawConsent(); if (Request.Cookies.ContainsKey(AppSettings.TimeOffsetCookieName)) Response.Cookies.Delete(AppSettings.TimeOffsetCookieName); if (Request.Cookies.ContainsKey(AppSettings.ScreenSizeCookieName)) Response.Cookies.Delete(AppSettings.ScreenSizeCookieName); return RedirectToPage(); }
If the mustAcceptCookies variable is not declared and set true by the _CookieConsentPartial.cshtml, the CACP sets any non-essential cookies from site.js.
wwwroot > js > site.js:
let setCookie = function (cname, cvalue, exdays, path = '/', samesite = 'strict') { let d = new Date(); d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); document.cookie = cname + '=' + cvalue + ';expires=' + d.toUTCString() + ';path=' + path + '; samesite=' + samesite + ';'; } let getCookie = function (cname) { let name = cname + '='; let ca = document.cookie.split(';'); for (let i = 0; i < ca.length; i++) { let c = ca[i]; while (c.charAt(0) === ' ') c = c.substring(1); if (c.indexOf(name) === 0) return c.substring(name.length, c.length); } return ''; } // Depends on message-modal.js let verifyCookieConsent = function () { // mustAcceptCookies declared by _CookieConsentPartial.cshtml and is truthy. if (window.mustAcceptCookies) { showMessageModal('You are attempting a request which requires the use of non-essential cookies.\r\n' + 'You must Accept cookies.', 'alert-info'); return false; } else return true; } document.addEventListener('DOMContentLoaded', function () { // !window.mustAcceptCookies indicates it is not declared by _CookieConsentPartial.cshtml, or is falsy. if (!window.mustAcceptCookies) { // client timezone if (getCookie('CACP.TimeOffset') === '') { setCookie('CACP.TimeOffset', new Date().getTimezoneOffset(), 1, '/', 'strict'); } // client screen width and height if (getCookie('CACP.ScreenSize') === '') { setCookie('CACP.ScreenSize', window.screen.availWidth.toString() + 'X' + window.screen.availHeight.toString(), 365, '/', 'strict'); } } // Depends on signalr.js and OnlineCountHub.cs let onlineCount = document.querySelector('span.online-count'); if (typeof (signalR) !== "undefined") { //.withAutomaticReconnect([0, 0, 10000]) let countConnection = new signalR.HubConnectionBuilder() .withUrl('/onlinecount').build(); let updateCountCallback = function (message) { if (!message) return; if (onlineCount) onlineCount.innerText = message; }; function onConnectionError(error) { if (error && error.message) console.error(error.message); } countConnection.on('updateCount', updateCountCallback); countConnection.onclose(onConnectionError); countConnection.start() .then(function () { console.log('OnlineCount Connected'); }) .catch(function (error) { if (showMessageModal) showMessageModal(error.message, 'alert-danger'); console.error(error.message); }); } });
The verifyCookieConsent function can be used to query cookie consent from JavaScript.
Comments(0)