Help with setting up authentication

Hi, I’m trying to secure access to my bokeh server. To this end, I found two options in the documentation: use signed session IDs, or provide a get_user function in an auth module. I tried both approaches, and can get neither to work:

With signed session IDs, it’s no problem to set up bokeh to only accept those, but when using server_document, there is no way to provide session IDs. Is there something I am missing in terms of how I should use bokeh here?

I also want to implement an auth hook, but the documentation says my get_user function should return None in case the user is not authenticated – when I do that, the following exception occurs:

2020-08-22 15:24:09,948 Starting Bokeh server with process id: 139215
<Response [406]>
no user found!
2020-08-22 15:24:21,810 Uncaught exception GET /sensordata/autoload.js?bokeh-autoload-element=1002&bokeh-app-path=/sensordata&bokeh-absolute-url= (

HTTPServerRequest(protocol=‘http’, host=‘’, method=‘GET’, uri=’/sensordata/autoload.js?bokeh-autoload-element=1002&bokeh-app-path=/sensordata&bokeh-absolute-url=’, version=‘HTTP/1.1’, remote_ip=‘’)

Traceback (most recent call last):
File “/home/…/venv/lib/python3.8/site-packages/tornado/”, line 1703, in _execute
result = await result
File “/home/…/venv/lib/python3.8/site-packages/bokeh/server/views/”, line 60, in get
session = await self.get_session()
TypeError: object NoneType can’t be used in ‘await’ expression

It seems to me like the documentation is wrong here, and something other than None should be returned, but I’m not sure what, since I guess anything else would just create a new “default user”.

For reference: this is the code I’m trying to use as an auth provider:

import os

import requests
from dotenv import load_dotenv
from tornado.web import RequestHandler

from bokeh_server.sensordata.backend import Backend

def get_user(request_handler: RequestHandler):
    user_id = request_handler.get_query_argument("userId")
    token = request_handler.get_query_argument("token")

    response = requests.get(

    if response.status_code == 200:
        return user_id

    print("no user found!")

    return None

def get_login_url(_: RequestHandler):
    return ""

You want server_session, which accepts a session_id parameter, not server_document.

As for the auth handler, the function should definitely return None, as can be seen in the example in the repo. Does that example work for you? If not, perhaps there is some unknown package incompatibility (please provide relevant package info). If the example does work, then there is something specific about your actual full code that is causing (or exposing) a problem, but it will be difficult to diagnose without a minimal reproducer.

Hey Bryan, thanks for your prompt response, I’m sorry I only saw it today.

Regarding the second issue, it seems that my script to start bokeh standalone to be able to debug it wasn’t complete, since now with using bokeh serve it works. Thanks!

For the first issue I tried to replace server_document with server_session, but then I get CORS errors because the OPTIONS method is not allowed, killing the preflight checks done to verify CORS. I’ve now worked around it by setting a “bokeh-session-id” header manually and using server_document as before. Of course I did set --allow-websocket-origin=*:

script = server_document(
        arguments={k: v for (k, v) in arguments.items()},
        headers={"bokeh-session-id": generate_session_id()},

I’m not really sure I follow, is there some change needed on the Bokeh side?

TLDR: I think I kinda fixed my problems. Except maybe for the OPTIONS request thing, where I’m not sure if bokeh should/does support those.

Sorry for my long absence - the question would be does Bokeh reply to OPTIONS requests? As far as I know, under certain preconditions browsers will send an OPTIONS request to determine if CORS is allowed. It seems sometimes my browser does this, but maybe they only show up if they fail due to misconfiguration on my part? Bokeh definitely sometimes works. I’m sorry I can’t provide a better example, this is mostly because I’m also deploying in a complicated environment and don’t really know it’s exact specifications.

I solved my first problem by just hardcoding the bokeh server session id into server_document, since I didn’t know how to set up a session to retrieve with server_session

script = server_document(
    # only consider arguments that are not None
    arguments={k: v for (k, v) in arguments.items() if v is not None},
    headers={"bokeh-session-id": generate_session_id()},

I’m not sure anymore how I solved the second problem, but it seems returning None in get_user doesn’t crash anything and probably it was something with packages, as the examples did work.

@enra64 Currently the Bokeh server does not repond to OPTIONS requests. I had once made an issue to propose add support for a pre-flight OPTIONS request:

But there was never any interest or feedback so I closed it with no action. However, if there’s something tangible do discuss, we can certainly re-open.