-
-
Notifications
You must be signed in to change notification settings - Fork 531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OAuth not working with prefixed app or behind a reverse proxy #3555
Comments
@TheoMathurin Hi Theo, I am also running nginx reverse proxy and having hard time with azure oauth. Keeps saying The redirect URI does not match. Any tips on what my redirect should be set to on azure and what i need to specify on the command line? My nginx listens on 443 and reroutes to upstream server 127.0.0.1:5100 |
Have you tried editing the auth.py module as suggested? The redirect URI given on the command line should be the address used to get to your app, e.g. https://your-server/your_app. Then the auth module should actually redirect to this URL, including the path. This is currently not the case, which is what the suggested changes address. |
@TheoMathurin hmm i tried what you suggested but that returns ERROR: 'module' object is not callable @Property |
Can you try urlparse.urlparse instead of urlparse? Or "from urlparse import urlparse" instead of "import urlparse". |
Still cant get this thing to work properly. No longer complains about redirect mismatch but app does not come up. Registered the redirect on azure. When i login into the server the authentication seems to be in a loop 3 or 4 times and never passes. Just tryong to hit my app server, authenticate with azure ad, then redirect back to app server on 443, then have nginx route traffic to an available port. Does the redirect need to be --oauth-redirect-uri http://host:5006/prefix/myapp... I would prefer if i could use https://myappserver.com:443 so my reverse proxy could load balance. |
The --oauth-redirect-uri should be https://myappserver.com + any path that is set in the nginx config for your app. If there's no path then your issue is likely not related to the one I raised here. What's the URL in your browser at the end of the process, after authentication? |
http { default_type application/octet-stream; access_log ./logs/access.log; location / { |
--oauth-redirect-uri=https://myappserver.com |
|
Ok I woud advise you to try to set up authentication first with a very simple app and the most basic nginx config possible (see Bokeh docs). |
azure would only need the web redirect url setup right? Json web tokens wont fly around here. |
For me as of 0.14.1 the fix does not seem to work. Behind a reverse proxy I get the following URL with a 404 error: http://host/myapp/login?next=%2Fmyapp. And with a prefix I get http://host:5006/prefix/myapp/login?next=%2Fprefix%2Fmyapp I should have tried earlier, I learned it the hard way today in a professional setting. Can someone confirm? If that is correct, can we reopen this or should I file a separate issue? |
@philippjfr, if you have an idea about what should be looked at in order to fix this, I'm willing to investigate. As reported in my previous comment, the path is correctly copied over but you are not redirected to the provider login page. If you're already logged in though, you can access the app directly as an authenticated user. |
Regarding the prefix case, I've found that you can make it work if the OAuth redirect URI only includes the prefix in the path and not the full path to the app (e.g. In fact in the Still trying to make the reverse proxy setup work. It should be noted that with Panel 0.12.4 + the fix I mentioned when I filed the issue, it does work when I try to acces the URL for which I now have a 404 error (http://host/myapp/login?next=%2Fmyapp). |
Thanks @TheoMathurin. If you want let's set up a joint debugging session to figure this out. |
Sure, I can reach out to you on gitter or via e-mail, whichever suits you best. |
Ok it does work behind a reverse proxy if you specify the URL path when adding the endpoints: @property
def endpoints(self) -> List[Tuple[str, Type[RequestHandler]]]:
''' URL patterns for login/logout endpoints.
'''
endpoints: List[Tuple[str, Type[RequestHandler]]] = []
if self.login_handler:
assert self.login_url is not None
# Change in line below
endpoints.append((urlparse.urlparse(config.oauth_redirect_uri).path + '/login', self.login_handler))
if self.logout_handler:
assert self.logout_url is not None
endpoints.append(('/logout', self.logout_handler))
return endpoints Edit: This fix will work as is if the URI path happens to be the name of your app (see comment below) |
In fact with this fix, it does not work anymore with a prefixed app... I will not consider a PR since it breaks something while fixing another. It looks like something a bit more elaborate is needed. Most notably it may be necessary to check whether a prefix has been set to add the adequate endpoint. As I'm mostly interested in apps behind a reverse proxy though, I will use this change for now. |
It's actually a bit tricky even without considering prefixes. First, the endpoint string needs to correspond to the name of your app. For instance if the web server proxies requests from Second, if you're not using the name of your app as the path, the |
I can confirm we are also observing this issue with Version: 1.3.8 nginx: - host: "yourdomain.com"
paths:
- path: /panel-apps/ For the env vars:
Then serve as:
NOTE: |
Update to my comment from last year, from what I have understood after some tests with the code as of 1.3.8 behind Apache acting as a reverse proxy. Setup: a Panel app called myapp running at 127.0.0.1:5006/myapp (with The first problem raised is now different: the login endpoint is now hardcoded to "/login" so you must have a dedicated rule that redirects 127.0.0.1/login to 127.0.0.1:5006/login. AFAIK, this is not expected behaviour. The URL path as entered by the client in the browser should be taken into account (maybe inferred from the redirect URI) so that the generic rule also works for the login endpoint. The second point is still an issue. After authentication you are still redirected to http://127.0.0.1/myapp instead of http://127.0.0.1/app. While the host is correctly extracted from the redirect URI, the path is ignored and somehow replaced by that of the Tornado URL (returned by the But even then there's now a third issue. I still can't get my application to work. The browser indicates a failure to establish a websocket connection ( |
Many thanks for reporting back here.
Not fully following this point. Note that the endpoint is configurable with
This can also be overridden by setting redirect_uri = "{0}://{1}".format(
self.request.protocol,
self.request.host
) and the path should be taken from the original URL you visited (although the logic here is convoluted and I'd have to look at this again.
Without seeing the precise configuration here I can't help. What I do know is that we routinely run authenticated Panel apps behind reverse proxies (primarily nginx) without issue. |
Many thanks @philippjfr for your reply.
I did not know about --login-endpoint, apparently it's not documented. I'm having a hard time trying to use it though. The default is just /login, and setting it to /app/login or /myapp/login does no good. Again to me it looks like there is a confusion between URL paths handled by Tornado and those seen by the client. If Tornado redirects the browser to some URL, it will then in turn be processed by the reverse proxy. So for instance if you set --login-endpoint to /myapp/login, Tornado will redirect the browser to /myapp/login which leads Apache to return 404 (the app is accessible at /app). If you set it to /app/login, the path of the request received by Tornado after being proxied is /myapp/login which also yields a 404 error (this time returned by Tornado).
I do provide a redirect URI through the PANEL_OAUTH_REDIRECT_URI environment variable and I'm saying that the path is not taken from this variable or the URL you visited (as seen in the client browser), but from the local URL (Tornado application). It will work only if the two paths are the same (see my comment last year).
I'm using the rules indicated in the Bokeh docs on reverse proxying with Apache, but I agree that knowledge of the full configuration is necessary to debug this. |
Anyway the first two issues are manageable. It only involves adding a rule for the login endpoint in Apache and modifying I've looked into the third (new) issue. In the Apache logs I got the following:
Note the unexpectedly long string sent with the Looking at bokehjs, I think the relevant code that opens the websocket connection is here. We can see that the long string would correspond to |
That's right. Bokeh includes the token in the Websocket header on purpose. It's an inelegant solution but allows the original request and the Websocket connection to be handled by different replicas of the server. In nginx this sometimes requires setting
which should be documented in bokeh but we probably have to do a better job of making that visible. I'd hope there's an equivalent config option for Apache. |
Yes there is! It's the Indeed this should definitely be mentioned in the Bokeh docs. I may submit a PR to that end. Thanks for the tip @philippjfr |
Tbh I think we probably also want a how-to set up a reverse proxy guide in Panel. |
Ok. I've contributed to the Apache section in the Bokeh docs so I'm willing to help if needed. |
If you just make a PR with a few bullet points I'd be happy to flesh it out. |
Sure. However, in what way would this guide add information relative to the Bokeh guide? Are there elements that are specific to Panel in this regard? |
Tested with Panel 0.13.0 and some older versions, Bokeh 2.4.2.
I've been using the OAuth authentication feature provided by Panel with a custom provider for some time. It's working very well and I appreciate how easy it is to set up.
Now, I'm currently working on a new production environment where the app is behind a reverse proxy (see recent topic on discourse). The server is launched with something like:
With a random main.py in myapp/:
While trying to make it work, I realized that the auth.py module has some limitations regarding the management of URLs and redirection. Namely, the process fails if the app is available at a specific path. This is typically the case in a reverse proxy setup, but also if you use a prefix.
First, if you look at the latest version of the file, line 752 there is a return statement that builds the wrong URL. Trying to access http://host/myapp, the URL will change to http://host/login?next=%2Fmyapp, i.e. the path is ignored. This yields a 404 Error.
I've found that this can be fixed with the following change to the
login_url
method:This will get the user to the provider page. Then, the path is also ignored in the redirection (
self.redirect
method call line 269) so that after the user has been authorized, Tornado redirects to http://host/ instead of http://host/myapp. Passingurlparse(redirect_uri).path
instead of"/"
solves the issue.Note that the same issue arises without reverse proxying if you use a prefix, although the fix I put in place does not seem to work for some reason. In other words, you won't be able to set up authentication in this configuration:
The text was updated successfully, but these errors were encountered: