calibre-web
calibre-web copied to clipboard
Allow reverse proxy authentication by header when accessing /opds
Is your feature request related to a problem? Please describe.
I have setup traefik proxy, with authelia for authentication sitting before calibre-web. So every time I open calibre-web website, traefik redirects it to authelia, authelia sets the Remote-User and other related proxy authentication headers, and then these headers are included in the request going to calibre-web. This setup works great when accessing main website pages. However, when accessing /opds
part through this setup, the authentication code for this endpoint does not match the authentication for the main website and so it doesn't read proxied authentication headers.
Describe the solution you'd like Would like for the authentication code for opds (https://github.com/janeczku/calibre-web/blob/master/cps/opds.py#L51) to also include reverse proxy header authentication, if it is enabled.
Additional context
Would it be feasible to reuse the same authentication function from usermanagement.py
(https://github.com/janeczku/calibre-web/blob/master/cps/usermanagement.py#L30) in the opds, so both parts have a similar authentication flow?
I've been thinking about this, but the question is, do the OPDS clients know how to use the authentication flows?
That's a good question. I've tested with only one client (but I'm suspecting others work like that too).
The client in question would show username/password login form, since the server sends the {'WWW-Authenticate': 'Basic realm="Login Required"'}
in headers, after that it seems to be sending the username/password to server and server approves it and proceeds. Presumably the client either saves some cookie for subsequent requests or sends the username/password pair again for all subsequent requests.
The workflow in the original post has authelia container sitting before calibre-web. Authelia is configured to send basic authentication via WWW-Authenticate
header, if the user is not logged in and handle that side of things in general. So the book client interacts with authelia to authenticate and after success, authelia container forwards login information in the Remote-User
header to calibre-web.
Now, here is where the problem is, calibre-web doesn't understand the Remote-User
header, so it sends back another WWW-Authenticate
header. and so the book client shows another sign in page. I've actually seen it happen - the client would show one signin form corresponding to authelia and after successful login, it shows another one corresponding to calibre-web, but after logging in into the second one, credentials for the first don't work out and it repeats the loop starting with authelia basic authentication form.
I'm pretty sure HTTP Basic Auth doesn't use cookies or anything like that, the browser (or any HTTP client) just sends the Authorization header with the username:password encoded in base64 with every request.
I've tried on moon reader+, and it just displays an error message if the reverse proxy shows a login page (i.e. cloudflare access).
I'm actually curious to what client you're using and how did you get them to show your authelia login page.
I'm personally running a modified version that handles my cloudflare access JWTs and verifies them, https://github.com/janeczku/calibre-web/pull/2468.
I'm with @ruifung All clients I know only understand basic authentication (and this sometimes buggy). Basic Auth also uses no cookies, it requests the credentials to be passed on every request, some clients sometimes accept cookies and forward them with each request (feels to me like they don't not know for what). As the last question from @ruifung wasn't answered for one month, I'm closing this as "won't fix"
Hello again. Sorry, I could not respond earlier.
I'm actually curious to what client you're using and how did you get them to show your authelia login page.
I did not mean that any of the clients would show authelia login page. It is not a problem that they only use basic authentication, since it is possible to configure authelia to handle basic authentication: https://www.authelia.com/integration/proxies/traefik/#basic-authentication. With this mode enabled, it will respond with status 401 and WWW-Authenticate
header, which will trigger book reader clients to use HTTP Basic Auth flow.
I put authelia in basic mode only before /opds
endpoint in my local setup and I added a pretty silly patch to requires_basic_auth_if_no_ano function:
diff --git a/cps/opds.py b/cps/opds.py
index 0c83fa70..9fa9a575 100644
--- a/cps/opds.py
+++ b/cps/opds.py
@@ -47,6 +47,9 @@ def requires_basic_auth_if_no_ano(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
+ user = load_user_from_request(request)
+ if user:
+ return f(*args, **kwargs)
if config.config_anonbrowse != 1:
if not auth or auth.type != 'basic' or not check_auth(auth.username, auth.password):
return authenticate()
With this, I am able to login and open opds catalog on both Moon+ Reader Pro
and KOReader
.
So, could you reconsider this feature and reopen the ticket?
Extra details for interest:
- Moon+ Reader Pro is weird with its basic authentication flow. During my debugging I noticed that it is always doing 2 requests for some endpoint - first it tries with some cookie and without
Authorization
header, which then fails with status 401, and then it tries the same thing second time but withAuthorization
header, which then succeeds. Where it is taking the cookie for the first request, I'm not sure (maybe authelia, maybe other things in the request stack, or maybe it got saved from when I was experimenting with various flows). - I noticed that Moon+ Reader never tries to send authentication headers when accessing cover images and when accessing weird
/opds/osd
endpoint. - KOReader works much nicer - it always includes Authentication header in the request. Because of that, it is possible to put authelia into some sort of "semi-basic" mode, where it is possible change
Authorization
header toProxy-Authorization
on the proxy and not append?auth=basic
to the verify endpoint.
I've worked around this by using ldap suppoprt in addition to proxy auth. Plus a service account for accessing cover images, injected via reverse proxy. Opds auth goes over ldap to authentik using app passwords.
@qlonik: I added a logic similiar to yours to the "requires_basic_auth_if_no_ano" decorator, it should now work as desired. Please check the newest nightly version
Came here from #2711.
I upgraded to my docker container to linuxserver/calibre-web:nightly and it's still not working for me. Although the behavior changed slightly. It used to give me two failed message in the log when I'd try once, but now it only shows one failed message in the log.
I'm using MapleRead CX to test. The 'admin' account works fine, however, any other user does not work. I'm using calibre-web behind Nginx (the LinuxServer SWAG container) as a subfolder, so the URL is like https://myserv.mydomain.com/calibe-web/opds/
, and using Organizr-auth API (which is using X-WEBAUTH-USER).
@OzzieIsaacs This change got propagated via linuxserver container, and I finally managed to test it.
It doesn't work from the get go, but it can be addressed: I had to hide Authorization
header on my proxy/ingress (nginx in my case). I set proxy_set_header Authorization "";
option and it started working. Maybe this tricky point can be addressed by some amount of documentation somewhere?
Looks like change introduced here https://github.com/janeczku/calibre-web/commit/ac13f6042aab11f923f8299914bbf83f0d3a8b38 favours Authorization
header as a first method of authentication, before trying to use proxy-based header authentication. However, it seems that there is no way for authelia to indicate to nginx to remove Authorization
header after authentication, resulting in both Authorization
and Remote-User
headers set. This results in authelia password being forwarded to calibre-web and understandably the authentication fails, since the calibre-web wouldn't have the same password as authelia (I mean it could if, for example, ldap is being used or password is simply set to the same value, but that seems more like a special case). Anyway, by instructing nginx to hide Authorization
header, only Remote-User
will be set, the basic authentication flow of calibre-web will be skipped and proxy-based authentication will be used.
@PriamX Given you are using nginx too, I would try hiding the header for this proxy connection too. Hopefully that will help you.