hi all!
I’m trying to serve Bokeh application through Flask using externally signed sessions. The idea is to generate a signed session for each authenticated user (in Flask) and then pull it from Bokeh server working alongside. Bokeh server should not be accessible at all through any other means.
The basic example is really simple and is adapted from server_session example:
from flask import Flask, render_template
from bokeh.client import pull_session
from bokeh.embed import server_session
from bokeh.util.token import generate_session_id
app_url = "http://localhost:5100/bokeh_app/"
app = Flask(__name__)
@app.route('/')
def bkapp_page():
bokeh_session_id = generate_session_id(secret_key=<SECRET_KEY>, signed=True)
with pull_session(session_id=bokeh_session_id, url=app_url) as session:
script = server_session(session_id=session.id, url=app_url)
return render_template("embed.html", script=script, template="Flask")
if __name__ == '__main__':
app.run(port=8080)
for the Flask app and
import numpy as np
from bokeh.plotting import figure, curdoc
def add_circles():
sample_plot.circle(x=np.random.normal(size=(10,)),
y=np.random.normal(size=(10,)))
bokeh_doc = curdoc()
sample_plot = figure(plot_height=400, plot_width=400)
bokeh_doc.add_root(sample_plot)
bokeh_doc.add_periodic_callback(add_circles, 1000)
for the Bokeh app. However, when trying to serve all of this with
export BOKEH_SECRET_KEY=<SECRET_KEY>
bokeh serve --port 5100 --allow-websocket-origin localhost:8080 --allow-websocket-origin 127.0.0.1:8080 --session-ids external-signed bokeh_app.py
and
python3 flask_app.py
I get an invalid token exception:
2021-01-16 21:41:44,376 Token for session 'IC4cV2wEuIxgNiK4U0knlc58uoFqnE1K0MsfqS1TeLQi.nvqsJ781xapwIIlB0gSaA3ab9eQN_wBdYSPlLT0vxLk' had invalid signature
2021-01-16 21:25:46,179 Uncaught exception GET /bokeh_app/ws (::1)
HTTPServerRequest(protocol='http', host='localhost:5100', method='GET', uri='/bokeh_app/ws', version='HTTP/1.1', remote_ip='::1')
Traceback (most recent call last):
File "/usr/lib64/python3.8/site-packages/tornado/websocket.py", line 956, in _accept_connection
open_result = handler.open(*handler.open_args, **handler.open_kwargs)
File "/home/traveller/.local/lib/python3.8/site-packages/bokeh/server/views/ws.py", line 141, in open
raise ProtocolError("Invalid token signature")
bokeh.protocol.exceptions.ProtocolError: Invalid token signature
in the Bokeh server logs. Flask in turn reports that it cannot connect:
OSError: Cannot pull session document because we failed to connect to the server (to start the server, try the 'bokeh serve' command)
I was able to pin down the exact failing path in check_token_signature
by adding a print
:
if signed:
print(token)
token_pieces = token.split('.', 1)
if len(token_pieces) != 2:
return False
Despite the result of generate_session_id(secret_key=<SECRET_KEY>, signed=True)
looking correct (<session_id>.<session_signature>
), what we get from the print(token)
above is something like
eyJzZXNzaW9uX2lkIjogIkdMaU9EWFM1UE1RcWR3VTA5VHZ6WXVJZUhRR2dVV012Z01ld2VtYm9MeDZxIiwgInNlc3Npb25fZXhwaXJ5IjogMTYxMDgyMTg0Nn0
i.e., no periods at all and len(token_pieces) != 2
resolves to True
in check_token_signature
, leading to the invalid token error.The funny thing is that session in
2021-01-16 21:41:44,376 Token for session 'IC4cV2wEuIxgNiK4U0knlc58uoFqnE1K0MsfqS1TeLQi.nvqsJ781xapwIIlB0gSaA3ab9eQN_wBdYSPlLT0vxLk' had invalid signature
looks exactly like in Flask app as bokeh_session_id
. What I do not get, is why token
, which goes to check_token_signature
from WebSocket handler, does look like this.
I probably overlook some simple mistake and do not understand the inner mechanics, by it requires to get into Tornado, which I’m absolutely not familiar with.
What is the right way of doing this? What am I missing&
Thanks for any help!