Xsrf cookie does not match post argument

for dash app, /hub/user-redirect/proxy/5151/_dash-update-component , i am always seeing “xsrf cookie does not match post argument” , i tried to add it like below, but still did not help

app.renderer = '''
var renderer = new DashRenderer({
    request_pre: (payload) => {
        const csrfToken = document.cookie.split('; ').find(row => row.startsWith('_xsrf='));
        if (csrfToken) {
            const tokenValue = csrfToken.split('=')[1];
            if (!payload.headers) {
                payload.headers = {};
            }
            payload.headers['X-XSRF-Token'] = tokenValue;  
            payload.headers['Authorization'] = 'token  <API_TOKEN; // 
        } else {
            console.warn('CSRF Token not found in cookie.');
        }
    },
    request_post: (payload, response) => {
        console.log('Request Payload:', payload); // Debugging
        console.log('Response:', response); // Debugging
    }
});
'''

i can see X-XSRF-Token and Authorization header are added in the payload
did not help, Any suggestion how to fix this ?

What’s generating the error- is it JupyterHub, JupyterLab/notebook/server, or your dash app/extension? Can you show us your logs, and ideally give us enough information to reproduce your problem?

1 Like

@manics Thanks for helping here.
It’s JupyterHub. hub is rejecting this post request . Only relevent log i see in hub pod is [Xsrf cookie does not match post argument]. In Inspecting UI i see 403

What else should i provide that can help further debugging ?

As mentioned earlier, providing enough information for someone to reproduce the bug would be helpful, e.g. instructions on how to setup and configure JupyterHub on a new virtual machine.

Can you also turn on debug logging, and show us your full logs, not just the error message?

I guess you are executing this JS in a JupyterLab extension (presumably dash). If so, I would suggest to use ServerConnection from @jupyterlab/services. This package will include all necessary Auth and XSRF tokens in the requests. An example can be found here.

i am running this dash app as python script , from singleuser pod. Single user pod image also have jupyter-server-proxy installed. Below script could suggest what i am doing

import dash
from dash import dcc, html
from dash.dependencies import Input, Output

app = dash.Dash(__name__, requests_pathname_prefix='/hub/user-redirect/proxy/5151/')

# Define the layout of the app
app.layout = html.Div([
    html.H1("Hello, World!")
])

# Run the app
if __name__ == '__main__':
    app.run_server(host='0.0.0.0', port=5151, debug=False, jupyter_mode="external")

from IPython.display import IFrame

url = "https://<url>/hub/user-redirect/proxy/5151"
IFrame(url, width=800, height=600)

Now this hello world works fine , but as soon as i add callback (which make post request , it throws 403 . Then i tried to add this header and token using my snippet posted previously but it did not solve (even though i see header are added in the payload correctly)

@manics below is precise “hello world” example that reproducing my error , when i run in notebook (single user pod ) . I will also enable debug log and put here shortly

import dash
from dash import dcc, html
from dash.dependencies import Input, Output

# Create the Dash app
app = dash.Dash(__name__, requests_pathname_prefix='/hub/user-redirect/proxy/5151/')

app.renderer = '''
var renderer = new DashRenderer({
    request_pre: (payload) => {
        const csrfToken = document.cookie.split('; ').find(row => row.startsWith('_xsrf='));
        if (csrfToken) {
            const tokenValue = csrfToken.split('=')[1];
            if (!payload.headers) {
                payload.headers = {};
            }
            payload.headers['X-XSRF-Token'] = tokenValue;  
            payload.headers['Authorization'] = 'token  <api token>'; // 
        } else {
            console.warn('CSRF Token not found in cookie.');
        }
    },
    request_post: (payload, response) => {
        console.log('Request Payload:', payload); // Debugging
        console.log('Response:', response); // Debugging
    }
});
'''

# Define the layout of the app
app.layout = html.Div([
    html.H1("Hello, World!"),

    # Add a text input field
    dcc.Input(id='input-text', type='text', placeholder='Enter some text'),

    # Add an HTML element to display the output
    html.Div(id='output-text', style={'marginTop': 20})
])

# Define the callback to update the output text
@app.callback(
    Output('output-text', 'children'),
    Input('input-text', 'value')
)
def update_output_text(input_value):
    # Update the displayed text with the user input
    return f'You entered: {input_value}' if input_value else 'Please enter something!'

# Run the app
if __name__ == '__main__':
    app.run_server(host='0.0.0.0', port=5151, debug=False, jupyter_mode="external")

from IPython.display import IFrame

# Display the app in an IFrame
url = "<home url>/hub/user-redirect/proxy/5151"
IFrame(url, width=800, height=600)
1 Like

Maybe it is an issue with the header name of XSRF? According to @jupyterlab/services package, we should use X-XSRFToken. Maybe try your luck with it?

@mahendrapaipuri thanks for supporting, i tried that as well , did not help

Your script is working fine. I could test it.

There are few things that you need to modify in your script mostly the path prefix of where dash server is running. Normally you can access the third party apps proxised through jupyter-server-proxy at https://jupyterhub.example.com/user/<username>/<server_name>/proxy/<port>/ if you are using named servers or https://jupyterhub.example.com/user/<username>/proxy/<port>/ if you are using default server.

Here is the modified Python script that launches dash server

import dash
from dash import dcc, html
from dash.dependencies import Input, Output

# Create the Dash app
app = dash.Dash(__name__, requests_pathname_prefix='/user/<username>/<server_name>/proxy/5151/')

app.renderer = '''
var renderer = new DashRenderer({
    request_pre: (payload) => {
        const csrfToken = document.cookie.split('; ').find(row => row.startsWith('_xsrf='));
        if (csrfToken) {
            const tokenValue = csrfToken.split('=')[1];
            if (!payload.headers) {
                payload.headers = {};
            }
            payload.headers['X-XSRF-Token'] = tokenValue;  
            payload.headers['Authorization'] = 'token  <token value>'; // 
        } else {
            console.warn('CSRF Token not found in cookie.');
        }
    },
    request_post: (payload, response) => {
        console.log('Request Payload:', payload); // Debugging
        console.log('Response:', response); // Debugging
    }
});
'''

# Define the layout of the app
app.layout = html.Div([
    html.H1("Hello, World!"),

    # Add a text input field
    dcc.Input(id='input-text', type='text', placeholder='Enter some text'),

    # Add an HTML element to display the output
    html.Div(id='output-text', style={'marginTop': 20})
])

# Define the callback to update the output text
@app.callback(
    Output('output-text', 'children'),
    Input('input-text', 'value')
)
def update_output_text(input_value):
    # Update the displayed text with the user input
    return f'You entered: {input_value}' if input_value else 'Please enter something!'

# Run the app
if __name__ == '__main__':
    app.run_server(host='0.0.0.0', port=5151, debug=False, jupyter_mode="external")

The only thing I have changed is requests_pathname_prefix when creating dash app.

1 Like

ah perfect, thanks so much . jupyter-server-proxy doc says /hub/user-redirect should do the same , but looks like it has some issue (in auth segment). Seems like with /user//<server_name>/proxy/5151/ , even i don’t need to inject XRF toke or api token, its working without it

1 Like

It is because the _xsrf tokens are linked to the paths since JupyterHub 4 (I guess). I assume in your first solution, you were making auth request with token from /user path to /hub path and hence, token does not match error.

Could be , that also make sense