Best practise for cookie or GDPR permissions

Hi guys,

Most websites now have a popup when you first visit them, explaining what data is processed/stored, and asking permission for the site to use cookies. I believe it is a legal requirement in the EU.

I wondered if anyone could share their methods of doing this, or comment on what is the easiest or best way to do this with Dash?

Thank you!

I think you could use a dbc.Modal and style it your way.

Hello @Pippo,

Welcome to the community!

I think in general, dash apps do not use cookies. Local and Session storage is used for things that are within the regular libraries.

Where you start to get into cookies is with people logging in, etc. Typically, the cookie is how you determine if they are logged in, especially with flask-login.

I have this in my flask app:

def cookies_check():
    value = request.cookies.get('cookie_consent')
    return value == 'true'

@app.context_processor
def inject_template_scope():
    injections = dict()
    injections.update(cookies_check=cookies_check)
    return injections

And then have this in the applicable template:

{% if cookies_check() %}
        {# then user has already consented so no requirement for consent banner #}

{% else %}
        {# show a cookie consent banner #}
        <br>
        <div id="cookie-consent-container" style="background: white; font-family: Comfortaa; position: absolute;">
    <strong>We value your privacy</strong><br>This site uses cookies and other technologies to improve your browsing experience,
    verify your session, and perform analytics. <br>Clicking “I Consent” indicates that you agree to the use of these technologies on your device.<br>
            <button id="cookie-consent" type="button">I Consent</button>
        </div>
        <script>
            $("#loginbutton").prop("disabled",true)

            var fn = function () {
                document.cookie = "cookie_consent=true";
                document.getElementById('cookie-consent-container').hidden = true;
                $("#loginbutton").prop("disabled",false)
            };
            document.getElementById('cookie-consent').onclick = fn;
        </script>
{% endif %}

Now, as far as Terms & Conditions and EULA, we store those being accepted per user once cookies have been accepted and login verified, but we have links plainly visible on our standard site.

image

Now, of course, how you display this consent button and disclaimer is completely up to you. You could even use a callback that then adds the cookie to document or response from the server.

4 Likes

Hi, unfortunately I haven’t had time to look at this yet as something more pressing has come up, but I just wanted to thank you for sharing your code and screenshot, that is very kind :slight_smile: I will post back here if I have queries when I get chance to look at it, thank you again!

1 Like

Is it possible to use this on a plotly dash app? Isn’t jinja2 not compatible with plotly?

Hello @MarcoAurelio,

Dash is built on top of Flask. So, this template is available to be populated.

I’m sure there is a way to do this in just Dash, but I don’t have an example of it currently.

1 Like

Hey, I manage to do it. It was quite simple, I just use the .route and render template:

2 Likes

@MarcoAurelio ,

Yes, that is how easy it is to integrate a flask template into a dash app. :slight_smile:

1 Like

Hey @jinnyzor, I ended up using client-side callbacks to insert the GDPR permission. I did not figure out what to do next with your suggestion. What I did was use client-side callbacks to edit the document. I don’t know if it is secure or something (I followed a tip on the documentation to add scripts through client-side callbacks).

Here is a simple code example:

import dash
from dash import dcc, html, Input, Output, State

app = dash.Dash(__name__)

# Layout of the app
app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='page-content'),
    dcc.Store(id='cookie-consent', storage_type='session'),
    html.Div(
        id='cookie-banner',
        children=[
            html.P("This website uses cookies to ensure you get the best experience."),
            html.Button("Accept", id='accept-cookies', n_clicks=0)
        ],
        style={'display': 'none', 'position': 'fixed', 'bottom': '10px', 'width': '100%', 'background': '#ccc', 'padding': '10px', 'textAlign': 'center'}
    )
])

app.clientside_callback(
    """
    function(href, n_clicks) {
        let consent = document.cookie.split('; ').find(row => row.startsWith('cookie-consent='));
        if (consent === undefined && n_clicks > 0) {
            const d = new Date();
            d.setTime(d.getTime() + (365*24*60*60*1000)); // 1 year
            const expires = "expires=" + d.toUTCString();
            document.cookie = "cookie-consent=true;" + expires + ";path=/";
            consent = "cookie-consent=true";
        }
        if (consent === undefined) {
            document.getElementById('cookie-banner').style.display = 'block';
        } else {
            document.getElementById('cookie-banner').style.display = 'none';
        }
        return consent !== undefined;
    }
    """,
    Output('cookie-consent', 'data'),
    Input('url', 'href'),
    Input('accept-cookies', 'n_clicks')
)

# Conditionally add gtag script
app.clientside_callback(
    """
    function(consent) {
        if (consent) {
            const scriptId = 'gtag-script';
            if (!document.getElementById(scriptId)) {
                const script = document.createElement('script');
                script.id = scriptId;
                script.src = 'https://www.googletagmanager.com/gtag/js?id=[GA_id]';
                document.head.appendChild(script);

                window.dataLayer = window.dataLayer || [];
                function gtag() { dataLayer.push(arguments); }
                gtag('js', new Date());
                gtag('config', [GA_id]);
            }
        }
        return '';
    }
    """,
    Output('page-content', 'children'),
    Input('cookie-consent', 'data')
)

if __name__ == '__main__':
    app.run_server(debug=True)

Hello @MarcoAurelio,

Sure you can do that. There really wasn’t a need to do anything else as the consent needs to be done each time upon each session. So the cookie will be there. :grin: