Skip to content

Commit

Permalink
Blazor GDPR article (#34499)
Browse files Browse the repository at this point in the history
  • Loading branch information
guardrex authored Jan 16, 2025
1 parent 73eedb9 commit fa84c7c
Show file tree
Hide file tree
Showing 3 changed files with 257 additions and 1 deletion.
251 changes: 251 additions & 0 deletions aspnetcore/blazor/security/gdpr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
---
title: EU General Data Protection Regulation (GDPR) support in ASP.NET Core Blazor
author: guardrex
description: Learn how to implement EU General Data Protection Regulation (GDPR) support in Blazor apps.
monikerRange: '>= aspnetcore-6.0'
ms.author: riande
ms.custom: mvc
ms.date: 01/16/2025
uid: blazor/security/gdpr
zone_pivot_groups: blazor-app-models
---
# EU General Data Protection Regulation (GDPR) support in ASP.NET Core Blazor

[!INCLUDE[](~/includes/not-latest-version.md)]

This article explains how to implement support for [EU General Data Protection Regulation (GDPR)](https://ec.europa.eu/info/law/law-topic/data-protection/reform/what-does-general-data-protection-regulation-gdpr-govern_en) requirements.

:::zone pivot="server"

In the `Program` file:

* Add <xref:Microsoft.AspNetCore.Builder.CookiePolicyOptions> configuration to require user consent for non-essential cookies and set the same-site policy to none. For more information, see <xref:security/samesite>.
* Add the default implementation for the <xref:Microsoft.AspNetCore.Http.IHttpContextAccessor> service by calling <xref:Microsoft.Extensions.DependencyInjection.HttpServiceCollectionExtensions.AddHttpContextAccessor%2A>.

```csharp
builder.Services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;

options.MinimumSameSitePolicy = SameSiteMode.None;
});

builder.Services.AddHttpContextAccessor();
```

:::moniker range=">= aspnetcore-8.0"

In the `Program` file before the call to <xref:Microsoft.AspNetCore.Builder.RazorComponentsEndpointRouteBuilderExtensions.MapRazorComponents%2A>, add Cookie Policy Middleware by calling <xref:Microsoft.AspNetCore.Builder.CookiePolicyAppBuilderExtensions.UseCookiePolicy%2A>:

:::moniker-end

:::moniker range="< aspnetcore-8.0"

In the `Program` file before the call to <xref:Microsoft.AspNetCore.Builder.ComponentEndpointRouteBuilderExtensions.MapBlazorHub%2A>, add Cookie Policy Middleware by calling <xref:Microsoft.AspNetCore.Builder.CookiePolicyAppBuilderExtensions.UseCookiePolicy%2A>:

:::moniker-end

```csharp
app.UseCookiePolicy();
```

Add the following `CookieConsent` component to handle cookie policy consent.

The component uses a [collocated JavaScript file](xref:blazor/js-interop/javascript-location#load-a-script-from-an-external-javascript-file-js-collocated-with-a-component), named `CookieConsent.razor.js`, to load a module. Confirm or adjust the path to the collocated file in the `OnAfterRenderAsync` method. The following component assumes that the component and its companion JavaScript file are in the `Components` folder of the app.

`CookieConsent.razor`:

```razor
@using Microsoft.AspNetCore.Http.Features
@using Microsoft.AspNetCore.Http
@implements IAsyncDisposable
@inject IHttpContextAccessor Http
@inject IJSRuntime JS
@if (showBanner)
{
<div id="cookieConsent" class="alert alert-info alert-dismissible fade show"
role="alert">
Use this space to summarize your privacy and cookie use policy.
<a href="/privacy">Privacy Policy</a>
<button type="button" @onclick="AcceptPolicy" class="accept-policy close"
data-bs-dismiss="alert" aria-label="Close"
data-cookie-string="@cookieString">
Accept
</button>
</div>
}
@code {
private IJSObjectReference? module;
private ITrackingConsentFeature? consentFeature;
private bool showBanner;
private string? cookieString;
protected override void OnInitialized()
{
consentFeature = Http.HttpContext?.Features.Get<ITrackingConsentFeature>();
showBanner = !consentFeature?.CanTrack ?? false;
cookieString = consentFeature?.CreateConsentCookie();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Components/CookieConsent.razor.js");
}
}
private async Task AcceptPolicy()
{
if (module is not null)
{
await module.InvokeVoidAsync("acceptPolicy", cookieString);
showBanner = false;
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
```

Add the following [collocated JavaScript file](xref:blazor/js-interop/javascript-location#load-a-script-from-an-external-javascript-file-js-collocated-with-a-component) to maintain the `acceptPolicy` function in a JavaScript module.

`CookieConsent.razor.js`:

```javascript
export function acceptPolicy(cookieString) {
document.cookie = cookieString;
}
```

Within `<main>` Razor markup of the `MainLayout` component (`MainLayout.razor`), add the `CookieConsent` component:

```razor
<CookieConsent />
```

## Customize the cookie consent value

Specify the cookie consent value by assigning a custom string to <xref:Microsoft.AspNetCore.Builder.CookiePolicyOptions.ConsentCookieValue%2A?displayProperty=nameWithType>. The following example changes the default value of "`yes`" to "`true`":

```csharp
options.ConsentCookieValue = "true";
```

:::zone-end

:::zone pivot="webassembly"

In Blazor WebAssembly apps, [local storage](https://developer.mozilla.org/docs/Web/API/Window/localStorage) is a convenient approach for maintaining a user's acceptance of a site's cookie policy. The following approach demonstrates the approach.

If the app doesn't already have a `Shared` folder for shared components, add a `Shared` folder to the app.

Add the namespace for shared components to the `_Imports.razor` file. In the following example, the app's namespace is `BlazorSample`, and the shared folder's namespace is `BlazorSample.Shared`:

```razor
@using BlazorSample.Shared
```

Add the following `CookieConsent` component to handle cookie policy consent.

`Shared/CookieConsent.razor`:

```razor
@implements IAsyncDisposable
@inject IJSRuntime JS
@if (showBanner)
{
<div id="cookieConsent" class="alert alert-info alert-dismissible fade show"
role="alert">
Use this space to summarize your privacy and cookie use policy.
<a href="/privacy">Privacy Policy</a>
<button type="button" @onclick="AcceptPolicy" class="accept-policy close"
data-bs-dismiss="alert" aria-label="Close">
<span aria-hidden="true">Accept</span>
</button>
</div>
}
@code {
private IJSObjectReference? module;
private bool showBanner = false;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./Shared/CookieConsent.razor.js");
showBanner = !await module.InvokeAsync<bool>("getCookiePolicyAccepted");
StateHasChanged();
}
}
private async Task AcceptPolicy()
{
if (module is not null)
{
await module.InvokeVoidAsync("setCookiePolicyAccepted");
showBanner = false;
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
```

Add the following [collocated JavaScript file](xref:blazor/js-interop/javascript-location#load-a-script-from-an-external-javascript-file-js-collocated-with-a-component) to maintain the `setCookiePolicyAccepted` and `getCookiePolicyAccepted` functions in a JavaScript module.

`Shared/CookieConsent.razor.js`:

```javascript
export function getCookiePolicyAccepted() {
const cookiePolicy = localStorage.getItem('CookiePolicyAccepted');
return cookiePolicy === 'yes' ? true : false;
}

export function setCookiePolicyAccepted() {
localStorage.setItem('CookiePolicyAccepted', 'yes');
}
```

In the preceding example, you can change the name of the local storage item and value from "`CookiePolicyAccepted`" and "`yes`" to any preferred values. If you change one or both values, update both functions.

Within `<main>` Razor markup of the `MainLayout` component (`Layout/MainLayout.razor`), add the `CookieConsent` component:

```razor
<CookieConsent />
```

:::zone-end

## Additional resources

* [Microsoft Trust Center: Safeguard individual privacy with cloud services from Microsoft: GDPR](https://www.microsoft.com/trust-center/privacy/gdpr-overview)
* [European Commission: Data protection explained](https://ec.europa.eu/info/law/law-topic/data-protection/reform/what-does-general-data-protection-regulation-gdpr-govern_en)
5 changes: 4 additions & 1 deletion aspnetcore/security/gdpr.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ ASP.NET Core provides APIs and templates to help meet some of the [EU General Da
* The project templates include extension points and stubbed markup that you can replace with your privacy and cookie use policy.
* The `Pages/Privacy.cshtml` page or `Views/Home/Privacy.cshtml` view provides a page to detail your site's privacy policy.

For GDPR guidance that applies to Blazor apps, see <xref:blazor/security/gdpr>.

To enable the default cookie consent feature like that found in the ASP.NET Core 2.2 templates in a current ASP.NET Core template generated app, add the following highlighted code to `Program.cs`:

[!code-csharp[Main](~/security/gdpr/sample/RP6.0/WebGDPR/Program.cs?name=snippet_1&highlight=4-11,23)]
Expand Down Expand Up @@ -64,7 +66,8 @@ For databases that don't provide built-in encryption at rest, you may be able to

## Additional resources

* [Microsoft.com/GDPR](https://www.microsoft.com/trustcenter/Privacy/GDPR)
* [Microsoft Trust Center: Safeguard individual privacy with cloud services from Microsoft: GDPR](https://www.microsoft.com/trust-center/privacy/gdpr-overview)
* [European Commission: Data protection explained](https://ec.europa.eu/info/law/law-topic/data-protection/reform/what-does-general-data-protection-regulation-gdpr-govern_en)

:::moniker-end

Expand Down
2 changes: 2 additions & 0 deletions aspnetcore/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,8 @@ items:
uid: blazor/security/qrcodes-for-authenticator-apps
- name: Content Security Policy
uid: blazor/security/content-security-policy
- name: EU General Data Protection Regulation (GDPR) support
uid: blazor/security/gdpr
- name: Server-side and Blazor Web App additional scenarios
uid: blazor/security/additional-scenarios
- name: State management
Expand Down

0 comments on commit fa84c7c

Please sign in to comment.