- Go 99.5%
- Dockerfile 0.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Seerr's session store (connect-typeorm) emits disconnect on any database error and express-session never re-enables it, so every sign-in after that is a 200 without connect.sid until Seerr restarts. The endpoint is for a liveness probe on Seerr: it signs in again only while the latest sign-in got no session, so a healthy Seerr sees no extra sign-ins and no sessions pile up; other failures answer ok, since a restart mends none of them. Proxy errors are answered 502 in plain text: the default handler's empty 502 made browsers save an empty file. |
||
| .forgejo/workflows | ||
| api | ||
| internal | ||
| .dockerignore | ||
| .gitignore | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| main.go | ||
| main_test.go | ||
| README.md | ||
seerr-oidc
A reverse proxy that serves Seerr's own pages to signed-in people without a sign-in of
Seerr's. It keeps a Seerr session for each email and puts it on their requests; the
browser never holds one. It signs in as the local Seerr user of that email with the
password HMAC-SHA256(GATE_KEY, email), and sets that password through the API, creating
the user if Seerr has none, when the sign-in fails. Seerr wants the current password to
change the API key owner's own, so a new GATE_KEY needs that user's password reset by
hand.
A request whose path starts with /api/ and that carries a non-empty X-Api-Key header
goes to Seerr as it came: no sign-in, no session, the key left for Seerr to check.
Apps that sign in to Seerr themselves, such as TV apps, are passed through in both
modes, their requests and Seerr's answers untouched, connect.sid included:
- a
POSTto/api/v1/auth/localor/api/v1/auth/jellyfin, always; - a
POSTto/api/v1/auth/logout, and any request under/api/with aconnect.sidcookie, when it comes without a session of seerr-oidc's (OIDC mode) or without theGATE_USER_HEADERheader (header mode).
A request that has both the session or the header and a connect.sid cookie is served
on seerr-oidc's own Seerr session, as any other. Outside /api/, connect.sid lets
nothing through.
A request that Seerr cannot serve, or for whose person no Seerr session can be had, is
answered 502 in plain text.
Seerr's session store disconnects on a database error and stays disconnected until Seerr
restarts; Seerr then accepts sign-ins without a session. /_gate/seerr reports that state
for a liveness probe: once a sign-in gets no session, each call signs in again as that
person, and answers 503 until a session comes back. Before that, and on any other failure,
it answers ok without asking Seerr anything.
Modes
Header mode, when OIDC_ISSUER_URL is unset: an authenticating proxy in front (such
as oauth2-proxy) signs people in, and the email is read from the GATE_USER_HEADER
request header. Any other request without the header is answered 401.
OIDC mode, when OIDC_ISSUER_URL is set: seerr-oidc signs people in itself with the
OpenID Connect authorization-code flow (PKCE, state and nonce), scopes
openid email profile groups. The GATE_USER_HEADER header from the client is removed
and never trusted. After sign-in the ID token's email and groups go into the
seerr_oidc cookie, encrypted and authenticated with AES-256-GCM under SESSION_KEY
(Secure, HttpOnly, SameSite=Lax, path /, lasting SESSION_TTL); the cookie is not
passed to Seerr. A token with email_verified false, or whose groups include none of
OIDC_ALLOWED_GROUPS, is answered 403, as is a session whose groups no longer do.
Without a valid session, and not passed through as above, a page request (a GET or HEAD outside /api/, not marked
X-Requested-With: XMLHttpRequest, with Sec-Fetch-Mode absent or navigate) is
redirected to the provider and comes back to the same path; any other request is
answered 401. A return path that is not a path on this site becomes /. A callback whose
state does not match a sign-in this browser started is answered 400.
| Path | Answers |
|---|---|
/_gate/healthz |
ok, without a session or the header, in both modes |
/_gate/seerr |
503 while Seerr accepts sign-ins without a session, ok otherwise; for a liveness probe on Seerr |
path of OIDC_REDIRECT_URL |
the provider's callback (OIDC mode) |
/oauth2/sign_out |
clears the session cookie (OIDC mode) |
Configuration
| Variable | Default | Meaning |
|---|---|---|
SEERR_URL |
Seerr, without /api/v1 |
|
SEERR_API_KEY |
Seerr API key with MANAGE_USERS |
|
GATE_KEY |
key the passwords derive from | |
GATE_ADDR |
:8081 |
listen address |
GATE_USER_HEADER |
X-Forwarded-Email |
header carrying the user's email in header mode, removed in OIDC mode |
GATE_PERMISSIONS |
160 |
Seerr permission bits for a user the proxy creates |
OIDC_ISSUER_URL |
OpenID provider; setting it turns on OIDC mode | |
OIDC_CLIENT_ID |
client ID, required in OIDC mode | |
OIDC_CLIENT_SECRET |
client secret, required in OIDC mode | |
OIDC_REDIRECT_URL |
full callback URL registered with the provider, required in OIDC mode; without a path, its path is /oauth2/callback |
|
OIDC_ALLOWED_GROUPS |
comma-separated groups let in, required in OIDC mode | |
SESSION_KEY |
32 bytes in standard base64, required in OIDC mode: openssl rand -base64 32 |
|
SESSION_TTL |
168h |
session lifetime, a Go duration |
With OIDC_ISSUER_URL set, startup fails naming any other required OIDC variable that is
unset, and when the provider's discovery document cannot be fetched.
Running
go build .
SEERR_URL=http://127.0.0.1:5055 SEERR_API_KEY=... GATE_KEY=... ./seerr-oidc
The image, sarak.sh/arr/seerr-oidc, runs /seerr-oidc as its entrypoint and exposes
port 8081.